Hmya, the enduring mystique of DoEvents(). There’s been an enormous amount of backlash against it, but nobody ever really explains why it is «bad». The same kind of wisdom as «don’t mutate a struct». Erm, why does the runtime and the language supports mutating a struct if that’s so bad? Same reason: you shoot yourself in the foot if you don’t do it right. Easily. And doing it right requires knowing exactly what it does, which in the case of DoEvents() is definitely not easy to grok.
Right off the bat: almost any Windows Forms program actually contains a call to DoEvents(). It is cleverly disguised, however with a different name: ShowDialog(). It is DoEvents() that allows a dialog to be modal without it freezing the rest of the windows in the application.
Most programmers want to use DoEvents to stop their user interface from freezing when they write their own modal loop. It certainly does that; it dispatches Windows messages and gets any paint requests delivered. The problem however is that it isn’t selective. It not only dispatches paint messages, it delivers everything else as well.
And there’s a set of notifications that cause trouble. They come from about 3 feet in front of the monitor. The user could for example close the main window while the loop that calls DoEvents() is running. That works, user interface is gone. But your code didn’t stop, it is still executing the loop. That’s bad. Very, very bad.
There’s more: The user could click the same menu item or button that causes the same loop to get started. Now you have two nested loops executing DoEvents(), the previous loop is suspended and the new loop is starting from scratch. That could work, but boy the odds are slim. Especially when the nested loop ends and the suspended one resumes, trying to finish a job that was already completed. If that doesn’t bomb with an exception then surely the data is scrambled all to hell.
Back to ShowDialog(). It executes DoEvents(), but do note that it does something else. It disables all the windows in the application, other than the dialog. Now that 3-feet problem is solved, the user cannot do anything to mess up the logic. Both the close-the-window and start-the-job-again failure modes are solved. Or to put it another way, there is no way for the user to make your program run code in a different order. It will execute predictably, just like it did when you tested your code. It makes dialogs extremely annoying; who doesn’t hate having a dialog active and not being able to copy and paste something from another window? But that’s the price.
Which is what it takes to use DoEvents safely in your code. Setting the Enabled property of all your forms to false is a quick and efficient way to avoid problems. Of course, no programmer ever actually likes doing this. And doesn’t. Which is why you shouldn’t use DoEvents(). You should use threads. Even though they hand you a complete arsenal of ways to shoot your foot in colorful and inscrutable ways. But with the advantage that you only shoot your own foot; it won’t (typically) let the user shoot hers.
The next versions of C# and VB.NET will provide a different gun with the new await and async keywords. Inspired in small part by the trouble caused by DoEvents and threads but in large part by WinRT’s API design that requires you to keep your UI updated while an asynchronous operation is taking place. Like reading from a file.
System.Windows.Forms.Application.DoEvents — это метод в .NET Framework, предназначенный для обновления графического интерфейса пользовательского приложения во время выполнения процессов. Он позволяет системе обрабатывать сообщения из очереди сообщений Windows и выполнять другие операции во время выполнения приложения.
Этот метод особенно полезен в ситуациях, когда приложение выполняет длительную операцию и нужно обновить состояние графического интерфейса, чтобы пользователь видел изменения, происходящие в приложении. Без использования Application.DoEvents, графический интерфейс может оставаться замороженным на время выполнения операции.
Пример использования Application.DoEvents:
private void LongOperation()
{
for (int i = 0; i < 100; i++)
{
// Выполнение длительной операции
Application.DoEvents(); // Обновление графического интерфейса
}
}
В данном примере метод LongOperation выполняет длительную операцию в цикле. После каждой итерации цикла вызывается метод Application.DoEvents, чтобы обновить графический интерфейс. Таким образом, пользователь будет видеть изменения в окне приложения во время выполнения операции.
Однако стоит быть аккуратным при использовании метода Application.DoEvents, так как он может привести к неожиданным ошибкам в приложении, особенно если внутри операции происходит изменение состояния приложения. Поэтому следует использовать его с осторожностью и при необходимости обновления графического интерфейса во время выполнения длительных операций.
System.Windows.Forms.Application.DoEvents
Когда в приложении Windows Forms происходит длительная операция, например, обработка больших объемов данных или выполнение сложных вычислений, это может привести к задержкам в работе интерфейса пользователя. В результате пользователь может почувствовать, что приложение застывает или перестает отвечать. Использование метода DoEvents
позволяет устранить эту проблему.
Вызов метода DoEvents
приводит к обработке всех событий в очереди сообщений на текущий момент, включая все отложенные и отложенно выполненные события. Это позволяет UI-потоку приложения обрабатывать события и обновлять интерфейс, даже во время выполнения длительной операции.
Однако, следует быть осторожным при использовании метода DoEvents
, так как он может приводить к сложноотслеживаемым ошибкам и нежелательному поведению. Например, если вызвать метод DoEvents
внутри цикла, который обрабатывает большое количество данных, это может привести к длительной загрузке процессора и замедлению работы всей системы.
Кроме того, использование метода DoEvents
может усложнить отладку и поддержку кода, так как он нарушает линейный ход исполнения программы и делает его менее предсказуемым.
Несмотря на некоторые негативные аспекты, метод DoEvents
может быть полезным инструментом для обеспечения отзывчивости интерфейса пользователя в приложениях Windows Forms, особенно при работе с длительными операциями. Однако перед его использованием следует тщательно взвесить преимущества и недостатки.
Описание
Обычно, когда вы запускаете приложение с графическим интерфейсом, Windows-форма переходит в режим ожидания и ожидает пользовательских действий, таких как нажатия клавиш или мыши. В это время код приложения не выполняется и форма может казаться «замороженной». Если в этот момент выполняется некий длительный процесс, например, обработка больших объемов данных, то приложение может «зависнуть», не реагируя на действия пользователя.
Метод System.Windows.Forms.Application.DoEvents
позволяет избежать блокировки интерфейса и дает возможность сообщениям в очереди обрабатываться. Он проверяет наличие ожидающих событий и позволяет им быть обработанными. Это позволяет форме продолжить работу и отвечать на пользовательские действия даже во время выполнения долгих операций.
Одним из примеров использования System.Windows.Forms.Application.DoEvents
может быть длительное выполнение работы в отдельном потоке. Вы можете использовать этот метод в цикле для проверки флага остановки или других условий и вызывать его, чтобы позволить форме реагировать на события пользователя.
Три ужасные фичи программирования из прошлого
Время на прочтение
6 мин
Количество просмотров 56K
Я верю в программистское клише о том, что большинство плохих фич имеет причины для существования. Ненавидимый многими оператор goto
позволяет быстро и удобно выбраться из глубоко вложенной структуры, если пользоваться им с умом. Определённая степень нестрогости типов позволяет им быть более изящными. Указатели памяти могут заставить вас возненавидеть свою жизнь, но они были критически важны в те годы, когда компьютерное «железо» было слабее современного умного термостата. Список можно продолжать.
Но когда я вспоминаю об этих запылённых старых реликтах, то осознаю, что некоторые старые идеи настолько плохи, что лучше всего было бы сжечь их навечно. В этой статье я расскажу о трёх фичах языков программирования, которые были настоящим кошмаром.
1. On Error Resume Next (классический VB 6)
On Error Resume Next
реализует подход, который в большинстве ситуаций возникновения ошибок хуже всего: «просто продолжай выполнение». При этом он делает его правилом по умолчанию для любой проблемы. Представьте, если бы мы реагировали так же на сбои в реальной жизни. Попал в аварию на машине? Просто продолжай ехать!
' Напишем этот код на случай, если не сможем
' подключиться к базе данных.
On Error Resume Next
Dim TotalPayment As Decimal
Dim TotalHours As Decimal
Dim HourlyRate As Decimal
TotalPayment = 5000
' Ой, мы забыли задать TotalHours!
' Код не должен работать, но у нас есть иммунитет к ошибкам.
HourlyRate = TotalPayment / TotalHours
' Всё вроде правильно. Давайте обновим базу данных!
Call UpdateDatabase(HourlyRate)
Но подождите, это ещё не всё. On Error Resume Next
не имеет никаких границ. Он будет продолжать свой путь через длинные цепочки ошибок. Он даже не обязан находиться в начале модуля вашего кода (можно вставить его в любое место кода и переключиться на обычный режим при помощи On Error Resume 0
).
On Error Resume Next
гарантирует, что вы достигнете конца процедуры кода, и это соблазняет нас своей определённостью. Вам не нужно беспокоиться, что будет пропущена какая-то важная часть кода очистки. Но в пункт назначения вы доберётесь не совсем в том виде, в котором начинали путь. Это агрессивная стратегия «дойти до конца любой ценой», только «доходить до конца», если тебе не удалось выполнить обязательную задачу, обычно и не стоит.
Единственная возможная причина, по которой можно использовать On Error Resume Next
— это полное отсутствие кода обработки ошибок, когда вам нужно пропустить все не особо важные проблемы, которые вы игнорируете. Например, вы можете осознанно создавать файл, который уже может существовать, настраивать принтер, который может не поддерживаться, или выполнять вычисление, которое может привести к делению на ноль. Объективно, в такой момент вы поступаете не очень хорошо, но мы вас прощаем. Однако если вы используете On Error Resume Next
, то усугубляете своё невежество, беззаботно двигаясь в сторону ещё большей катастрофы.
Не буду врать. Я пользовался этой конструкцией (и она мне нравилась!) задолго до того, как начал учиться программированию по-настоящему. А теперь она практически мертва. VB.NET, текущая версия VB, которая влачит жалкое существование в мире .NET, заменила On Error Resume Next
логичной структурированной обработкой ошибок. Хотя так ли это на самом деле? Оказывается, в современном .NET можно по-прежнему использовать обработку ошибок из созданного десятки лет назад классического VB, в том числе и On Error Resume Next
. Это полное безумие, но, к счастью, кажется, сегодня этим никто не занимается.
Тем не менее, On Error Resume Next
всё ещё существует в реальном коде. Возможно, вы обнаружите, что эта конструкция ломает макрос электронной таблицы Excel, которую использует ваш отдел продаж. Спасибо тебе за это, VBA.
2. DoEvents (Windows Forms)
Даже по расплывчатому имени встроенной функции DoEvents()
уже можно понять, что стоит готовиться к чему-то уродливому. И метод Application.DoEvents()
нас не разочаровал.
DoEvents()
— это когда-то популярный, но очень опасный костыль для Windows-приложений, не желающих работать с многопоточностью. Например, классическим сценарием его использования является обновление UI в коротком цикле. Представим, что мы выполняем очень затратную по времени математическую операцию и помещаем значения в list box. Пользователь не увидит ни одно из этих чисел, пока вы не вернёте управление и Windows не сможет обновить окно. Обычно этого не случается, пока код не завершит работу, если вы не выполняете эту работу в фоне в отдельном потоке.
DoEvents()
просит приложение на мгновение приостановиться, чтобы Windows могла сделать то, что ей нужно. Так что если вы вызовете DoEvents()
в цикле, то, вероятно, дадите операционной системе возможность перерисовать окно:
for (int i = 0; i < 1000000; i++)
{
listBox.Items.Add(someCalculation(i));
// Этот цикл блокирует UI. Мы можем отрефакторить код
// или просто вставить этот маленький костыль...
Application.DoEvents();
}
Но после этого ситуация быстро начинает развиваться по наклонной. Проблема в том, что ты точно не знаешь, что будет делать DoEvents()
. Другие части приложения могут получать сообщения Windows (допустим, если пользователь куда-то нажал), и могут начать выполнять свой код в месте, которое ты создал при вызове DoEvents()
. Звучит печально, но на самом деле это очень весело (если вам нравится отладка по ночам), потому что результат на каждом компьютере будет чуть-чуть различаться!
Как знает любой программист, самая худшая проблема — это не когда что-то не работает, а когда программа работает на одном компьютере, но обладает такой вариативностью, что в другом месте способна загадочным образом вылетать. И если у вас нет пока этой проблемы, DoEvents()
вносит как раз нужное количество недетерминированности, чтобы она появилась.
Разумеется, работать с потоками сложно. Но в .NET уже есть простые в использовании инструменты, например, компонент BackgroundWorker
, позволяющие выполнять всю нужную работу в другом потоке при помощи простой модели на основе событий. Нет необходимости придумывать какие-то костыли с помощью DoEvents()
, и их создают очень немногие. Тем не менее, этот метод по-прежнему таится в библиотеке классов, импортированный из олдскульного VB и доступный для каждого языка .NET, в том числе и для современного C#.
Отвратительно.
3. Динамический Goto (COBOL)
В отличие от предыдущих проблем, этот кошмар программирования лично меня никогда не касался. (Может, я и стар, но я не стар как COBOL.) Однако эта фича настолько безумна, что я не могу её не упомянуть.
Представьте, что вы программируете на древнем языке с загадочными операторами управления потоком. А потом вы обнаруживаете, что этот язык способен изменять свой собственный код в процессе исполнения, меняя пути исполнения, по которым он движется через различные синтаксические структуры. Замысловато! Если всё работает, то программа сохраняет своё тонкое равновесие. Если она не работает, то вы получаете нечто вроде уробороса, поедающего свой собственный хвост.
Фича, которую я называю динамическим goto, имеет в языке COBOL имя оператора ALTER
. Он позволяет изменять существующую команду GO TO
так, чтобы она передавала контроль не той части программы, которая прописана в коде. Оператор GO TO
даже можно изменять многократно, перемещать в разные части кода, как по лабиринту. Проще говоря, ALTER
берёт всё то, что вы ненавидите в GO TO
— запутывающий поток выполнения, спагетти-код — и значительно это усиливает.
Вот о какой структуре я говорю
IF WS-X = 2 THEN
ALTER SELECT-PATH TO PROCEED TO PATH-2.
ELSE IF WS-X = 3 THEN
ALTER SELECT-PATH TO PROCEED TO PATH-3.
END-IF.
GO TO SELECT-PATH.
SELECT-PATH.
GO TO PATH-1.
В этом примере команда GO TO SELECT-PATH
может перенести нас к метке SELECT-PATH
(которая затем направляет нас к PATH-1
). Но в зависимости от предыдущей проверки переменной её можно изменить так, чтобы она перенаправляла нас к PATH-2
или к PATH-3
. И кто знает, что могло бы произойти в других частях кода. Другими словами, мы не можем точно сказать, куда приведёт нас GO TO
, если только не знаем полную историю выполнения приложения. Но не волнуйтесь, наверняка где-то есть лог, который можно изучить.
Можно назвать это метапрограммированием, но большинство из нас просто скажет, что это Очень Плохая Идея.
Да, на маломощных компьютерах того времени с небольшим размером стека подобная чёрная магия имела смысл (возможно?). И может быть, были времена, когда On Error Resume Next
и DoEvents()
были наилучшими компромиссами в случае сложных проблем. Сегодня, спустя шестьдесят с лишним лет после создания языка, оператор ALTER
считается устаревшим, и запланировал к удалению. Что ж, по крайней мере, они решили исправить ошибку.
Когда в следующий раз кто-то справедливо укажет вам на странные особенности JavaScript, на безумие необъявленных переменных или неявную типизацию любого языка, напомните ему, что всегда может быть хуже. И хуже было.
Надеюсь, вы никогда не столкнётесь с этим кошмаром в старой кодовой базе. А если у вас есть собственные любимые чудовищные конструкции прошлого, то поделитесь ими в комментариях!
C#,Windows Form, WPF, LINQ, Entity Framework Examples and Codes
Application.DoEvents Method Processes all Windows messages currently in the message queue.
To understand how Application.DoEvents() method works, create a new winforms application and add a picturebox and assign a image and add a button to the form.
write the below code, in form1.cs file Form1_Load method and button1_Click method
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
private void Form1_Load(object sender, EventArgs e) { pictureBox1.Visible = false; } private void button1_Click(object sender, EventArgs e) { pictureBox1.Visible = true; //Application.DoEvents(); System.Threading.Thread.Sleep(5000); pictureBox1.Visible = false; } |
Now run the above code.
When you click on button, you cannot see the picture box, though we are making picture box visible on button click.
Now uncomment the Applicaton.DoEvents line and run the code. You can see the picture box is visible on button click because calling Appliation.DoEvents method makes the current thread to be suspended all the waiting windows messages in the queue to be processed.
You may also like
Can Application.DoEvents()
be used in C#?
Is this function a way to allow the GUI to catch up with the rest of the app, in much the same way that VB6’s DoEvents
does?
This question is related to
c#
winforms
doevents
Hmya, the enduring mystique of DoEvents(). There’s been an enormous amount of backlash against it, but nobody ever really explains why it is «bad». The same kind of wisdom as «don’t mutate a struct». Erm, why does the runtime and the language supports mutating a struct if that’s so bad? Same reason: you shoot yourself in the foot if you don’t do it right. Easily. And doing it right requires knowing exactly what it does, which in the case of DoEvents() is definitely not easy to grok.
Right off the bat: almost any Windows Forms program actually contains a call to DoEvents(). It is cleverly disguised, however with a different name: ShowDialog(). It is DoEvents() that allows a dialog to be modal without it freezing the rest of the windows in the application.
Most programmers want to use DoEvents to stop their user interface from freezing when they write their own modal loop. It certainly does that; it dispatches Windows messages and gets any paint requests delivered. The problem however is that it isn’t selective. It not only dispatches paint messages, it delivers everything else as well.
And there’s a set of notifications that cause trouble. They come from about 3 feet in front of the monitor. The user could for example close the main window while the loop that calls DoEvents() is running. That works, user interface is gone. But your code didn’t stop, it is still executing the loop. That’s bad. Very, very bad.
There’s more: The user could click the same menu item or button that causes the same loop to get started. Now you have two nested loops executing DoEvents(), the previous loop is suspended and the new loop is starting from scratch. That could work, but boy the odds are slim. Especially when the nested loop ends and the suspended one resumes, trying to finish a job that was already completed. If that doesn’t bomb with an exception then surely the data is scrambled all to hell.
Back to ShowDialog(). It executes DoEvents(), but do note that it does something else. It disables all the windows in the application, other than the dialog. Now that 3-feet problem is solved, the user cannot do anything to mess up the logic. Both the close-the-window and start-the-job-again failure modes are solved. Or to put it another way, there is no way for the user to make your program run code in a different order. It will execute predictably, just like it did when you tested your code. It makes dialogs extremely annoying; who doesn’t hate having a dialog active and not being able to copy and paste something from another window? But that’s the price.
Which is what it takes to use DoEvents safely in your code. Setting the Enabled property of all your forms to false is a quick and efficient way to avoid problems. Of course, no programmer ever actually likes doing this. And doesn’t. Which is why you shouldn’t use DoEvents(). You should use threads. Even though they hand you a complete arsenal of ways to shoot your foot in colorful and inscrutable ways. But with the advantage that you only shoot your own foot; it won’t (typically) let the user shoot hers.
The next versions of C# and VB.NET will provide a different gun with the new await and async keywords. Inspired in small part by the trouble caused by DoEvents and threads but in large part by WinRT’s API design that requires you to keep your UI updated while an asynchronous operation is taking place. Like reading from a file.
It can be, but it’s a hack.
See Is DoEvents Evil?.
Direct from the MSDN page that thedev referenced:
Calling this method causes the current
thread to be suspended while all
waiting window messages are processed.
If a message causes an event to be
triggered, then other areas of your
application code may execute. This can
cause your application to exhibit
unexpected behaviors that are
difficult to debug. If you perform
operations or computations that take a
long time, it is often preferable to
perform those operations on a new
thread. For more information about
asynchronous programming, see
Asynchronous Programming Overview.
So Microsoft cautions against its use.
Also, I consider it a hack because its behavior is unpredictable and side effect prone (this comes from experience trying to use DoEvents instead of spinning up a new thread or using background worker).
There is no machismo here — if it worked as a robust solution I would be all over it. However, trying to use DoEvents in .NET has caused me nothing but pain.
Yes, there is a static DoEvents method in the Application class in the System.Windows.Forms namespace. System.Windows.Forms.Application.DoEvents() can be used to process the messages waiting in the queue on the UI thread when performing a long-running task in the UI thread. This has the benefit of making the UI seem more responsive and not «locked up» while a long task is running. However, this is almost always NOT the best way to do things.
According to Microsoft calling DoEvents «…causes the current thread to be suspended while all waiting window messages are processed.» If an event is triggered there is a potential for unexpected and intermittent bugs that are difficult to track down. If you have an extensive task it is far better to do it in a separate thread. Running long tasks in a separate thread allows them to be processed without interfering with the UI continuing to run smoothly. Look here for more details.
Here is an example of how to use DoEvents; note that Microsoft also provides a caution against using it.
Yes.
However, if you need to use Application.DoEvents
, this is mostly an indication of a bad application design. Perhaps you’d like to do some work in a separate thread instead?
I saw jheriko’s comment above and was initially agreeing that I couldn’t find a way to avoid using DoEvents if you end up spinning your main UI thread waiting for a long running asynchronous piece of code on another thread to complete. But from Matthias’s answer a simple Refresh of a small panel on my UI can replace the DoEvents (and avoid a nasty side effect).
More detail on my case …
I was doing the following (as suggested here) to ensure that a progress bar type splash screen (How to display a «loading» overlay…) updated during a long running SQL command:
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
}
The bad: For me calling DoEvents meant that mouse clicks were sometimes firing on forms behind my splash screen, even if I made it TopMost.
The good/answer: Replace the DoEvents line with a simple Refresh call to a small panel in the centre of my splash screen, FormSplash.Panel1.Refresh()
. The UI updates nicely and the DoEvents weirdness others have warned of was gone.
I’ve seen many commercial applications, using the «DoEvents-Hack». Especially when rendering comes into play, I often see this:
while(running)
{
Render();
Application.DoEvents();
}
They all know about the evil of that method. However, they use the hack, because they don’t know any other solution. Here are some approaches taken from a blog post by Tom Miller:
- Set your form to have all drawing occur in WmPaint, and do your rendering there. Before the end of the OnPaint method, make sure you do a this.Invalidate(); This will cause the OnPaint method to be fired again immediately.
- P/Invoke into the Win32 API and call PeekMessage/TranslateMessage/DispatchMessage. (Doevents actually does something similar, but you can do this without the extra allocations).
- Write your own forms class that is a small wrapper around CreateWindowEx, and give yourself complete control over the message loop.
-Decide that the DoEvents method works fine for you and stick with it.
The DoEvents does allow the user to click around or type and trigger other events, and background threads are a better approach.
However, there are still cases where you may run into issues that require flushing event messages. I ran into a problem where the RichTextBox control was ignoring the ScrollToCaret() method when the control had messages in queue to process.
The following code blocks all user input while executing DoEvents:
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace Integrative.Desktop.Common
{
static class NativeMethods
{
#region Block input
[DllImport("user32.dll", EntryPoint = "BlockInput")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool BlockInput([MarshalAs(UnmanagedType.Bool)] bool fBlockIt);
public static void HoldUser()
{
BlockInput(true);
}
public static void ReleaseUser()
{
BlockInput(false);
}
public static void DoEventsBlockingInput()
{
HoldUser();
Application.DoEvents();
ReleaseUser();
}
#endregion
}
}
Application.DoEvents can create problems, if something other than graphics processing is put in the message queue.
It can be useful for updating progress bars and notifying the user of progress in something like MainForm construction and loading, if that takes a while.
In a recent application I’ve made, I used DoEvents to update some labels on a Loading Screen every time a block of code is executed in the constructor of my MainForm. The UI thread was, in this case, occupied with sending an email on a SMTP server that didn’t support SendAsync() calls. I could probably have created a different thread with Begin() and End() methods and called a Send() from their, but that method is error-prone and I would prefer the Main Form of my application not throwing exceptions during construction.