WinForm и циклы - PullRequest
       48

WinForm и циклы

4 голосов
/ 26 мая 2010

У меня настроена WinForm и процесс, который повторяется до тех пор, пока на форме не будет нажата кнопка.

Когда я пытаюсь запустить свой код, форма даже не отображается. Я подозреваю, что это потому, что код застрял в цикле и не достаточно далеко, чтобы отобразить WinForm. Как получить форму для отображения и цикл после этой точки?

Ответы [ 7 ]

4 голосов
/ 26 мая 2010

Если вы зацикливаетесь, потому что вам нужно периодически что-то делать с графическим интерфейсом в ожидании ввода, я предлагаю использовать Timer элемент управления и его Tick событие .

Если вы хотите делать не-GUI во время ожидания, более традиционный таймер лучше подходит для этой задачи,

2 голосов
/ 26 мая 2010

Вы, вероятно, должны запустить цикл в фоновом потоке. Класс BackgroundWorker делает это довольно простым.

1 голос
/ 26 мая 2010

Не делайте этого.

Windows Forms (как и большинство современных инструментов разработки пользовательского интерфейса) - это управляемая событиями платформа.Вы никогда не должны использовать цикл, который «ждет», чтобы что-то произошло;вместо этого вы хотите использовать событие , которое инициирует что-то, что происходит.

По сути, это происходит следующим образом: WinForms имеет цикл, запускающий насос сообщений, который прослушивает события из Windows и запускает события C # вответ на них.Ваш код выполняется в том же потоке, что и насос сообщений (это необходимо, поскольку в WinForms только одному потоку разрешено касаться любого данного элемента управления).Таким образом, если вы поместите этот поток в цикл, код WinForms, который должен перекачивать сообщения, не будет, и ваш пользовательский интерфейс будет зависать, так как он не отвечает ни на какие сообщения из Windows.(Если вы продолжите щелкать по нему, вы заполните очередь сообщений и получите диалоговое окно с надписью «Это приложение перестало отвечать на запросы, вы хотите завершить его?» Или что-то в этом роде.)

Правильное решениеэто сделать одно из следующих действий:

Другое решение, которое будет работать, но не очень хорошая идея:

  • Использование Application.DoEvents () - но пожалуйста на самом деле не делайте этого
1 голос
/ 26 мая 2010

Загрузка формы замораживается, поскольку пользовательский интерфейс формы Windows выполняется в одном потоке. И логика, которую вы помещаете в событие Load этой формы, выполняется в этом потоке.

Вы можете легко запустить цикл в отдельном потоке, используя компонент BackgroundWorker в форме Windows. На событие DoWork вашего фонового работника вы помещаете код с циклом, который должен выполняться без блокировки вашего пользовательского интерфейса. В событии Form.Load вы можете запустить компонент фонового рабочего процесса, вызвав метод RunWorkerAsync . В обработчике событий вашей кнопки вы помещаете код для остановки фонового работника, вызывая метод CancelAsync .

Статья Как: реализовать форму, использующую фоновую операцию показывает, как именно ее выполнить.


Ваш комментарий о невозможности обновления текста TextBox из компонента фонового рабочего процесса. Это происходит потому, что не разрешено изменять состояние элемента управления Windows Form из другого потока (ваш фоновый рабочий код выполняется в отдельном потоке) Документация MSDN гласит:

Доступ к элементам управления Windows Forms не является поточно-ориентированным. Если у вас есть два или более потоков, управляющих состоянием элемента управления, можно перевести элемент управления в несогласованное состояние. Возможны и другие связанные с потоками ошибки, такие как состояние гонки и взаимоблокировки. Важно убедиться, что доступ к вашим элементам управления выполняется потокобезопасным способом.

Пример того, как вы можете обновить состояние элементов управления Windows-форм из фонового потока, будет аналогичен приведенному ниже (при условии, что новое значение уже сохранено в строковой переменной с именем text):

// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.textBox1.InvokeRequired)
{   
  SetTextCallback d = new SetTextCallback(SetText);
  this.Invoke(d, new object[] { text });
}
else
{
  this.textBox1.Text = text;
}

Я позаимствовал этот код, оторванный от Как: сделать потокобезопасные вызовы для статьи Windows Forms Controls . Он может предоставить вам больше информации о том, как работать с многопоточными окнами.

0 голосов
/ 26 мая 2010

Вы должны запустить цикл в фоновом потоке, используя компонент BackgroundWorker.

Помните, что фоновый поток не может напрямую взаимодействовать с элементами управления пользовательского интерфейса.
Чтобы сообщить о прогрессе в пользовательском интерфейсе, необходимо вызвать метод BackgroundWorker's ReportProgress в фоновом потоке и обработать событие ProgressChanged для обновления пользовательского интерфейса.

Вы можете вызывать метод CancelAsync при нажатии кнопки и выполнять цикл до тех пор, пока свойство CancellationPending не станет true.

0 голосов
/ 26 мая 2010

Это происходит потому, что ваш цикл не позволяет оконной функции обрабатывать сообщения, например, те, которые говорят ей перерисовывать себя. Сделайте вызов Application.DoEvents () внутри вашего цикла, чтобы позволить интерфейсу продолжать функционировать.

Тем не менее, вы должны спросить себя, почему вы делаете это в первую очередь. Если, скажем, вы копируете кучу файлов, это может иметь смысл. Тем не менее, для большинства задач ответ на тик по таймеру должен сработать и не заблокирует пользовательский интерфейс.

0 голосов
/ 26 мая 2010

Вы можете использовать событие загрузки формы для запуска начала цикла.

Так что он обработает событие Me.Load Однако необходимо ли, чтобы ваш цикл происходил внутри пользовательского интерфейса?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...