Using the BackgroundWorker Component in C#

Threads and Delegates, Windows Forms

A few days ago I wrote an article describing how to create a worker thread for your Windows Forms. Just recently a friend of mine brought to my attention that the .NET Framework contains a component called BackgroundWorker which helps you to do what I had explained in that article but in an easier and quicker way. So I decided to read up on the BackgroundWorker component and now I am going to show you how to use it.

The BackgroundWorker component can be added to your form from the toolbox within Visual Studio. Once you add the component create an event handler for each of its three events – DoWork, ProgressChanged, and RunWorkerCompleted. After doing this you should end up with the below code.

private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
}

private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
}

private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
}

Now just as we did in the Create a Worker Thread for your Windows Form example, let’s add a textbox which displays the progress of a heavy operation, and two buttons, one to start the operation and one to stop it.

Next we’re going to add the code of our heavy operation in the DoWork event handler of our BackgroundWorker.

private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
    // Example heavy operation
    for (int i = 0; i <= 999999; i++)
    {
        // Sleep for 10ms to simulate work
        System.Threading.Thread.Sleep(10);

        // Report the progress now
        this.backgroundWorker.ReportProgress(i);

        // Cancel process if it was flagged to be stopped.
        if (this.backgroundWorker.CancellationPending)
        {
            e.Cancel = true;
            return;
        }
    }
}

As you can see we are iterating for a million times and sleeping for 10 milliseconds on each iteration. Then we are telling the backgroundWorker component to report its progress to the UI. When we call the ReportProgress method the ProgressChanged event is fired. We will be looking at that event handler soon. Finally we are checking the CancellationPending property to see if the backgroundWorker was flagged to stop. If it was we are stopping the process by calling e.Cancel = true;.

The backgroundWorker_ProgressChanged event handler runs on the UI thread so it saves us the hassle of creating a delegate to access the UI. This is good news because it keeps your code simpler and easier to maintain since there is less code overall. The event handler looks like this:

private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    // Update UI
    this.txtProgress.Text += "  *";
}

Once the backgroundWorker completes the heavy operation, or is stopped by an error or a manual cancellation, the RunWorkerCompleted event will fire. Here we can detect why the backgroundWorker stopped as can be seen below:

private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    if (e.Error != null)
    {
        // An error occurred
        this.txtProgress.Text += Environment.NewLine + "An error occurred: " + e.Error.Message;
    }
    else if (e.Cancelled)
    {
        // The process was cancelled
        this.txtProgress.Text += Environment.NewLine + "Job cancelled.";
    }
    else
    {
        // The process finished
        this.txtProgress.Text += Environment.NewLine + "Job finished.";
    }
}

We are almost done. What's missing is a way to start and stop the heavy operation. Subscribe to the Click event for the start and stop buttons we added in the beginning and call RunWorkerAsync() or CancelAsync() accordingly as shown below:

private void btnStart_Click(object sender, EventArgs e)
{
    // Start the process on a seperate thread
    this.backgroundWorker.RunWorkerAsync();
}

private void btnStop_Click(object sender, EventArgs e)
{
    // Flag the process to be stopped.
    // This will not stop the process. 
    // It will only set the backgroundWorker.CancellationPending property.
    this.backgroundWorker.CancelAsync();
}

Finally we must tell the backgroundWorker to expect cancellation requests and to report progress. You can do this in your Form_Load event handler as shown below:

private void Form1_Load(object sender, EventArgs e)
{
    // Tell backgroundWorker to support cancellations
    this.backgroundWorker.WorkerSupportsCancellation = true;

    // Tell backgroundWorker to report progress
    this.backgroundWorker.WorkerReportsProgress = true;
}

And that's it. We have created a worker thread without actually creating one since it is done internally by the BackgroundWorker component. As you can see this method is by far easier to use and unless you need absolute full control over your thread I recommend you use this method instead of manually creating a worker thread yourself.

I hope you enjoyed this article. See you soon.

Dave

DaveOnCSharp.com runs on the Thesis Theme


How smart is your Theme? How good is your support? Check out ThesisTheme for WordPress.

The Thesis Theme is one of the best Wordpress frameworks out there, especially if you understand software development, which since you're here you probably do. Thesis allows you to design post and page templates using a drag-and-drop system which gives you access to all the relevant page elements you would normally have to manually access through code. This is why understanding development techniques definitely helps, even though you don't need to write any code to get Thesis up and running.

So go check out the incredible features at DIYThemes and start using Thesis now!

3 comments… add one
  • yaniv

    hello dave i have problem wite delegate i thing private delegate void UpdateStatusDelegate();
    i hope you can help me

  • Carlos Arias

    Awesome!
    but … how to change diferent controls?

    // Update UI
        this.txtProgress.Text += "  *";
        this.txtProgress_other.Text =other_value;
    

    ???

  • sam

    /* Not a completed, but till thanks a lot to David.*/
    /* I think “how to create a worker thread for your Windows Forms.” is actually much greater and useful article than this one. */

    this.backgroundWorker1.DoWork += backgroundWorker1_DoWork;
    this.backgroundWorker1.ProgressChanged += backgroundWorker1_ProgressChanged;
    this.backgroundWorker1.RunWorkerCompleted += backgroundWorker1_RunWorkerCompleted;

    /*Like delegates, you needs to add above method before RunWorkerAsync()*/

    this.backgroundWorker1.RunWorkerAsync();

Leave a Comment