# VBForums CodeBank > CodeBank - Visual Basic .NET >  Correct way to use the BackgroundWorker

## Pradeep1210

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 IfEnd 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 = FalseEnd 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:


```
' Inside DoWork event
worker.ReportProgress(10, "Run coding 1")

' In ProgressChanged event
Me.Label1.Text = CType(e.UserState, String)
```

This was just a simple example where a string is passed.

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.


```
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
```

2. From inside the DoWork event, you pass an object instance of this structure.
e.g.


```
worker.ReportProgress(10, New ControlWithText(Label1, "Run coding 1"))

worker.ReportProgress(20, New ControlWithText(Label2, "Run coding 2"))
```

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.


```
If TypeOf e.UserState Is ControlWithText Then
    Dim cwt As ControlWithText = CType(e.UserState, ControlWithText)
    cwt.ControlName.Text = cwt.Text
End If
```

----------


## TheBarret

Great tutorial, thank you!

I see that i could use far more out of this way then i used to know it!

----------


## Pradeep1210

That's Great  :Thumb:   :Smilie:

----------


## Garrann

Thanks for the great tutorial Pradeep1210!

----------


## freedomkw

Perfect & Error-Less :-) 

Good job!

----------


## nbrege

Pradeep ... can you show an example of how to fill a DataGridView with the results of a query?

----------


## MattP

> Pradeep ... can you show an example of how to fill a DataGridView with the results of a query?


You would simple fill the DataTable in the DoWork event.  

Once the DataTable is filled you'd pass it to the UserState parameter of ReportProgress.  (The 2nd parameter)

In the ProgressChanged event cast e.UserState to a DataTable and set it to the DataGridView's DataSource.

----------


## peymanebrahimi

Hi,
Thanks for great info.
What about showing a message in another form, such as a none BorderStyleForm with a pic on it saying "please wait" or "loading" during the progress and closing the form after completion?

----------


## Pradeep1210

I was away from this forums for some time and couldn't reply back to the answers.
Not sure if you already have your answer or not, but here's how we will do it:

1. Create your form that shows the progress.  You can design it whatever way you like. Say the name of that form is "ProgressForm".

vb.net Code:
Public Class ProgressForm
...
...
...
End Class

2. Create a public property/method in the ProgressForm that sets the text in the label (or whatever control you want to show the progress in):

vb.net Code:
Public Property ProgressText() As String
Get
    Return Label1.Text
End Get
Set(value As String)
    Label1.Text = value
End Set
End Property

3. Declare a variable of ProgressForm type at the top of your form (the main form on which your BackgroundWorker is):

vb.net Code:
Dim MyProgressForm as ProgressForm

4. Before calling the BackgroundWorker's RunWorkerAsync() method, Show that form:

vb.net Code:
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
...
...
...
    '' call this method to start your asynchronous Task.
    MyProgressForm.Show(Me)
    BackgroundWorker1.RunWorkerAsync()
End Sub
5. Now in the BackgroundWorker's ProgressChanged event, set the property of your ProgressForm that shows appropriate message:

vb.net Code:
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.
     MyProgressForm.ProgressText = CType(e.UserState, String)
 End Sub

6. After finishing your work, close that form in the RunWorkerCompleted event:

vb.net Code:
Private Sub BackgroundWorker1_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
...
...
...
    MyProgressForm.Close()
End Sub

That's all that is required to be done.

*NOTE:* All the above code is free-hand typed and may contain typos. It is only meant to explain the concept.

----------


## peymanebrahimi

Pradeep1210 thanks for your reply,
Now on I use this approach
I have created a form with a picturebox in it and a gif file which shows "_loading_", named it as 'ProgressForm'
In another class which BGW is defined and one of its method supposed to do time consuming jobs, the following are declared:


```
'at class level
     Private frmProgress As New ProgressForm

'at start Button method body
    BGW.RunWorkerAsync() 

In BGW_DoWork :
    worker.ReportProgress(100, New ProgressForm)  
    'Time consuming job goes here

'In BGW_ProgressChanged : 
    If TypeOf e.UserState Is ProgressForm Then
        frmProgress.Show()
    End If

'In BGW_RunWorkerCompleted : 
    frmProgress.Close()
```


I know this approach is like yours, but I want to know whether there is a way to write it better or in a professional way.
After all, thanks a lot for your time and explanation.

----------


## Pradeep1210

The way you are doing it is completely different than what I showed you...

It seems like you want to create a new instance of your ProgressForm from inside the worker thread, and use that in the ProgressChanged event. I won't recommend this approach. UI elements like any controls or forms must be created on the UI thread only, otherwise they will usually cause cross-thread operation problem. You don't get any errors at present because you never use that form instance. It is as good as passing a boolean value. 
Besides this, everytime you report the progress, a new instance of your ProgressForm will be created. And you never use that form instance! So you just keep on filling your memory space unnecessarily. It will ultimately get garbage-collected. But that will also consume your precious CPU cycles. This is not the correct approach anyways.

The other problem is, you only show the form. You never give any UI updates. Calling the *frmProgress.Show()* method repeatedly will have no effect. It is same as calling it once.
This may be according to your requirements, but I would have preferred updating a Label or ProgressBar on the ProgressForm. This way the user at least knows what percentage of the work is done and how much more time he should expect to wait before it is fully completed.

If you examine the approach I showed you, the following things differ which you should consider:
1. Only one instance of the ProgressForm is created for the lifecycle.
2. The ProgressForm gives information about how much percentage of the task is done. (I just showed you how to update a Label, but you can use other controls like ProgressBar etc.)
3. I don't do the "New ProgressForm" with declaration. I do it just before I start the BackgroundWorker task. This way I don't need to keep an instance of that ProgressForm in memory until I actually begin doing the time consuming task, and that form really needs to be shown.

----------


## peymanebrahimi

Pradeep1210 thank you.
Details of making new instance each time reporting progress and GC were really helpful.
I have used progressbar to show the amount of progress in another scenario and in this one I want to show only the 'Please wait ...' or 'loading ...' which is sufficient enough for now.
I changed the code as below:



```
'at class level
     Private frmProgress As ProgressForm

'at start Button method body
    frmProgress .show()
    BGW.RunWorkerAsync() 

In BGW_DoWork :
    worker.ReportProgress(100)  
    'Time consuming job goes here

'In BGW_ProgressChanged : 
    

'In BGW_RunWorkerCompleted : 
    frmProgress.Close()
```



But, sorry for this silly question, you specified (Me) on show method of the form. Does it refer to the same form as buuton1 is placed in?
I took a look at msdn Form show Method (http://msdn.microsoft.com/en-us/library/szcefbbd.aspx) It says 'owner As IWin32Window' as the argument of the method.
The actual question: How can I be sure to close the 'Please Wait ...' form on BGW completed event. AndAlso what is the best argument for report progress which does not have any idea about stage of the work, just 'Please wait ...".
Thanks again very much for your time and nice detailed explanation.

----------


## Pradeep1210

Yes for the scenerio you mentioned, just showing the form before callingn the _RunWorkerAsync_ method and closing it in the _RunWorkerCompleted_ method is sufficient. You don't even need to report the progress (_worker.ReportProgress_ method), since you will not be making use of it.

*Form.Show(Me)*
Passing "Me" as owner of your progress form will ensure that it will always remain on the top of your form, even if it is not focused. Give it a try and you would be able to see the effect yourself.

----------


## peymanebrahimi

Thank you man.
It was fast and helpful.

----------


## virosoft

Hi !
I've been trying to wrap my head around all the information reading up on BackgroundWorker to relate to my application. I don't need to "check up" on the progress of the Thread's progress so am only using DoWork, but I'd like to update the main form's GUI. The code below even though executing line by line in debugging, will not actually add to the listbox or update the label. Why won't this work?

Thanks for the help !!


vb.net Code:
Public Class Main    Public Sub log(strMsg As String)        If Me.InvokeRequired = True Then            Me.Invoke(New Threading.WaitCallback(AddressOf log), strMsg)        Else            Dim origDate As DateTime = DateTime.Now            lstLog.Items.Add(origDate.ToString("[hh:mm:ss]") & " " & strMsg)            lblCurrentTask.Text = strMsg        End If    End SubEnd Class Class AnotherClass    Dim bw As BackgroundWorker = New BackgroundWorker    Sub New()        bw.WorkerSupportsCancellation = True        AddHandler bw.DoWork, AddressOf bw_DoWork    End Sub    Private Sub bw_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs)        Main.log("Message sent to main GUI")    End Sub    Public Sub startScript()        If Not bw.IsBusy = True Then            bw.RunWorkerAsync()        End If    End Sub    Public Sub stopScript()        If bw.WorkerSupportsCancellation = True Then            bw.CancelAsync()        End If    End SubEnd Class

----------


## Pradeep1210

Just as I mentioned in my original post, you should not call the Form or controls on the form directly from inside the DoWork event. Instead, report progress and update any GUI elements there.

The following should work:


vb.net Code:
Class AnotherClass    Dim bw As BackgroundWorker = New BackgroundWorker     Sub New()        bw.WorkerSupportsCancellation = True        bw.WorkerReportsProgress = True        AddHandler bw.DoWork, AddressOf bw_DoWork        AddHandler bw.ProgressChanged, AddressOf bw_ProgressChanged    End Sub     Private Sub bw_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs)        'Main.log("Message sent to main GUI")        bw.ReportProgress(0)     '-- report some progress so that bw_ProgressChanged event is raised    End Sub     Private Sub bw_ProgressChanged(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs)        Main.log("Message sent to main GUI")    End Sub     Public Sub startScript()        If Not bw.IsBusy = True Then            bw.RunWorkerAsync()        End If    End Sub    Public Sub stopScript()        If bw.WorkerSupportsCancellation = True Then            bw.CancelAsync()        End If    End SubEnd Class

----------


## virosoft

Thanks Pradeep1210 ! 
I used a structure to be able to pass multiple arguments, this way I could use conditions to also manipulate other form elements.


vb.net Code:
Private Sub bw_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs)        ...        objArgs.eventType = "LogEntry" : objArgs.eventMsg = "Message Information" : bw.ReportProgress(0, objArgs)        ...    End Sub     Private Structure struct_EvokeEvent        Public eventType As String        Public eventMsg As String    End Structure    Private Sub bw_ProgressChanged(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs)        Dim objArgs As struct_EvokeEvent = e.UserState        Select Case objArgs.eventType            Case "LogEntry"                Main.log(objArgs.eventMsg)            Case "SetupConfig"                task.taskDesc = Main.gridEvents.Rows(0).Cells(0).Value                task.taskParam = Main.gridEvents.Rows(0).Cells(1).Value                toSelect(Main.gridEvents, 0)        End Select    End Sub

----------


## Ansl72

> I was away from this forums for some time and couldn't reply back to the answers.
> Not sure if you already have your answer or not, but here's how we will do it:
> 
> 1. Create your form that shows the progress.  You can design it whatever way you like. Say the name of that form is "ProgressForm".
> 
> vb.net Code:
> Public Class ProgressForm.........End Class
> 
> 2. Create a public property/method in the ProgressForm that sets the text in the label (or whatever control you want to show the progress in):
> ...


Hi @Pradeep1210 I tryed your code but I have this exception, please help me!!!

----------


## si_the_geek

You need to create an instance of an object before you can use it... one way is to add this line just before the .Show line:


```
MyProgressForm = New ProgressForm
```

----------


## Ansl72

thanks so much!  :Smilie: 
Yes, I saw it as soon as I wrote my post  :wave:

----------


## Grand

> You would simple fill the DataTable in the DoWork event.  
> 
> Once the DataTable is filled you'd pass it to the UserState parameter of ReportProgress.  (The 2nd parameter)
> 
> In the ProgressChanged event cast e.UserState to a DataTable and set it to the DataGridView's DataSource.


Can someone please help me with this? I have difficulty to "pass" the datatable to the "UserState parameter of ReportProgress".  How is that done?
Also "in the ProgressChanged event cast e.UserState to a DataTable"?

Thanks

----------


## si_the_geek

You pass the parameter like this:


```
   BackgroundWorker1.ReportProgress(0, theDataTable)
```

...and in the ProgressChanged event, cast it back like this:


```
        Dim theDataTable as DataTable = DirectCast(e.UserState, DataTable)
```

----------


## Grand

OMG, that easy and still so far for me. Thank you. Can you please tell me why that first parameter should be zero and not 100?

----------


## jmcilhinney

> Can you please tell me why that first parameter should be zero and not 100?


The first parameter should be whatever you want it to be.  If you are actually going to use the value to provide feedback to the user then it should accurately represent the current progress.  If you're not going to use it for anything in the ProgressChanged event handler then what's the point of using anything other than zero?

I'm not convinced that you're doing the right thing here though.  You are passing a DataTable and you seem to be indicating that the progress is at 100%, i.e. the background work is complete.  If you are trying to pass data back to the UI thread when the work is complete then you should not be calling ReportProgress and handling ProgressChanged.  Instead, you should be setting the e.Result property and letting the DoWork event handler complete.  You then handle the RunWorkerCompleted event and get the data back from the e.Result property there.  If this DataTable is the result of the backgroundwork then that is how it should be handled.  ReportProgress/ProgressChanged is just to keep the user updated along the way.  That might involve just the numerical progress or it might be a batch of data that you have processed so far but if you are only transferring data in a single block at the end, don't do it using ReportProgress/ProgressChanged.

----------


## Grand

> If you are trying to pass data back to the UI thread when the work is complete then you should not be calling ReportProgress and handling ProgressChanged. 
>  Instead, you should be setting the e.Result property and letting the DoWork event handler complete.  You then handle the RunWorkerCompleted event and get the data back from the e.Result property there.  If this DataTable is the result of the backgroundwork then that is how it should be handled.


Spot on. That is exactly what I wanted to do. Would you also please tell me how exactly I set e.result property? And how I bring back the table so I can bind it with the datagridview?
Thanks.

I tried this but the table is not returend here:



```
    Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted

        Dim MyDataTable As DataTable = DirectCast(e.Result, DataTable)
        DataGridView2.DataSource = MyDataTable
        SpinnerForm.Close() 'Close the form with spinner

    End Sub
```

----------


## si_the_geek

You would set the property like this:


```
e.result = TheDataTable
```

----------


## jmcilhinney

> Spot on. That is exactly what I wanted to do. Would you also please tell me how exactly I set e.result property? And how I bring back the table so I can bind it with the datagridview?
> Thanks.
> 
> I tried this but the table is not returend here:
> 
> 
> 
> ```
>     Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
> ...


That part is right but you didn't put the DataTable there in the first place to get it back again.  I told you exactly what to do:



> Instead, you should be setting the e.Result property and letting the DoWork event handler complete.


How can you be asking how to set a property?  You're already doing it in code you already have.  The code you just posted sets the DataSource property of a DataGridView.  Why should setting any other property be any different?

----------

