2008-09-19 3 views
9

У меня есть приложение WinForms, написанное на C# с .NET 3.5. Он выполняет длительный пакетный процесс. Я хочу, чтобы приложение обновляло статус того, что делает пакетный процесс. Каков наилучший способ обновления пользовательского интерфейса?Как я могу получить доступ к потоку пользовательского интерфейса для обновления пользовательского интерфейса при выполнении пакетной обработки в приложении WinForm?

ответ

14

BackgroundWorker звучит как объект, который вы хотите.

0

Приложение.DoEvents() или возможно запустить партию в отдельной теме?

+0

Я бы использовал BackgroundWorker и выполнял пакетный запуск в отдельном потоке. Мне не нравится Application.DoEvents(), потому что, даже если вы это делаете, в то время как вы этого не делаете, и вы работаете с обработкой, ваш пользовательский интерфейс полностью не отвечает на запросы и может появиться «подвешен» вашему пользователю. – 2008-09-20 04:34:33

+2

Не делайте этого - это настоящий запах кода. Я никогда не видел «Application.DoEvents()» в производственном коде, где я думал: «Это было самое простое и правильное решение». – 2010-01-11 17:58:38

+0

Если вы используете CLR Profiler 2.0, вы заметите, что использование DoEvents создаст много ручек, которые будут придерживаться! Я бы предложил использовать BackgroundWorker, как сказал jolson. – joek1975 2010-01-21 15:46:54

1

Используйте компонент backgroundworker для запуска пакетной обработки в отдельном потоке, это не повлияет на поток пользовательского интерфейса.

3

Запустите длительный процесс на фоне потока. Класс рабочего рабочего - это простой способ сделать это - он обеспечивает простую поддержку для отправки обновлений прогресса и событий завершения, для которых обработчики событий вызываются в правильном потоке для вас. Это делает код чистым и кратким.

Для отображения обновлений, индикаторы выполнения или текст строки состояния являются двумя наиболее распространенными подходами.

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

4

Быстрый и грязный способ использует Application.DoEvents() Но это может вызывают проблемы с событиями заказа. Так что не рекомендуется

Проблема, вероятно, заключается не в том, что вы должны уступить нити ui, но что вы делаете обработку в потоке ui, блокируя ее от обработки сообщений. Вы можете использовать компонент backgroundworker для выполнения пакетной обработки в другом потоке без блокировки потока пользовательского интерфейса.

0

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

1

Я хочу повторить то, что отмечали мои предыдущие комментаторы: пожалуйста, избегайте DoEvents(), когда это возможно, поскольку это почти всегда форма «взлома» и вызывает кошмары для обслуживания.

Если вы идете по дороге BackgroundWorker (что я предлагаю), вам придется иметь дело с перекрестными вызовами пользовательского интерфейса, если вы хотите вызвать какие-либо методы или свойства элементов управления, поскольку они являются нито-аффинными и должны вызывается только из потока, на котором они были созданы. Используйте Control.Invoke() и/или Control.BeginInvoke(), если это необходимо.

1

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

Control.Invoke является синхронным (ожидает, пока делегат не вернется). Если вы не хотите ждать, вы используете .BeginInvoke() только в очереди команды.

Значение return .BeginInvoke() позволяет проверить, завершен ли метод или подождать, пока он завершен.

3

Чтобы узнать, что говорят люди о DoEvents, расскажите, что может случиться.

Скажите, что у вас есть какая-то форма с данными на нем, а ваше долгое время - сохранение базы данных или создание отчета на ее основе. Вы начинаете сохранять или генерировать отчет, а затем периодически вызываете DoEvents, чтобы экран продолжал рисовать.

К сожалению, экран не просто рисует, но также реагирует на действия пользователя. Это происходит потому, что DoEvents останавливает то, что вы сейчас делаете, чтобы обрабатывать все сообщения Windows, ожидающие обработки вашим приложением Winforms. Эти сообщения включают запросы на перерисовку, а также любой пользовательский ввод, щелчок и т. Д.

Так, например, пока вы сохраняете данные, пользователь может делать что-то вроде того, что приложение показывает модальное диалоговое окно, которое полностью не связанный с долговременной задачей (например, Help-> About). Теперь вы реагируете на новые действия пользователя внутри уже запущенной задачи. DoEvents вернется, когда все события, ожидающие, когда вы его вызвали, будут завершены, а затем ваша долговременная задача будет продолжена.

Что делать, если пользователь не закрывает модальный диалог? Ваша долго работающая задача ждет вечно, пока это диалоговое окно не будет закрыто. Если вы совершаете транзакцию в базу данных и выполняете транзакцию, теперь вы держите транзакцию открытой, пока у пользователя есть кофе. Либо ваша транзакция заканчивается, и вы теряете работу настойчивости, либо транзакция не отключается, и вы потенциально блокируете других пользователей БД.

Что здесь происходит, так это то, что Application.DoEvents делает ваш код реентерабельным. См. Определение википедии here. Обратите внимание на некоторые пункты из верхней части статьи, что для того, чтобы код был реентерабельным, он:

  • Не должен содержать статических (или глобальных) данных, не являющихся постоянными.
  • Должен работать только с данными, предоставленными ему вызывающим абонентом.
  • Нельзя полагаться на блокировки для ресурсов однопользовательского режима.
  • Нельзя вызывать нерентабельные компьютерные программы или подпрограммы.

Это очень маловероятно, что долго работает код в приложении WinForms работает только на данных, передаваемых методу вызываемого абонента, не имеет статические данные, не держит замков, и вызывает только другие реентрантные методы.

Как многие люди здесь говорят, DoEvents может привести к очень странным сценариям в коде. Ошибки, которые могут привести к этому, могут быть очень трудными для диагностики, и ваш пользователь вряд ли скажет вам: «О, это могло произойти, потому что я щелкнул эту несвязанную кнопку, пока я ждал ее сохранения».

1

Использование Backgroundworker, и если вы также пытаетесь обновить GUI нити путем обработки ProgressChanged события (например, для ProgressBar), не забудьте также установить WorkerReportsProgress=true или нить, докладывает прогресс будет умирать в первый раз он пытается позвонить ReportProgress ...

исключение вызывается, но вы можете его не увидеть, если только вы не включили «когда бросили», и на выходе будет показано, что поток вышел.

Смежные вопросы