I'll probably start every next post with this...
In my own words:
The
BackgroundWorker component is an easy way of using threading. It gives you some events so that you can easily control your execution flow (progress, cancellation, etc). And, as who knows a little bit of
threading already knows, it manages to keep your UI responsive while performing lengthy and time consuming operations on the
background. It is somewhat of a wrapper, so you can use threading and report the progress withouth even putting much thoughts on it...
In MSDN's words:
The BackgroundWorker class allows you to run an operation on a separate, dedicated thread. Time-consuming operations like downloads and database transactions can cause your user interface (UI) to seem as though it has stopped responding while they are running. When you want a responsive UI and you are faced with long delays associated with such operations, the BackgroundWorker class provides a convenient solution.
I'll use a simple form with a progress bar, two buttons and a BackgroundWorker, so the form will look like this:
Working the Worker
For this example, it's important to have the Asynchronous properties set to true, like the image below:
The properties' names are self-explanatory.
As for
events, we have three of them:
DoWork:
This is the main execution entry point. This is where you should put your asynchronous code, or your asynchronous entry method call.
ProgressChanged:
This event does the progress reporting. If you want to report your progress on a progress bar, for example, this is where you should set its progress value.
RunWorkerCompleted:
Self explanatory. Execution falls here right after work is completed, so that you can do any clean up, display any messages, etc.
Stating that, generate the event handlers:
Properties:
Code:
To start running our example, we'll call method
RunWorkerAsync inside Button Start's Click Event Handler:
private void btnStart_Click(object sender, EventArgs e)
{
if (!backWorker.IsBusy)
{
backWorker.RunWorkerAsync();
}
}
If you don't plan on disabling your start button or preventing the user from starting the operation again, you should put the check above. Property
IsBusy returns us a boolean indicating if the BackgroundWorker is currently performing any background operation. If it is, and we call
RunWorkerAsync again, it'll throw us an Exception of type
InvalidOperationException with message "This BackgroundWorker is currently busy and cannot run multiple tasks concurrently.".
Calling method
RunWorkerAsync will execute whatever's inside the
DoWork Event Handler, asynchronously. At this point, execution will already be in another thread.
So let's put some dummy loop inside it just so it keeps looping for a while:
private void backWorker_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 1; i <= 100; i++)
{
System.Threading.Thread.Sleep(500);
}
}
Now assume that you want to report execution progress on that. The BackgroundWorker component gives us the method
ReportProgress. We'll use the simple overload, which takes an integer value from 0 to 100 (0% to 100%), and raises the
ProgressChanged event. Our code here will look like this:
private void backWorker_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 1; i <= 100; i++)
{
System.Threading.Thread.Sleep(500);
backWorker.ReportProgress(i); // Don't forget that i represents a percentage from 0 to 100.
}
}
And inside the Event Handler
ProgressChanged, we'll set the progress bar's value to the value that we passed as a parameter when we called the method
ReportProgress:
private void backWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar.Value = e.ProgressPercentage; // Percentage value comes on ProgressChangedEventArgs
}
If we run our project here, we'll see the progress of execution on the progress bar. But what if we want to cancel it?BackgroundWorker has a readonly property named
CancellationPending, and a method named
CancelAsync. As you could already figure out, when we want to cancel an asynchronous operation, we call method
CancelAsync and it'll set the
CancellationPending property to true. We can then check if it's true everytime we reach a point in our code where we can actually cancel the operation.Call
CancelAsync inside Button Cancel's Click Event Handler:
private void btnCancel_Click(object sender, EventArgs e)
{
backWorker.CancelAsync();
}
Check the property
CancellationPending on every iteration on our loop:
private void backWorker_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 1; i <= 100; i++)
{
if (backWorker.CancellationPending)
{
e.Cancel = true; // Here we're letting the Worker know that we're cancelling our operation
return;
}
else
{
System.Threading.Thread.Sleep(500);
backWorker.ReportProgress(i);
}
}
}
Notice that we set a property on
DoEventArgs to true. We do that to indicate that the operation has been canceled when execution reaches our next event handler,
RunWorkerCompleted. As we call
return, execution will leave
DoWork and fall into
RunWorkerCompleted, where we can display any messages, or do some cleanup:
private void backWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled) // Remember that we've indicated cancellation on DoWork?
MessageBox.Show("User cancelled execution");
else
MessageBox.Show("Execution completed successfully");
}
So that's basically it. This is a simple way of using BackgroundWorker, just to show you how powerful it is. It literally simplifies using threading. Hope you like it.