|
|||||||||||||||||||||||||
|
|||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
Note: This is an unedited contribution. If this article is inappropriate,
needs attention or copies someone else's work without reference then please
Report This Article
Proper progress notificationThis is going to be met with great skepticism but I’m going to say it anyway… There is only one best way to show a progress window for long operations. That answer is to show a modal progress window on the UI thread while the work is done on the worker thread. First let’s look at why the progress window must be on the UI thread: If you try to show a dialog on a worker thread, that dialog cannot have an owner from the calling thread. Try and you’ll get the exception:
And showing the dialog on another thread without an owner (modal or non) makes the dialog appear non-modal and therefore allows the user to click on your main window and loose the progress window to the depths of the Z-order making it difficult for the user to know if the operation is still in progress or not. One trick you might be tempted to try is setting the progress window’s Next, let’s examine why the work ought to be done in a separate thread at all. If you don’t and the application doesn’t call So why not use DoEvents to allow your app to respond? Not only would you need to call frequent Lastly, should the progress dialog modal or modeless? You might think that it doesn’t matter. Assuming you have code that needs to run after the worker thread is complete, does it matter if the code is running in some “Thread is complete” event handler or immediately after your progress window closes? In many cases I admit it doesn’t matter. But I assert that it is good practice to do the later. This is because your code is always in some way being calling from an event handler. Whether it’s a button click event or some object raising an event, with the exception of Sub Main, you are always running in an event handler of some sort. And if you spawn your worker thread and return immediately, the caller of that event may run some additional code. Now because you’re showing a progress bar, you are indicating to the user that this operation did not finish. So why would you return from the event as if the operation did finish? What if there are multiple observers of that event. There’s nothing stopping another module from adding a second event handler to that same event. And if you return immediately, that code will run before your long operation has completed. This may not be a problem but it could.
Also, the event might do its own thing afterwards assuming you are done with the operation. For example, imagine an event So, how do we do all this? While there is more than one way, the easiest in my opinion is to use the Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)_
Handles Button1.Click
BackgroundWorker = New System.ComponentModel.BackgroundWorker
BackgroundWorker.RunWorkerAsync()
FormProgress = New FormProgress
FormProgress.ShowDialog()
End Sub
Private Sub BackgroundWorker_DoWork(ByVal sender As Object, _
ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker.DoWork
' do your long operation here
End Sub
Private Sub BackgroundWorker_RunWorkerCompleted(ByVal sender As Object, _
ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) _
Handles BackgroundWorker.RunWorkerCompleted
FormProgress.Close()
End Sub
Whalla! It’s as simple as that. Presumably you’d also report a percent finished back thru the One caveat: users can still enter Alt+F4 or click the red X in the upper right corner of your progress dialog if it’s there so you’ll need to prevent this. An easy way to get around this is to just cancel the Form Close unless it’s coming from our code. Private AllowClose As Boolean = False
Private Sub FormProgress_FormClosing(ByVal sender As Object, _
ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
If e.CloseReason = CloseReason.UserClosing And AllowClose Then
e.Cancel = True
End If
End Sub
Public Sub ForceClose()
AllowClose = True
Me.Close()
AllowClose = False
End Sub
Just call
|
||||||||||||||||||||||||