Use of Application.DoEvents()
Asked Answered
P

9

292

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?

Pelota answered 3/3, 2011 at 14:8 Comment(2)
DoEvents is part of Windows Forms, not the C# language. As such, it can be used from any .NET language. However, it should not be used from any .NET language.Dereliction
It's 2017. Use Async/Await. See the end of @hansPassant answer for details.Epistrophe
P
12

From my experience I would advise great caution with using DoEvents in .NET. I experienced some very strange results when using DoEvents in a TabControl containing DataGridViews. On the other hand, if all you're dealing with is a small form with a progress bar then it might be OK.

The bottom line is: if you are going to use DoEvents, then you need to test it thoroughly before deploying your application.

Pelota answered 22/3, 2011 at 2:47 Comment(2)
Nice answer, but if I may suggest a solution for the progressbar thing which is waays better , I would say, do your work in a separate thread, make your progress indicator available in a volatile, interlocked variable, and refresh your progressbar from a timer. This way no maintenance coder is tempted to add heavyweight code to your loop.Sammiesammons
DoEvents or an equivalent is unavoidable if you have hefty UI processes and don't want to lock up the UI. First option is to not do large chunks of UI processing, but this can make the code less maintainable. However, await Dispatcher.Yield() does a very similar thing to DoEvents and can essentially allow a UI process that locks up the screen to be made for all intents and purposes async.Pritchard
P
503

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.

Palladic answered 3/3, 2011 at 16:35 Comment(5)
This is only the tip of the ice berg, though. I have been using Application.DoEvents without problems until I added some UDP features to my code, which resulted in the problem described here. I'd love to know if there's a way around that with DoEvents.Curia
this is a nice answer, the only thing I feel it misses is an explanation/discussion of yielding execution and thread-safe design or timeslicing in general - but that is easily three times as much more text again. :)Melancholia
Would benefit from a more practical solution than generally "use threads". For example, the BackgroundWorker component manages threads for you, avoiding most of the colorful outcomes of foot-shooting, and it doesn't require bleeding edge C# language versions.Unessential
TL;DR: Using Application.DoEvents is OK, provided that you are a conscientious, disciplined, and well educated developer with good judgement. And the problem is that Application.DoEvents is particularly enticing to developers who are lacking at least one of those qualities (temporarily at least).Miller
@BenVoigt related question: Async/await vs BackgroundWorkerMiller
S
30

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.

Sanorasans answered 3/3, 2011 at 14:10 Comment(2)
It's worth noting that that post is from 2004, before .NET 2.0 and BackgroundWorker helped simplify the "correct" way.Jameyjami
Agreed. Also, the Task library in .NET 4.0 is rather nice as well.Sanorasans
F
27

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.

Fuliginous answered 3/3, 2011 at 14:19 Comment(0)
P
12

From my experience I would advise great caution with using DoEvents in .NET. I experienced some very strange results when using DoEvents in a TabControl containing DataGridViews. On the other hand, if all you're dealing with is a small form with a progress bar then it might be OK.

The bottom line is: if you are going to use DoEvents, then you need to test it thoroughly before deploying your application.

Pelota answered 22/3, 2011 at 2:47 Comment(2)
Nice answer, but if I may suggest a solution for the progressbar thing which is waays better , I would say, do your work in a separate thread, make your progress indicator available in a volatile, interlocked variable, and refresh your progressbar from a timer. This way no maintenance coder is tempted to add heavyweight code to your loop.Sammiesammons
DoEvents or an equivalent is unavoidable if you have hefty UI processes and don't want to lock up the UI. First option is to not do large chunks of UI processing, but this can make the code less maintainable. However, await Dispatcher.Yield() does a very similar thing to DoEvents and can essentially allow a UI process that locks up the screen to be made for all intents and purposes async.Pritchard
D
11

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?

Downstroke answered 3/3, 2011 at 14:10 Comment(2)
what if you want it so you can spin and wait on work to complete in another thread?Melancholia
@Melancholia Then you really should try async-await.Sammiesammons
V
5

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.

Vitrics answered 4/12, 2013 at 8:19 Comment(1)
Refresh does not update the window, though. If a user selects another window on the desktop, clicking back to your window will not have any effect and the OS will list your application as unresponsive. DoEvents() does a lot more than a refresh, as it interacts with the OS through the messaging system.Hagen
E
3

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.
Economically answered 19/12, 2011 at 21:59 Comment(0)
G
3

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
    }
}
Groin answered 3/1, 2019 at 5:8 Comment(2)
You should always block events when calling doevents. Otherwise other events on your app will fire in response and your app could start doing two things at once.Epistrophe
Nice - but leaves one problem - I have a secondary thread running - and need to get inputs "there" - unless DoEvents() is used - the secondary process doesnt receive inputs - how do I get THAT resolved ? Of course I dont want to program my own input processing method just for the secondary thread - If there is already input processing for the main UILeishmaniasis
T
2

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.

Tradelast answered 20/9, 2014 at 16:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.