BackgroundWorker Basics in C#

Post date: Mar 17, 2011 3:16:32 AM

This will be my first tutorial so I hope I can make myself clear. Also English isn't my first language so I apologize in advance for the inevitable language errors.

What will this tutorial cover?

Making the BackgroundWorker do useful work for you, how to get the results back(I'm using List<int> as returndata, passing parameters to the BackgroundWorker, keeping track of progress and cancel the background process while it's running. Finally I'll show a way of collecting the results from the BackgroundWorker “on the fly” instead of waiting for all the calculations to be done.

Why use the BackgroundWorker?

You can use it to shift some heavy calculations, for example database access or file searching, to another thread and make the user interface more responsive. If both UI and the heavy calculations are ran within the same thread the UI appears to hang, making the average pc user think the program has crashed. So let's do it the decent way and use BackgroundWorker!

How to use the BackgroundWorker?

The BackgroundWorker may sound quite intimidating, but actually it's very easy and intuitive to do use once you've done the first time. So let's get going and make a simple application showing you the usage of the BackgroundWorker. In case you want to look up the BackgroundWorker class on MSDN, it's in the System.Component-model namespace.

The first thing you need to do is to add the BackgroundWorker to the application and the easiest way to do so is to drag it from the toolbox onto your form. It's under the components tab. You'll see the BackgroundWorker showing up as BackgroundWorker1 in the gray box under your form.

The BackgroundWorker is event-driven, so what you need to do to get the basics working is this:

Ok, lets get coding! Double click on the DoWork event and add:

01

02

03

04

05

06

07

08

09

10

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)

        {

            List<int> temp = new List<int>();//our simulated return data

            for (int i=0; i<= 10; i++)

            {

                Thread.Sleep(300);//used to simulate lengthy operations,

                temp.Add(i);//in this case 10*300ms=3s(add using System.Threading)

            }

            e.Result = temp;//return temp

        }

And now for the RunWorkerCompleted event:

1

2

3

4

5

private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)

        {

            data.AddRange((List<int>)e.Result);//copies return value to public list we declared before

            MessageBox.Show("Done");

        }

Finally make a button, change the text to Start, and add the following to the Button.Click event:

1

2

3

4

private void  btnStart_Click(object sender, EventArgs e)

        {

            backgroundWorker1.RunWorkerAsync();//this invokes the DoWork event

        }

How do I pass an argument to the BackgroundWorker?

That's very easy. Let's try and pass the time the thread sleeps as an argument. First change the way we invoke the DoWork event to:

private void btnStart_Click(object sender, EventArgs e)

2

3

4

        {

            backgroundWorker1.RunWorkerAsync(300);//300 gives a total of 3 seconds pause

        }

And use the argument in our BackgroundWorker by changing the code to:

01

02

03

04

05

06

07

08

09

10

11

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)

        {

            int time = (int)e.Argument;

            List<int> temp = new List<int>();//our return data

            for (int i=0; i<= 10; i++)

            {

                Thread.Sleep(time);//used to simulate lengthy operations,

                temp.Add(i);//in this case 10*300ms=3s(add using System.Threading)

            }

            e.Result = temp;//return temp

        }

How do I cancel a BackgroundWorker operation halfway?

First set the WorkerSupportsCancellation property to true in the property window. Now add a button with text “Cancel”, double click on it and add:

1

2

3

4

private void btnCancel_Click(object sender, EventArgs e)

        {

            backgroundWorker1.CancelAsync();//makes the backgroundworker stop

        }

Now we need to make the BackgroundWorker check if it has gotten the order to stop, and then break out of the loop. To do so, change the code to:

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)

        {

            int time = (int)e.Argument;

            List<int> temp = new List<int>();//our return data

            for (int i=0; i<= 10; i++)

            {

                if (backgroundWorker1.CancellationPending)//checks for cancel request

                {

                    e.Cancel = true;

                    break;

                }

                Thread.Sleep(time);//used to simulate lengthy operations,

                temp.Add(i);//in this case 10*300ms=3s(add using System.Threading)

            }

            e.Result = temp;//return temp

        }

Now if we press cancel, we get into trouble, so we also need to change RunWorkerCompleted to check for cancellations:

01

02

03

04

05

06

07

08

09

10

11

12

13

private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)

        {

  if (e.Cancelled)//it doesn't matter if the BG worker ends normally, or gets                          //cancelled,

            {

                //both cases RunWorkerCompleted is invoked, so we need to check what                    //has  happened

                MessageBox.Show("You've cancelled the backgroundworker!");

            }

            else

            {

                data.AddRange((List<int>)e.Result);//copies return value to public list                                                     we declared before

                MessageBox.Show("Done");

            }

        }

How do I keep track of progress?

First set the WorkerReportsProgress property to true. Then change the DoWork method to:

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)

        {

            int time = (int)e.Argument;

            List<int> temp = new List<int>();//our return data

            for (int i=0; i<= 10; i++)

            {

                if (backgroundWorker1.CancellationPending)//checks for cancel request

                {

                    e.Cancel = true;

                    break;

                }

                backgroundWorker1.ReportProgress(i * 10);//reports a percentage between                                                           //0 and 100

                Thread.Sleep(time);//used to simulate lengthy operations,

                temp.Add(i);//in this case 10*300ms=3s(add using System.Threading)

            }

            e.Result = temp;//return temp

        }

Now create a new ProgressBar, and then double click on the BackgroundWorker's ProgressChanged event and add:

1

2

3

4

private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)

        {

            progressBar1.Value = e.ProgressPercentage;

        }

Is there any other use for WorkerReportsProgress?

Yes there is. Let's say that instead of calculating all data, and then passing it on like we did, you want to add data “on the go”. For this we can use the Userstate parameter, which we can use to return any object, and then in the ProgressChanged event handler, we can cast this into the appropriate type and do with it whatever you need. As an example we'll use the values of i we looped through and add them to a second list. To keep it short, change:

backgroundWorker1.ReportProgress(i*10);

into:

1backgroundWorker1.ReportProgress(i * 10,i);//reports ProgressPercentage AND Userstate

Now let's collect the data in a second List<int>:

private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)

2

3

4

5

6

        {

            progressBar1.Value = e.ProgressPercentage;

            data_new.Add((int)e.UserState);//casts the userstate into integer and adds                                             //it to a List

        }

This finishes this tutorial, I hope it was informative. Feedback would be appreciated!