Walkthrough: Implementing a Form That Uses a Background Operation

If you have an operation that will take a long time to complete, and you do not want your user interface (UI) to stop responding or "hang," you can use the BackgroundWorker class to execute the operation on another thread.

This walkthrough illustrates how to use the BackgroundWorker class to perform time-consuming computations "in the background," while the user interface remains responsive. When you are through, you will have an application that computes Fibonacci numbers asynchronously. Even though computing a large Fibonacci number can take a noticeable amount of time, the main UI thread will not be interrupted by this delay, and the form will be responsive during the calculation.

Tasks illustrated in this walkthrough include:

  • Creating a Windows-based Application

  • Creating a BackgroundWorker in Your Form

  • Adding Asynchronous Event Handlers

  • Adding Progress Reporting and Support for Cancellation

For a complete listing of the code used in this example, see How to: Implement a Form That Uses a Background Operation.

Note Note

The dialog boxes and menu commands you see might differ from those described in Help depending on your active settings or edition. To change your settings, choose Import and Export Settings on the Tools menu. For more information, see Customizing Development Settings.

The first step is to create the project and to set up the form.

To create a form that uses a background operation

  1. Create a Windows-based application project called BackgroundWorkerExample. For details, see How to: Create a New Windows Forms Application Project.

  2. In Solution Explorer, right-click Form1 and select Rename from the shortcut menu. Change the file name to FibonacciCalculator. Click the Yes button when you are asked if you want to rename all references to the code element 'Form1'.

  3. Drag a NumericUpDown control from the Toolbox onto the form. Set the Minimum property to 1 and the Maximum property to 91.

  4. Add two Button controls to the form.

  5. Rename the first Button control startAsyncButton and set the Text property to Start Async. Rename the second Button control cancelAsyncButton, and set the Text property to Cancel Async. Set its Enabled property to false.

  6. Create an event handler for both of the Button controls' Click events. For details, see How to: Create Event Handlers Using the Designer.

  7. Drag a Label control from the Toolbox onto the form and rename it resultLabel.

  8. Drag a ProgressBar control from the Toolbox onto the form.

You can create the BackgroundWorker for your asynchronous operation using the Windows Forms Designer.

To create a BackgroundWorker with the Designer

  • From the Components tab of the Toolbox, drag a BackgroundWorker onto the form.

You are now ready to add event handlers for the BackgroundWorker component's asynchronous events. The time-consuming operation that will run in the background, which computes Fibonacci numbers, is called by one of these event handlers.

To implement asynchronous event handlers

  1. In the Properties window, with the BackgroundWorker component still selected, click the Events button. Double-click the DoWork and RunWorkerCompleted events to create event handlers. For more information about how to use event handlers, see How to: Create Event Handlers Using the Designer.

  2. Create a new method, called ComputeFibonacci, in your form. This method does the actual work, and it will run in the background. This code demonstrates the recursive implementation of the Fibonacci algorithm, which is notably inefficient, taking exponentially longer time to complete for larger numbers. It is used here for illustrative purposes, to show an operation that can introduce long delays in your application.

    // This is the method that does the actual work. For this 
    // example, it computes a Fibonacci number and 
    // reports progress as it does its work. 
    long ComputeFibonacci(int n, BackgroundWorker worker, DoWorkEventArgs e)
    {
        // The parameter n must be >= 0 and <= 91. 
        // Fib(n), with n > 91, overflows a long. 
        if ((n < 0) || (n > 91))
        {
            throw new ArgumentException(
                "value must be >= 0 and <= 91", "n");
        }
    
        long result = 0;
    
        // Abort the operation if the user has canceled. 
        // Note that a call to CancelAsync may have set  
        // CancellationPending to true just after the 
        // last invocation of this method exits, so this  
        // code will not have the opportunity to set the  
        // DoWorkEventArgs.Cancel flag to true. This means 
        // that RunWorkerCompletedEventArgs.Cancelled will 
        // not be set to true in your RunWorkerCompleted 
        // event handler. This is a race condition. 
    
        if (worker.CancellationPending)
        {   
            e.Cancel = true;
        }
        else
        {   
            if (n < 2)
            {   
                result = 1;
            }
            else
            {   
                result = ComputeFibonacci(n - 1, worker, e) + 
                         ComputeFibonacci(n - 2, worker, e);
            }
    
            // Report progress as a percentage of the total task. 
            int percentComplete = 
                (int)((float)n / (float)numberToCompute * 100);
            if (percentComplete > highestPercentageReached)
            {
                highestPercentageReached = percentComplete;
                worker.ReportProgress(percentComplete);
            }
        }
    
        return result;
    }
    
  3. In the DoWork event handler, add a call to the ComputeFibonacci method. Take the first parameter for ComputeFibonacci from the Argument property of the DoWorkEventArgs. The BackgroundWorker and DoWorkEventArgs parameters will be used later for progress reporting and cancellation support. Assign the return value from ComputeFibonacci to the Result property of the DoWorkEventArgs. This result will be available to the RunWorkerCompleted event handler.

    Note Note

    The DoWork event handler does not reference the backgroundWorker1 instance variable directly, as this would couple this event handler to a specific instance of BackgroundWorker. Instead, a reference to the BackgroundWorker that raised this event is recovered from the sender parameter. This is important when the form hosts more than one BackgroundWorker. It is also important not to manipulate any user-interface objects in your DoWork event handler. Instead, communicate to the user interface through the BackgroundWorker events.

    // This event handler is where the actual, 
    // potentially time-consuming work is done. 
    private void backgroundWorker1_DoWork(object sender, 
        DoWorkEventArgs e)
    {   
        // Get the BackgroundWorker that raised this event.
        BackgroundWorker worker = sender as BackgroundWorker;
    
        // Assign the result of the computation 
        // to the Result property of the DoWorkEventArgs 
        // object. This is will be available to the  
        // RunWorkerCompleted eventhandler.
        e.Result = ComputeFibonacci((int)e.Argument, worker, e);
    }
    
  4. In the startAsyncButton control's Click event handler, add the code that starts the asynchronous operation.

    private void startAsyncButton_Click(System.Object sender, 
        System.EventArgs e)
    {
        // Reset the text in the result label.
        resultLabel.Text = String.Empty;
    
        // Disable the UpDown control until  
        // the asynchronous operation is done. 
        this.numericUpDown1.Enabled = false;
    
        // Disable the Start button until  
        // the asynchronous operation is done. 
        this.startAsyncButton.Enabled = false;
    
        // Enable the Cancel button while  
        // the asynchronous operation runs. 
        this.cancelAsyncButton.Enabled = true;
    
        // Get the value from the UpDown control.
        numberToCompute = (int)numericUpDown1.Value;
    
        // Reset the variable for percentage tracking.
        highestPercentageReached = 0;
    
        // Start the asynchronous operation.
        backgroundWorker1.RunWorkerAsync(numberToCompute);
    }
    
  5. In the RunWorkerCompleted event handler, assign the result of the calculation to the resultLabel control.

    // This event handler deals with the results of the 
    // background operation. 
    private void backgroundWorker1_RunWorkerCompleted(
        object sender, RunWorkerCompletedEventArgs e)
    {
        // First, handle the case where an exception was thrown. 
        if (e.Error != null)
        {
            MessageBox.Show(e.Error.Message);
        }
        else if (e.Cancelled)
        {
            // Next, handle the case where the user canceled  
            // the operation. 
            // Note that due to a race condition in  
            // the DoWork event handler, the Cancelled 
            // flag may not have been set, even though 
            // CancelAsync was called.
            resultLabel.Text = "Canceled";
        }
        else
        {
            // Finally, handle the case where the operation  
            // succeeded.
            resultLabel.Text = e.Result.ToString();
        }
    
        // Enable the UpDown control. 
        this.numericUpDown1.Enabled = true;
    
        // Enable the Start button.
        startAsyncButton.Enabled = true;
    
        // Disable the Cancel button.
        cancelAsyncButton.Enabled = false;
    }
    

For asynchronous operations that will take a long time, it is often desirable to report progress to the user and to allow the user to cancel the operation. The BackgroundWorker class provides an event that allows you to post progress as your background operation proceeds. It also provides a flag that allows your worker code to detect a call to CancelAsync and interrupt itself.

To implement progress reporting

  1. In the Properties, window, select backgroundWorker1. Set the WorkerReportsProgress and WorkerSupportsCancellation properties to true.

  2. Declare two variables in the FibonacciCalculator form. These will be used to track progress.

    private int numberToCompute = 0;
    private int highestPercentageReached = 0;
    
  3. Add an event handler for the ProgressChanged event. In the ProgressChanged event handler, update the ProgressBar with the ProgressPercentage property of the ProgressChangedEventArgs parameter.

    // This event handler updates the progress bar. 
    private void backgroundWorker1_ProgressChanged(object sender,
        ProgressChangedEventArgs e)
    {
        this.progressBar1.Value = e.ProgressPercentage;
    }
    

To implement support for cancellation

  1. In the cancelAsyncButton control's Click event handler, add the code that cancels the asynchronous operation.

    private void cancelAsyncButton_Click(System.Object sender, 
        System.EventArgs e)
    {   
        // Cancel the asynchronous operation. 
        this.backgroundWorker1.CancelAsync();
    
        // Disable the Cancel button.
        cancelAsyncButton.Enabled = false;
    }
    
  2. The following code fragments in the ComputeFibonacci method report progress and support cancellation.

    if (worker.CancellationPending)
    {   
        e.Cancel = true;
    }
    
    
    ...
    
    
    // Report progress as a percentage of the total task. 
    int percentComplete = 
        (int)((float)n / (float)numberToCompute * 100);
    if (percentComplete > highestPercentageReached)
    {
        highestPercentageReached = percentComplete;
        worker.ReportProgress(percentComplete);
    }
    

At this point, you can compile and run the Fibonacci Calculator application.

To test your project

  • Press F5 to compile and run the application.

    While the calculation is running in the background, you will see the ProgressBar displaying the progress of the calculation toward completion. You can also cancel the pending operation.

    For small numbers, the calculation should be very fast, but for larger numbers, you should see a noticeable delay. If you enter a value of 30 or greater, you should see a delay of several seconds, depending on the speed of your computer. For values greater than 40, it may take minutes or hours to finish the calculation. While the calculator is busy computing a large Fibonacci number, notice that you can freely move the form around, minimize, maximize, and even dismiss it. This is because the main UI thread is not waiting for the calculation to finish.

Now that you have implemented a form that uses a BackgroundWorker component to execute a computation in the background, you can explore other possibilities for asynchronous operations:

Was this page helpful?
(1500 characters remaining)
Thank you for your feedback
Show:
© 2014 Microsoft