I see forum members often getting confused and asking about how the background worker should be correctly coded.
Without going into much of the theory, I will demonstrate it with the help of an example. Pay attention the the comments in the code for explanation about each part of the code.
For this demo, put a BackgroundWorker, two Labels and two Buttons on your form. The default names (BackgroundWorker1, Label1, Label2, Button1, Button2) apply. Otherwise change appropriately in the code, or the names of the Controls.
Then put the following code:
vb.net Code:
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load Button1.Text = "Start" Button2.Text = "Cancel" Label1.Text = "" Label2.Text = "" End Sub Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click Button1.Enabled = False Button2.Enabled = True '' these properties should be set to True (at design-time or runtime) before calling the RunWorkerAsync '' to ensure that it supports Cancellation and reporting Progress BackgroundWorker1.WorkerSupportsCancellation = True BackgroundWorker1.WorkerReportsProgress = True '' call this method to start your asynchronous Task. BackgroundWorker1.RunWorkerAsync() End Sub Private Sub Button2_Click(sender As System.Object, e As System.EventArgs) Handles Button2.Click '' to cancel the task, just call the BackgroundWorker1.CancelAsync method. Button2.Enabled = False BackgroundWorker1.CancelAsync() End Sub Private Sub BackgroundWorker1_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork '' The asynchronous task you want to perform goes here '' the following is an example of how it typically goes. Const Max As Integer = 1000 For i = 1 To Max '' do something '' (I put a sleep to simulate time consumed) Threading.Thread.Sleep(100) '' report progress at regular intervals BackgroundWorker1.ReportProgress(CInt(100 * i / Max), "Running..." & i.ToString) '' check at regular intervals for CancellationPending If BackgroundWorker1.CancellationPending Then BackgroundWorker1.ReportProgress(CInt(100 * i / Max), "Cancelling...") Exit For End If Next '' any cleanup code go here '' ensure that you close all open resources before exitting out of this Method. '' try to skip off whatever is not desperately necessary if CancellationPending is True '' set the e.Cancel to True to indicate to the RunWorkerCompleted that you cancelled out If BackgroundWorker1.CancellationPending Then e.Cancel = True BackgroundWorker1.ReportProgress(100, "Cancelled.") End If End Sub Private Sub BackgroundWorker1_ProgressChanged(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged '' This event is fired when you call the ReportProgress method from inside your DoWork. '' Any visual indicators about the progress should go here. Label1.Text = CType(e.UserState, String) Label2.Text = e.ProgressPercentage.ToString & "% complete." End Sub Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted '' This event is fired when your BackgroundWorker exits. '' It may have exitted Normally after completing its task, '' or because of Cancellation, or due to any Error. If e.Error IsNot Nothing Then '' if BackgroundWorker terminated due to error MessageBox.Show(e.Error.Message) Label1.Text = "Error occurred!" ElseIf e.Cancelled Then '' otherwise if it was cancelled MessageBox.Show("Task cancelled!") Label1.Text = "Task Cancelled!" Else '' otherwise it completed normally MessageBox.Show("Task completed!") Label1.Text = "Error completed!" End If Button1.Enabled = True Button2.Enabled = False End Sub
Run the code and see that the Start and Cancel works smoothly.
Pay attention to the comments, to adapt this stub as per your actual needs.
[EDIT: updated with more information]
(copied here from: http://www.vbforums.com/showpost.php...32&postcount=8)
Passing more information than just a String value via ProgressChanged event
Actually you should never try to access any Control inside the DoWork.
There are just two ways you would need them inside the DoWork event.
- You want to pick up some value from some control.
- You want to update some control's value.
To pick some value from any control, put such values in variables, so that you don't need to reference the controls from inside the DoWork event. Do that before you call the RunWorkerAsync method (which in turn fires the DoWork event).
If you want to set something in any control, you should use the ReportProgress method to pass your values out of BackgroundWorker. The values you pass from ReportProgress can be accessed from the ProgressChanged event. You can assign those values to your controls or do anything else you want to do with them.
So you do:
This was just a simple example where a string is passed.Code:' Inside DoWork event
worker.ReportProgress(10, "Run coding 1")
' In ProgressChanged event
Me.Label1.Text = CType(e.UserState, String)
But in many cases you may want to pass more information than this to the ProgressChanged event.
e.g. you have 10 labels, and you also want to pass which label's text property to update, along with the string value you want to set.
Fortunately, the ReportProgress can pass a value of type Object to the ProgressChanged event. This means that you can pass virtually anything via that method. It can be as small as a string or number, or it can be a complex structure like some class object or array etc.
Below is an example where I pass the control name and its value to set.
1. Create a structure/class which can hold the control name (name of label) and the text value to set.
2. From inside the DoWork event, you pass an object instance of this structure.Code:Public Structure ControlWithText
Public ControlName As Control
Public Text As String
Public Sub New(ByVal ctrl As Control, ByVal text As String)
Me.ControlName = ctrl
Me.Text = text
End Sub
End Structure
e.g.
3. Now in your ProgressChanged event, you will have this object available. Just get the control name and the text out of it and do your task appropriately.Code:worker.ReportProgress(10, New ControlWithText(Label1, "Run coding 1"))
worker.ReportProgress(20, New ControlWithText(Label2, "Run coding 2"))
Code:If TypeOf e.UserState Is ControlWithText Then
Dim cwt As ControlWithText = CType(e.UserState, ControlWithText)
cwt.ControlName.Text = cwt.Text
End If