Understanding the BackgroundWorker Component
Pages: 1, 2
Let's switch to the code behind of the Windows form and do the coding. First, import the following namespace:
Imports System.ComponentModel
When the Start button is clicked, you first initialize some of the controls
on the form. You also change the cursor to an hourglass (Cursors.WaitCursor)
so that the user knows the application is working. You then get the BackgroundWorker
component to spin off a separate thread using the RunWorkAsync()
method. You pass the number entered by the user as the parameter for this method:
Private Sub btnStart_Click( _
ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnStart.Click
lblResult.Text = ""
btnCancel.Enabled = True
btnStart.Enabled = False
ProgressBar1.Value = 0
Me.Cursor = Cursors.WaitCursor
BackgroundWorker1.RunWorkerAsync(txtNum.Text)
End Sub
The DoWork event of the BackgroundWorker component will invoke
the SumNumbers() function (which I will define next) in a separate
thread. This event (DoWork) is fired when you call the RunWorkerAsync()
method (as was done in the previous step).
Private Sub BackgroundWorker1_DoWork( _
ByVal sender As System.Object, _
ByVal e As System.ComponentModel.DoWorkEventArgs) _
Handles BackgroundWorker1.DoWork
'This method will run on a thread other than the UI thread.
'Be sure not to manipulate any Windows Forms controls created
'on the UI thread from this method.
Dim worker As System.ComponentModel.BackgroundWorker = _
CType(sender, System.ComponentModel.BackgroundWorker)
e.Result = SumNumbers(CType(e.Argument, Double), worker, e)
End Sub
The SumNumbers() function basically sums up all the numbers from
0 to the number specified. It takes in three arguments--the number to sum up
to, the BackgroundWorker, and the DoWorkEventArgs. Note that within
the For loop, you check to see if the user has clicked on the Cancel
button (the event will be defined later in this article) by checking the value
of the CancellationPending property. If the user has cancelled
the process, set e.Cancel to True. For every ten iterations,
I will also calculate the progress completed so far. If there is progress (when
the current progress percentage is greater than the last one recorded), then
I will update the progress bar by calling the ReportProgress()
method of the BackgroundWorker component. You should not call the ReportProgress()
method unnecessarily, as frequent calls to update the progress bar will freeze
the UI of your application.
It is important that note that in this method (which was invoked by the DoWork
event), you cannot directly access the Windows controls, as they are not thread-safe. Trying to do so will also trigger a runtime error, a useful feature new
in Visual Studio 2005.
Function SumNumbers( _
ByVal number As Double, _
ByVal worker As System.ComponentModel.BackgroundWorker, _
ByVal e As DoWorkEventArgs) As Double
Dim lastPercent As Integer = 0
Dim sum As Double = 0
For i As Double = 0 To number
'---check if user cancelled the process
If worker.CancellationPending = True Then
e.Cancel = True
Else
sum += i
If i Mod 10 = 0 Then
Dim percentDone As Integer = i / number * 100
'---update the progress bar if there is a change
If percentDone > lastPercent Then
worker.ReportProgress(percentDone)
lastPercent = percentDone
End If
End If
End If
Next
Return sum
End Function
The ProgressChanged event is invoked whenever the ReportProgress()
method is called. In this case, I used it to update my progress bar:
Private Sub backgroundWorker1_ProgressChanged( _
ByVal sender As Object, ByVal e As ProgressChangedEventArgs) _
Handles BackgroundWorker1.ProgressChanged
'---updates the progress bar
ProgressBar1.Value = e.ProgressPercentage
End Sub
The RunWorkerCompleted event is fired when the thread (SumNumbers(),
in this case) has completed running. Here you will print the result accordingly
and change the cursor back to the default:
Private Sub BackgroundWorker1_RunWorkerCompleted( _
ByVal sender As Object, _
ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) _
Handles BackgroundWorker1.RunWorkerCompleted
If Not (e.Error Is Nothing) Then
MsgBox(e.Error.Message)
ElseIf e.Cancelled Then
MsgBox("Cancelled")
Else
lblResult.Text = "Sum of 1 to " & _
txtNum.Text & " is " & e.Result
End If
btnStart.Enabled = True
btnCancel.Enabled = False
Me.Cursor = Cursors.Default
End Sub
Finally, when the user clicks the Cancel button, you cancel the
process by calling the CancelAsync() method:
Private Sub btnCancel_Click( _
ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles btnCancel.Click
' Cancel the asynchronous operation.
BackgroundWorker1.CancelAsync()
btnCancel.Enabled = False
End Sub
Testing the Application
To test the application, press F5 and enter a large number (say, 9999999)
and click the Start button. You should see the progress bar updating and the
cursor changed to an hourglass. When the process is completed, the result will
be printed in the Label control (see Figure 4).

Figure 4. Testing the application
Wei-Meng Lee (Microsoft MVP) http://weimenglee.blogspot.com is a technologist and founder of Developer Learning Solutions http://www.developerlearningsolutions.com, a technology company specializing in hands-on training on the latest Microsoft technologies.
Return to the OnDotNet.com
You must be logged in to the O'Reilly Network to post a talkback.
Showing messages 1 through 6 of 6.
-
Error On BackGroundControl
2008-10-15 02:30:17 BADARIY [Reply | View]
Hi,
Thanks for your info on BackGroundControl
i am getting the following error could you please help me how to do this
Regards,
Badari.y
-
Threads
2006-09-13 02:56:26 Adelio [Reply | View]
What I am trying to code is an "activity" form.
i.e. like a progress bar BUT where it just moves left to right (and back again) a bit like KIT in Nightrider. I want to use this when I am calling stored procedures in my database in a seperate thread (no idea how long it will take to run and no chance of monitoring it's progress, it is either still running or finished).
I would run the SQL in a seperate thread and then display the activity form and it would just do it's thing, letting the user know that something is happening and then when the SQL has finished I tell the activity form to close.
What's the best way of doing it.
-
Complex example?
2005-07-25 21:48:17 edice [Reply | View]
Is it too much to ask for a decent example? In this case, the user is still doing just 1 task, with the addition of a cancel and progress button.
In reality, if calculation code reports progress and is cancellable, its probably easily performed synchronously, with a cancel-check at each progress update. When cancelled, the main thread would still have to join the threaded calculation's thread (or at least wait for it to cancel), so there is almost zero benefit to threading this example.
You've taken a trivial example that would work perfectly as a single-threaded task and popped it in a thread... I'd like to see an example that would be difficult to do in a single threaded world. Even the web responses isn't a great example, because theres always the ol' select() pattern that was once commonplace.
Can you post an example with some meat? Eg, doing two backgrounded tasks that work together would be a good start.
Even two independent backgrounded tasks can be difficult in a single-threaded context as it can be tricky to return control to the first task.
Eg faking backgrounding by calling QT's qApp->processEvents() regularly - this doesn't return control to the first task until the second task is done. For that to work, you have to break the computation into callable chunks and schedule the chunks with postEvent or a QTimer.
If that sort of thing was discussed, it might help educate those who don't fully understand why multithreading is so powerful.
Thanks
-
Complex example?
2005-12-02 09:04:31 redturtle [Reply | View]
you are wrong... even this simple example CANNOT be done synchronously! It's a perfect example, that shows even a simple task (that take lots of processing time) should be done in a background thread.
IF you tried your method then the User Interface would be totally stuck between progress bar updates. Plus there would be no way to cancel the operation! The thread would be stuck in your "for loop" and therefore the user interface/cancel button will not work! until you are out of the "calculation loop". Sure you can check if "cancel" has been hit between iterations, but guess what? your program will not have any time to actually notice mouse clicks.. its stuck in your "summing loop".
It's a simple task, but its not how simple the task is, its how much cpu time it takes up. If I tell you to count to 1 trillion, and i also tell you, "you cannot do anything else in life, until you are done counting to 1 trillion" then you will quickly die of starvation.. you cannot eat, sleep, walk, work, talk until you are done counting to 1 trillion... same for this app, if you use single thread, you cannot process mouse clicks until you are done summing up the numbers.
If you do not trust me, try it out... try this app, all within the UI thread. You'll notice quickly, you cannot cancel, you'll be starving the UI.






Specified cast is not valid.