It's an old VB developer trick, but it also works quite well in .NET: to present responsive WinForms interfaces, do most of your heavy lifting in the first Paint event, rather than in the Load event. I've seen many naive WinForm developers perform database queries and other heavyweight operations in the Form Load event, which absolutely kills perceived performance-- the form doesn't even display until all the work is done! It's much better to render part of the form first so the user gets immediate visual feedback, beyond an hourglass cursor, that stuff is happening.
The easiest way to deliver a semi-responsive interface is to attach an IsFirstPaint boolean to the paint event, and move the intensive part of the work you're doing on Load into Paint. However, for this to work, you still need to yield a bit to give the form time to paint some of its elements-- that means putting a little bit of DoEvents spackle in there, prior to going off and doing a bunch of work.
Now, I like the effort/results ratio that DoEvents and IsFirstPaint delivers, but I've never really been comfortable with DoEvents in the brave new world of .NET. It seems like.. old-school Win3x cooperative multitasking. I'm not the only developer to question the meaning of DoEvents in .NET, as this MSDN threading chat illustrates:
Q: What is the reason for maintaining DoEvents in the .NET framework? Why was it not limited to an assembly in the Microsoft.VisualBasic namespace? What is the need for DoEvents when there is proper support for multi-threaded applications?A: Jason (Microsoft) DoEvenets is a holdover from VB 5.x, but it is still useful in the .NET Framework world. If you have a loop that runs for a long time, it is often easier to call DoEvents than to refactor your loop to use true .NET threading.
Q: Why is DoEvents in the BCL namespace?
A: Glenn (Microsoft) Threading is difficult, and should be avoided if there's an easier way. If all you need to do is yield on your UI thread, DoEvents is perfect.
Q: DoEvents is evil?
A: Glenn (Microsoft) Yielding on the UI thread is a legitimate Windows programming practice. It always has been. DoEvents makes it easy, because the situations in which you need to use it are simple.
Application.DoEvents() - The call of the devil.DoEvents messes up the normal flow of your application. If I recall correctly, DoEvents is asynchronous which means it terminates before the application has actually processed any outstanding events, so if you're using it in a procedure with many sequential statements, calling DoEvents causes a huge disturbance whenever it's called. Basically, if you find yourself needing to call DoEvents anywhere, think about starting another thread instead, or using asynchronous delegates.
Imagine this if you will: You have a button on your form that, when clicked, does some complex processing. During the complex processing it also intermittently calls DoEvents to keep the application's user interface "responsive" -- not the best method, I would have used async delegates, but we're talking about a mediocre programmer here. Anyhow, the user sees the application still responding and no indication that there's some processing going on. So the user clicks that button again WHILE the processing is going on! The button responds to the event and starts another processing thread but it isn't actually a thread here, I hope you get what I'm saying. So, like I said earlier, DoEvents screws up the flow of the application too easily.
Of course, DoEvents and IsFirstPaint are only partial solutions to make the forms look like they load faster. Never underestimate the power of perceived performance, but the actual interface is still unresponsive. If you want a fully reponsive interface with background processing, the correct way to do it is with threading and Control.Invoke. A lot of armchair developers like to conveniently forget this, but threading is hard. Dangerous, even. It's not a coding burden you take on lightly. When things start happening in asynchronous, indeterminate order instead of the deterministic 1-2-3 order you'd expect.. things get absurdly difficult really quickly, as noted in this Roy Osherove blog post:
Beware - I've spent quite a lot of time on this problem. We are building a client application fetching information from a server (using SOAP) in the background, and displaying the information in the windows UI when it arrives. Unfortunately it arrives in another thread. On top of this we have implemented a cache the UI components should be reading from. The problem is that it is insufficient to call Control.Invoke() when changing information in, for example, a listbox. We also need to make sure the underlying data to be displayed does not change while the UI thread reads it. And simple synchronization is not enough as this will only give atomic access to a single element, when we need to block the entire array while updating the control.The best solution I've found until now is to model an UI thread and background threads as two separate processes (implemented as .NET threads) that only communicates through messages and has NO shared memory. The messages are modeled through a homebuild "mailbox" interface. The modeled is inspired by the language Erlang.
You've never truly debugged an app until you've struggled with an obscure threading issue. Threading is a manly approach for tough guys, and it will put hair on your chest-- but you may not have any left on your head when you're done.
I agree that DoEvents is not exactly great programming practice, but even Microsoft recommends using it in lieu of hard-core threading for simple problems. So it's something of a tradeoff. Easier WinForms threading is coming in .NET 2.0, but in the meantime, I'd look into the backgroundworker code samples.
DoEvents is really bad. You can use a progress bar or some other method to let the user know what is going on. DoEvents lets other events execute out of order and can really ruin your day :)
adam on February 6, 2006 12:15 PMAs with so many things, it depends. You definitely need to be careful. There is a followup post on DoEvents here:
http://www.codinghorror.com/blog/archives/000370.html
Jeff Atwood on February 6, 2006 12:54 PMI's definitly a cheap shot however, sometimes a cheap shot is all it takes...
johnmcpherson1@yahoo.com on April 24, 2006 1:26 PMi found this article by searching for "DoEvents is evil"
i've inherited a gigantic broken application that makes liberal, sensless use of DoEvents.
The horror! the horror!
lb on October 22, 2006 8:44 AMDoEvents is evil!?
Who said DoEvents allows a user to start multiple threads?
*** THATS THE WHOLE POINT; FOOL ***
If you don't want this, all you need is to declare a boolean variable named 'IsProcessing' or something... and, well, you get the rest.
me on November 18, 2006 9:59 AMI still don't get the idea
why people said that DoEvents is evil!?
I never used it before, while testing i see that DoEvents is almost same as refresh()
Am I correct?
To the Guy who said 'the user clicks the button again'
You need to lock out the user from doing that.
It is not that difficult
Here is a sample of how I do it:
'--------------------------------------------
Imports System.Windows.Forms
Public Class frmImporter
Inherits System.Windows.Forms.Form
Private mynProcessing As Boolean = False
#Region " Button Handlers: "
Private Sub CancelButtonClickHandler(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCancel.Click
InvokeCancel()
End Sub
Private Sub ImportButtonClickHandler(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnImport.Click
Try
InvokeImport()
Catch ex As Exception
MsgBox1(Me, GetMessageException(ex))
End Try
End Sub
End Region
#Region " Public Invocations: "
Public Sub InvokeImport()
Try
If mynProcessing Then Return ' Don't Allow this to run twice
Cur(True)
For Each dr As DataRow In dt.Rows
'Do the magic
If Not mynProcessing Then Exit For
Next
Catch ex As Exception
Throw ex
Finally
Cur(False)
End Try
End Sub
Public Sub InvokeCancel()
mynProcessing = False
Application.DoEvents()
End Sub
#End Region
#Region " Cursor And Form Lock "
Public Sub Cur(ByVal ynWAIT As Boolean)
If ynWAIT Then
Windows.Forms.Cursor.Current = Cursors.WaitCursor
btnImport.Enabled = False
btnCancel.True = False
mynProcessing = True
Else
Windows.Forms.Cursor.Current = Cursors.Default
btnImport.Enabled = True
btnCancel.Visible = False
mynProcessing = False
End If
Application.DoEvents()
End Sub
#End Region
End Class
Doh! I mean:
btnCancel.Visible = False
Dry on March 8, 2007 4:16 AMThis is such a basic (ewwwe a pun)thing that it saddens me that so many 'programmers' don't get it... If you understand your own program flow, if you KNOW HOW YOUR CODE WORKS, then DoEvents is not only fine, but it's brilliant and you will have no problems.
If you are one of the *many-and-growing* who don't really even understand your own program's flow (*cough* 90% open source *cough*), you should not only not use DoEvents, but you shouldn't use delegation or mutithreading either because you aren't a programmer , you are a voodoo practitioner...
I know you read in that it was 'bad' on a blog or a magazine, or a great book, but they should have to caviat that with '...if you have no idea what your *really* doing....'
sigh.....
Daniel on October 16, 2007 10:26 AMIt may be because I am relatively new to all this, but I struggle to see what all the fuss is about.
As I see it, .NET 2.0 programmers have three choices:
(1) Use Application.DoEvents()
(2) Use a threading model of your choice
(3) Use the BackgroundWorker class
Surely they all have the same issue: Your application needs to be written carefully, and, in particular, you need to disable certain user controls before you start a long-running process, so that the application doesn't get its knickers in a twist.
All three methods require care - and I can't see that one is massively better than any other.
I agree with Daniel - it's just a matter of knowing how your application works. Any strong preference for one method over the other seems to me to be rather evangelical rather than practical.
I will be using the BackgroundWorker class in my next application. I will be using it purely because I want to gain experience with the various practices in .NET. I have a feeling that using Application.DoEvents() would be much simpler, and make no difference whatsover to the efficiency, maintainability and user-experience of the application.
Perhaps the advantages or disadvantages of each approach becomes more apparent in long, sprawling programs?
There is only one case where I really had to use Application.DoEvent().
Scenario: A thread is processing incoming data and post it (via BeginInvoke) to the UI thread for display. At some point, you want to terminate properly both data gathering and data display processes.
The thing is, depending on the system load, you might have no idea how many BeginInvoke calls need to be processed. Assuming that your data gathering thread is dead, how could you be sure that no more BeginInvoke calls remains to be processed? Simply call Application.DoEvent(), this will get rid of every BeginInvoke calls still in the queue. There is no other ways.
Don't tell me to switch from BeginInvoke to Invoke, in some case you simply can't. Our only hope in that case is that Microsoft NEVER remove Application.DoEvent(). Quickly think of it, I'm pretty sure that the only scenario where this is really needed.
In brief, when you want to be sure that no more BeginInvoke calls launched from a dead thread remains to be done, call Application.DoEvent().
Jean Gauthier on December 18, 2007 1:14 AMThere is always people who will feel superior to put in practice "tough" or "macho" programming techniques, wasting time "and clients money" using something that later will bring bigger headaches (threading) for something that can be solved the easy way.
Dan Tohatan is wrong, I agree with the "refresh" comment, the way to make the Interface
display properly while precessing something long in the loop is to call the refresh method in the form (if you add a progress bar is even better). so that if you are switching screens to other apps and coming back to check the progress you will still see something and not just a white square.
The DoEvents is used ONLY FOR the PURPOSE to allow the user to actually click on some control in the form while processing (best example: a cancel button), so DOEVENTS is really very useful for what it was intendend, now if people use it the wrong way that's another story... I can see many related posts "DoEvents Considered Harmful" :)
I started out as a Java programmer personally - threading there is - in my humble opinion - much simpler where your able to simply inherit from the thread object then use a nice simple set of flags in the new object and some nice accessor methods to control the thread flow.
However that said I really don't see what the big fuss about using thread in .NET is either. Generally good OO program doesn't INCLUDE much - if any shared memory and simply adhering to good coding procedures keeps your code clean enough for threading to be safe.
That said I concede after reading this that App.DoEvents certainly has its place in the programmers arsenal. Never throw away a perfectly good tool.
S on May 16, 2008 1:39 PMGood work, Jeff, thx. I found another good resource at http://blogs.msdn.com/jfoscoding/archive/2005/08/06/448560.aspx - it provides some solutions for the known problems of DoEvents().
Anonymous on June 21, 2008 5:12 AMSorry, the link's gone... here again: http: // blogs.msdn.com / jfoscoding / archive / 2005 / 08 / 06 / 448560.aspx
Anonymous on June 21, 2008 5:14 AM@Daniel (on October 16, 2007 09:26 AM)
Do you think that open source programmers don't know the program's flow? And what do you think about the fact that Linux (caution, an open source operating system) don't need to accommodate an obscure DoEvents() method? Is a possible answer the fact that in Linux the XServers are clearly uncoupled from XClients through the XProtocol?
In other words: Is it possible, that MS Windows is made by *many-and-growing* programmers (as you said) or why is there a need for a such dirty method like 'DoEvents()' (- DoEvents() WILL break your program's flow)?
I have no preference... please be careful when drawing comparisons.
Anonymous on June 22, 2008 3:45 AMSorry but the only real reason to use doevents is laziness.
Nothing other.
For refreshing a button when doing heavy load stuff -- I just laught when i read that.
RaiseLee on October 22, 2008 7:58 AMDoEvents is very useful! It's certainly not evil.. unless you're the soft of programmer who gets confused easily.. in which case you should probably go play with some crayons or revert to VB6
Kristian on March 14, 2009 3:25 AMThanks for the information. I decided to use DoEvents to allow users to cancel out of a long-running process. With your support, I now feel reassured.
And yet, strangely, emasculated, too.
Jon Z on February 6, 2010 9:29 PMThe comments to this entry are closed.
|
|
Traffic Stats |