you call the RunWorkerAsync()
method (as was done in the previous step).
The DoWork event handler runs on a separate thread from the UI. Be sure not to manipulate any Windows Forms controls created on the UI thread from this method.
The SumNumbers()
function basically sums up all the numbers from 0 to the number specified:
private double SumNumbers(
double number, BackgroundWorker worker, DoWorkEventArgs e) {
int lastPercent = 0;
double sum = 0;
for (double i = 0; i <= number; i++) {
//---check if user cancelled the process---
if (worker.CancellationPending) {
e.Cancel = true;
} else {
sum += i;
if (i % 10 == 0) {
int percentDone = (int)((i / number) * 100);
//---update the progress bar if there is a change---
if (percentDone > lastPercent) {
worker.ReportProgress(percentDone);
lastPercent = percentDone;
}
}
}
}
return sum;
}
It takes in three arguments — the number to sum up to, the BackgroundWorker
component, and the DoWorkEventArgs
. Within the For
loop, you check to see if the user has clicked the Cancel button (this event is defined a little later in this chapter) by checking the value of the CancellationPending
property. If the user has canceled the process, set e.Cancel
to True
. After every 10 iterations, you calculate the progress completed so far. If there is progress (when the current progress percentage is greater than the last one recorded), you update the progress bar by calling the ReportProgress()
method of the BackgroundWorker
component. Do not call the ReportProgress()
method unnecessarily because frequent calls to update the progress bar will freeze the UI of your application.
It is important to note that in this method (which was invoked by the DoWork
event), you cannot directly access Windows controls because they are not thread-safe. Trying to do so will trigger a runtime error, a useful feature in Visual Studio 2008.
The ProgressChanged
event is invoked whenever the ReportProgress()
method is called. In this case, you use it to update the progress bar. To generate the event handler for the ProgressChanged
event, switch to design view and look at the properties of the BackgroundWorker
component. In the Properties window, select the Events icon and double-click the ProgressChanged
event (see Figure 10-15).

Figure 10-15
Code the event handler for the ProgressChanged
event as follows:
private void backgroundWorker1_ProgressChanged(
object sender, ProgressChangedEventArgs e) {
//---updates the progress bar and label control---
progressBar1.Value = e.ProgressPercentage;
lblResult.Text = e.ProgressPercentage.ToString() + '%';
}
Now double-click the RunWorkerCompleted
event to generate its event handler:
private void backgroundWorker1_RunWorkerCompleted(
object sender, RunWorkerCompletedEventArgs e) {
if (e.Error != null) MessageBox.Show(e.Error.Message);
else if (e.Cancelled) MessageBox.Show('Cancelled');
else {
lblResult.Text = 'Sum of 1 to ' + txtNum.Text + ' is ' + e.Result;
}
btnStart.Enabled = true;
btnCancel.Enabled = false;
}
The RunWorkerCompleted
event is fired when the thread (SumNumbers()
, in this case) has completed running. Here you print the result accordingly.
Finally, when the user clicks the Cancel button, you cancel the process by calling the CancelAsync ()
method:
private void btnCancel_Click(object sender, EventArgs e) {
//---Cancel the asynchronous operation---
backgroundWorker1.CancelAsync();
btnCancel.Enabled = false;
}
Testing the Application
To test the application, press F5, enter a large number (say, 9999999), and click the Start button. The progress bar updating should begin updating. When the process is complete, the result is printed in the Label
control (see Figure 10-16).

Figure 10-16
Summary
This chapter explains the rationale for threading and how it can improve the responsiveness of your applications. Threading is a complex topic and you need to plan carefully before using threads in your application. For instance, you must identify the critical regions so that you can ensure that the different threads accessing the critical region are synchronized. Finally, you saw that Windows Forms controls are not thread-safe and that you need to use delegates when updating UI controls from another thread.