Использование приложения.Функция doevents()

Can Application.DoEvents() использоваться в C#?

является ли эта функция способом позволить GUI догнать остальную часть приложения, во многом так же, как VB6 DoEvents делает?

9 ответов


Hmya, непреходящая мистика событий (). Против него было огромное количество отрицательной реакции, но никто никогда не объясняет, почему это "плохо". Такая же мудрость, как "не мутировать структуру". Почему среда выполнения и язык поддерживают мутацию структуры, если это так плохо? По той же причине: вы стреляете себе в ногу, если не делаете это правильно. Легко. И делать это правильно требует знания ровно что он делает, что в случае DoEvents () является наверняка не просто Грок.

сразу: почти любая программа Windows Forms фактически содержит вызов DoEvents (). Он умело замаскирован, однако с другим именем: ShowDialog (). Именно DoEvents() позволяет диалогу быть модальным, не замораживая остальные окна в приложении.

большинство программистов хотят использовать DoEvents, чтобы остановить их пользовательский интерфейс от замораживания, когда они пишут свой собственный модальный цикл. Это определенно так; это отправляет сообщения Windows и получает все запросы paint. Проблема, однако, в том, что он не является избирательным. Он не только отправляет сообщения с краской, но и доставляет все остальное.

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

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

вернуться к ShowDialog (). Он выполняет DoEvents (), но обратите внимание, что он делает что-то еще. Это отключает все окна в приложении, кроме диалога. Теперь, когда проблема 3-футов решена, пользователь не может ничего сделать, чтобы испортить логику. Оба режима отказа закрыть окно и начать работу снова решаются. Или, другими словами, у пользователя нет возможности заставить вашу программу запустить код в другом порядке. Он будет выполняться предсказуемо, как и при тестировании кода. Это делает диалоги чрезвычайно раздражающими; кто не ненавидит активный диалог и не может копировать и вставлять что-то из другого окна? Но такова цена.

Что требуется для безопасного использования DoEvents в вашем коде. Установка свойства Enabled всех ваших форм в false-это быстрый и эффективный способ избежать проблем. Конечно, ни один программист на самом деле не любит делать этот. И не делает. Именно поэтому вы не должны использовать DoEvents (). Вы должны использовать потоки. Несмотря на то, что они вручают вам полный арсенал способов стрелять в ногу красочными и непостижимыми способами. Но с тем преимуществом, что вы стреляете только в свою ногу; это не позволит (как правило) пользователю стрелять в нее.

следующие версии C# и VB.NET предоставит другой пистолет с новыми ключевыми словами await и async. Воодушевленный в малой части тревогой причиненной DoEvents и потоками но внутри большая часть дизайна API WinRT, что требует вы должны обновлять свой пользовательский интерфейс во время асинхронной операции. Как чтение из файла.


Это может быть, но это лайфхак.

посмотреть Является Ли DoEvents Злом?.

прямые от в MSDN в разделе это thedev ссылки:

вызов этого метода вызывает ток поток, который нужно приостанавливать пока все обрабатываются сообщения окна ожидания. Если сообщение вызывает событие вызвано, тогда другие области вашего код приложения может выполняться. Это может причина вашего приложения экспонат неожиданное поведение трудно отлаживать. Если вы выполните операции или вычисления, которые принимают a долгое время, часто предпочтительно к выполните эти операции на новом нитка. Для получения дополнительной информации о асинхронное программирование, см. Обзор Асинхронного Программирования.

поэтому Microsoft предостерегает от его использования.

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

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


да, в классе приложений в системе существует статический метод DoEvents.Окна.Пространство имен форм. Система.Окна.Формы.Приложение.DoEvents () можно использовать для обработки сообщений, ожидающих в очереди в потоке пользовательского интерфейса при выполнении длительной задачи в потоке пользовательского интерфейса. Это имеет то преимущество, что пользовательский интерфейс кажется более отзывчивым и не "заблокированным" во время выполнения длинной задачи. Тем не менее, это почти всегда не лучший способ сделать что-то. Согласно Microsoft, вызывая DoEvents "...вызывает приостановку текущего потока во время обработки всех сообщений окна ожидания."Если событие запускается, есть потенциал для неожиданных и прерывистых ошибок, которые трудно отследить. Если у вас есть обширная задача, гораздо лучше делать это в отдельном потоке. Выполнение длинных задач в отдельном потоке позволяет обрабатывать их без вмешательства в работу пользовательского интерфейса. Посмотри здесь дополнительные подробности.

здесь является примером использования DoEvents; обратите внимание, что Microsoft также предостерегает от его использования.


из моего опыта я бы посоветовал с большой осторожностью использовать DoEvents .Сеть. Я испытал некоторые очень странные результаты при использовании DoEvents в TabControl, содержащем DataGridViews. С другой стороны, если все, с чем вы имеете дело, - это небольшая форма с индикатором прогресса, тогда все может быть в порядке.

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


да.

однако, если вам нужно использовать Application.DoEvents, Это в основном указание на плохой дизайн приложения. Возможно, вы хотели бы сделать некоторую работу в отдельном потоке вместо этого?


Я видел много коммерческих приложений, используя "DoEvents-Hack". Особенно, когда рендеринг вступает в игру, я часто вижу это:

while(running)
{
    Render();
    Application.DoEvents();
}

они все знают о зле этого метода. Тем не менее, они используют Хак, потому что они не знают никакого другого решения. Вот некоторые подходы, от блоге by Тома Миллера:

  • установите форму, чтобы все чертежи происходили в WmPaint, и сделайте рендеринг там. Перед завершением метода OnPaint убедитесь, что вы сделали это.Invalidate (); это приведет к повторному запуску метода OnPaint немедленно.
  • P/Invoke в Win32 API и вызовите PeekMessage/TranslateMessage / DispatchMessage. (Функция doevents на самом деле делает что-то подобное, но вы можете сделать это без дополнительных ассигнований).
  • напишите свой собственный класс форм, который является небольшой оболочкой вокруг CreateWindowEx, и дайте себе полный контроль над циклом сообщений. - Решите, что метод DoEvents отлично работает для вас и придерживайтесь его.

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

подробнее о моем случае ...

Я делал следующее (Как было предложено здесь) чтобы убедиться, что индикатор выполнения типа заставки (как отобразить наложение" загрузка"...) обновлено во время длительной работы команды SQL:

IAsyncResult asyncResult = sqlCmd.BeginExecuteNonQuery();
while (!asyncResult.IsCompleted)  //UI thread needs to Wait for Async SQL command to return
{
      System.Threading.Thread.Sleep(10); 
      Application.DoEvents();  //to make the UI responsive
}

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

хороший/ответ: замените строку DoEvents простым вызовом обновления на небольшую панель в центре экрана заставки, FormSplash.Panel1.Refresh(). UI обновляет красиво, и странность DoEvents, о которой другие предупреждали, исчезла.


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

Это может быть полезно для обновления прогресс-баров и уведомления Пользователя о прогрессе в чем-то вроде конструкции и загрузки MainForm, если это займет некоторое время.

в недавнем приложении, которое я сделал, я использовал DoEvents для обновления некоторых меток на экране загрузки каждый раз, когда блок кода выполняется в конструкторе моей MainForm. Поток пользовательского интерфейса в этом случае был занят отправкой электронной почты на SMTP-сервере, который не поддерживал вызовы SendAsync (). Вероятно, я мог бы создать другой поток с методами Begin() и End() и вызвать Send() из них, но этот метод подвержен ошибкам, и я предпочел бы, чтобы основная форма моего приложения не бросала исключения во время построения.


проверьте документацию MSDN для Application.DoEvents метод.