Tuesday, January 27, 2015

Multithreading using Task Factory, C#, Basic Sample

Introduction

In my previous article (MultiThreading Using a Background Worker, C#), I talked about Background Workers, which were the easiest and most effective way of creating multi-threaded desktop applications. However, this technique is becoming obsolete, now the new Task Factory is here. There are many nice articles on this subject, detailed and well organized, like: Task Parallel Library and async-await Functionality - Patterns of Usage in Easy Samples, and the 6 part tutorial: Task Parallel Library: 1 of n. However, I wanted this article to be a sequel of my previous one, and to show you how to use the basic features of the Task Factory. I think that users who are migrating from Background workers will find this easy to follow since I wrote it as close as it can get to the technique we used in background workers, I intended not to use lambda expressions, although I encourage you to use them.

Background

Developers who have written desktop applications know how it feels when performing heavy duty operations, or when requesting data from a remote location. The user interface will freeze till the operation is completed. This is because the application is running on a single thread, and when this thread is busy doing some other stuff, the application will freeze till this thread is ready once again to render the user interface to the user. Here, multithreading comes to the rescue: you delegate your heavy work to another thread, and thus your application will remain responsive during the time of the operation. you can also make use of the multi-processing capability of the user's machine to distribute the job among different threads/tasks. For our sample, we will be using theTask Factory class.

Implementing the Sample

I will be using Visual studio 2012 for this project. Unfortunately, some of the features used here are not available in Visual Studio 2010 or earlier (like the async keyword), or in the best cases, hard to add and will end up in hidden bugs.
Begin by creating a new Windows Forms applications and set your design as below. I personally like to use table layout panels to design my forms, you can check a small tip I have written on this to give you a better idea about this control: Designing the Layout of Windows Forms using a TableLayoutPanel, with auto-expand panels.
Basically, we will have a text box (set to multiline mode) to show the results coming from our working thread, a numeric box to allow us to choose a number, a start button and a cancel button. We will also have a status strip with a status label, to show the progress from our task.
From the toolbox menu, under the Menus & Toolbars section, add a "Status Strip".

Inside the status strip, click the small arrow on the left corner and add a "Status Label". Rename the label tolblStaus, and set its Text property to an empty string.
Before we begin, keep in mind that only the main thread has access to the user controls, so we have to capture the user input from the main thread, and pass it to the background thread somehow.
Right click your form and select "View Code", type down the below method:
 private async void RunTask()
        {
            int numericValue = (int)numericUpDown1.Value;//Capture the user input
            object[] arrObjects = new object[] { numericValue };//Declare the array of objects

            using (Task<string> task = new Task<string>(new Func<object, 
            string>(PerfromTaskAction), arrObjects, cancellationToken))//Declare and 
                                                                       //initialize the task
            {
                lblStatus.Text = "Started Calculation...";//Set the status label to signal 
                                                          //starting the operation
                btnStart.Enabled = false; //Disable the Start button
                task.Start();//Start the execution of the task
                await task;// wait for the task to finish, without blocking the main thread

                if (!task.IsFaulted)
                {
                    textBox1.Text = task.Result.ToString();//at this point, 
                    //the task has finished its background work, and we can take the result
                    lblStatus.Text = "Completed.";//Signal the completion of the task
                }
                btnStart.Enabled = true; //Re-enable the Start button
            }
        }
Here, we first got the user input from the numeric box, created an array of objects, then added the value from the numeric box to this array. We will be passing this array of objects to the background thread, since only the main thread has access to the user controls. After that, we will initialize a Task<String> object, the <String>means that our Task will return a String object.
After that, we set our status label to "Started Calculation...", to signal to the user that our background operation has started. Then we will start our Task, using task.Start();. Then we will wait for the task using the awaitcommand. This is different from the wait command because it does not block the main thread, the execution will be done asynchronously, thus the use of the async keyword in the method declaration.
Now, before we write the code that will be executed in the background thread, let us write a simple method that will simulate a heavy operation (call to a remote server, request data from database, complex operation...), we will just call Thread.Sleep for a 100 milliseconds before returning the result:
private int PerformHeavyOperation(int i)
        {
            System.Threading.Thread.Sleep(100);
            return i * 1000;
        }
Now, we create the method that will be executed by the Task in the background thread, similar to DoWork event in the background worker:
private string PerfromTaskAction(object state)
        {
            object[] arrObjects = (object[])state;//Get the array of objects from the main thread
            int maxValue = (int)arrObjects[0];//Get the maxValue integer from the array of objects

            StringBuilder sb = new StringBuilder();//Declare a new string builder to build the result

            for (int i = 0; i < maxValue; i++)
            {
                sb.Append(string.Format("Counting Number: {0}{1}", 
                PerformHeavyOperation(i), 
                Environment.NewLine));//Append the result to the string builder
            }

            return sb.ToString();//return the result
        }
Finally, double click the start button, and type the below in the click event handler for this button. This will start our task.
  private void btnStart_Click(object sender, EventArgs e)
        {
            RunTask();
        }
Run the form, and click start, you will notice that the form will begin to calculate, remain responsive during the calculation period, and finally will show you the desired result.

Reporting Progress from the Task

It would be nice if we can show the user the progress of our operation, like a status message or a loading progress bar. As we have mentioned before, we cannot access the user interface directly from the background thread, thus we must find a way to report progress to the main thread from the background thread. For this, we will use a Progress<T> object. In my sample, I will be reporting progress as a string, thus declare an object at the top of your code of Type Progress<String> like below:
Progress<string> progressManager = 
new Progress<string>();//Declare the object that will manage progress, and 
  //will be used to get the progress form our background thread
In the form constructor, add the following line of code, this will set the progress changed event.
progressManager.ProgressChanged += progressManager_ProgressChanged;//Set the Progress changed event
The form constructor will now look like this:
 public Form1()
        {
            InitializeComponent();
            progressManager.ProgressChanged += progressManager_ProgressChanged;//Set the 
                                                                               //Progress changed event
        }
Implement the ProgressChanged event, we are just setting the text we received from the background thread to our status label. This event is fired inside the main thread, that's why we are able to access the status label.
 void progressManager_ProgressChanged(object sender, string e)
        {
            lblStatus.Text = e;
        }
Change your perform Task Action method to the below, notice how we are using the progress manager to report progress from our background thread:
     private string PerfromTaskAction(object state)
        {
            object[] arrObjects = (object[])state;//Get the array of objects from the main thread
            int maxValue = (int)arrObjects[0];//Get the maxValue integer from the array of objects

            StringBuilder sb = new StringBuilder();//Declare a new string builder to build the result

            for (int i = 0; i < maxValue; i++)
            {
                sb.Append(string.Format("Counting Number: {0}{1}", 
                PerformHeavyOperation(i), Environment.NewLine));//Append the result 
                                                                //to the string builder
                ((IProgress<string>)progressManager).Report(string.Format
                ("Now Counting number: {0}...", i));//Report our progress to the main thread
            }

            return sb.ToString();//return the result
        }
Now run your form, you will notice that the label will show you the progress update:

Canceling a Running Task

It is always desirable to allow the user to cancel a task that is taking too long to complete, or in case the user is not interested anymore in the result.
To cancel a running task, we will need a Cancellation Token, and to get a cancellation token we will need a cancellation token source. Luckily, there are two Microsoft classes that give you exactly what you want, theCancellationTokenSource and the CancellationToken. Begin by declaring aCancellationTokenSource object, and then declare a CancellationToken object:
 CancellationTokenSource cancellationTokenSource; //Declare a cancellation token source
 CancellationToken cancellationToken; //Declare a cancellation token object, 
  //we will populate this token from the token source, and pass it to the Task constructor.
Double click your "Cancel" button and add the following line of code to the event handler, this will issue a cancellation request to the cancellation token:
cancellationTokenSource.Cancel();
In your RunTask method, add the cancellation token to the constructor of your Task, also initialize theCancellationTokenSource object, and give the CancellationToken a new Token. We have to do this before each start of the task, because cancellation tokens can't be reused after they have been canceled, if we attempt to run a task that has its cancellation token in the canceled state, you will get a runtime error.
 private async void RunTask()
        {
            int numericValue = (int)numericUpDown1.Value;//Capture the user input
            object[] arrObjects = new object[] { numericValue };//Declare the array of objects

            //Because Cancellation tokens cannot be reused after they have been canceled, 
            //we need to create a new cancellation token before each start
            cancellationTokenSource = new CancellationTokenSource();
            cancellationToken = cancellationTokenSource.Token;

            using (Task<string> task = new Task<string>(new Func<object, 
            string>(PerfromTaskAction), arrObjects, cancellationToken))//Declare and initialize the task
            {
                lblStatus.Text = "Started Calculation...";//Set the status label to signal 
                                                          //starting the operation
                btnStart.Enabled = false; //Disable the Start button
                task.Start();//Start the execution of the task
                await task;// wait for the task to finish, without blocking the main thread

                if (!task.IsFaulted)
                {
                    textBox1.Text = task.Result.ToString();//at this point, 
                     //the task has finished its background work, and we can take the result
                    lblStatus.Text = "Completed.";//Signal the completion of the task
                }

                btnStart.Enabled = true; //Re-enable the Start button
            }
        }
Change your PerformTaskAction method to check for cancellation requests at each iteration in your loop, if you find out that the user issued a cancellation request, you break out of the loop, thus bring the execution of the background thread to an end. You can check if a cancellation request is pending by checking theIsCancellationRequested property of the Cancellation Token. Another method will be to use theCancellationToken.ThrowIfCancellationRequested() method to throw an AggregateExceptionthat will stop your background thread, and you can catch this exception from the main thread to know that the task was canceled.
private string PerfromTaskAction(object state)
        {
            object[] arrObjects = (object[])state;//Get the array of objects from the main thread
            int maxValue = (int)arrObjects[0];    //Get the maxValue integer from the array of objects

            StringBuilder sb = new StringBuilder();//Declare a new string builder to build the result

            for (int i = 0; i <= maxValue; i++)
            {
                if (cancellationToken.IsCancellationRequested)//Check if a cancellation request 
                                                              //is pending
                {
                    break;
                }
                else
                {
                    sb.Append(string.Format("Counting Number: {0}{1}", 
                    PerformHeavyOperation(i), Environment.NewLine));//Append the result 
                                                                    //to the string builder
                    ((IProgress<string>)progressManager).Report(string.Format
                    ("Now Counting number: {0}...", i));//Report our progress to the main thread
                }
            }

            return sb.ToString();//return the result
        }
Now try it, run the form and try to cancel your task while it is running, and check if your code works. You can dowload the sample from here

Sunday, January 18, 2015

Designing the Layout of Windows Forms using a TableLayoutPanel, with auto-expand panels

Introduction

Over the years, I have seen many developers designing Windows Forms by just dragging and dropping controls, and then repositioning them to match their requirements. When they need to change anything in the design, or when they add a new control, it becomes a little bit messy.
Here is a small demonstration of the technique I use to design Windows Forms, by using a control calledTableLayoutPanel. I know that there are so many other techniques, others might use docking properties and anchors for example, but I also intended for this tip to be a discussion forum, where I can also learn about other techniques.

Background

TableLayoutPanel is a control that "Represents a panel that dynamically lays out its contents in a grid composed of rows and columns" (From MSDN: TableLayoutPanel Class).

Basic Requirement

In this sample, we are required to build a simple manual entry form like the below, with some conditions:
Whatever the size of the form (down to a certain extent), even when the form is maximized, the following should always remain true:
  • The size of the text boxes must remain equal for all text boxes, and they must fill the largest space available
  • The labels must all be left aligned
  • The Grid View must fill all of the remaining space
  • A small refresh button must exist below the Grid, it must always be aligned to the right of the form.

Implementation

First let us create a new Windows Forms Applications Project. Expand the main form, and add a newTableLayoutPanel control by dragging it from the Toolbox menu under the "Containers" section.

Expand the new control till it fills the form, and Modify the Anchor property to attach to the 4 sides: "Top, Right, Bottom, Left". name the control "tblPnlMain".

Add a new Row to the table panel by clicking the small arrow at the right side of the panel, and then choosing "Add Row".

Now remove the last column by clicking the button "Remove Last Column". After that, in the first row, add another TableLayoutPanel and name it "tblPnlDataEntry". In this panel, we will be placing our labels, text boxes, save and cancel buttons.
Add two additional columns and two additional rows to this new panel.

Set the Anchor of this panel to take all of the four sides, then expand the panel to fill all of its parent container. Please note that in one cell of the table panel, we can only put one control. That's why we are putting a table panel to be able to add controls as much as we want.
Add 6 labels to the first and third column of the data entry manual form. Set the Anchor of all of them to be just "Left". Set their text properties as: "First Name, Last Name", "Mobile Phone", "Home Phone", "Billing Addresses" and "Country". Don't worry about the design now, we will fix that shortly.

Now, in the 2nd and 4th column of the data entry panel, add 6 text boxes. Set the Anchor to all of them to be "Left and Right". This will allow the text boxes to fill the whole width of their parent containers. You will end up with an ugly form like the below:

Now, click on the small arrow on the right side of the Data Entry table panel, and choose "Edit Rows and Columns". For Column 1 and Column 3, set the size type to "Autosize", this will force the columns to take the width of the largest child control. For Column 2 and Column 4, set the size type to "Percent", and set the value to 50% for each column. This will tell the columns to take 50 % of the remaining space each.

This will fix our form:

Now, in the 4th row, last column, add another table panel, name it "tblPnlButtons". For this panel, remove the last row, so that it has only two columns. In the first column, add a button and caption it as "Save". In the second column, add another button and caption it as "Cancel". Set the Anchor property for this panel to Right only. Also set the Anchor for both Buttons to "Right". The form will now look like this:

Now back to the Data Entry Table Panel, Click Edit Rows and Columns, choose the "Rows" item in the drop down list, set the first 3 rows to "AutoSize", and set the 4th row to "Percent", giving it 100%, this means that the last row will take all of the remaining space.

Now the form should look like this (if the buttons are still small and not appearing in full, try to expand your form a little bit).

Still something is not right, the Save and Cancel Button are too far from each other. Open the "Table Layout Panel Tasks" for the Buttons table panel, and set the size type of the first column to "Percent", and the value to 100%, for the second column, set the size type to "AutoSize".

Now the form will look like this:

Add a Grid View to the second row of the main table panel, and add a button to the last row. Set the Anchor of the grid view to all four sides (Top, Right, Bottom, Left), and set the Anchor of the button to Right. Caption the Button as "Refresh".

Open the Table Layout Panel Tasks for the Main table panel, and set the size type of the first row and last row to "AutoSize", and the size type of the second row to"Percent", and the value to 100%. Setting the Size percent of the second row to 100% will ensure that the Grid View will take all of the remaining space.

The completed form will look like this:

Now run the form, resize it, and then maximize it. You will see that the controls are always in order. Set the minimum size of the form to a value of your choice, to ensure that the controls have the minimum space they require to remain in order.
Now for example if you set the Form's "Right to left" property to true, you will notice that all the controls will still be in place, in order, and with the same proportions. This is one example of the benefits of aTableLayoutPanel, it takes a little bit of effort at the beginning, but it will save you a lot of work later on.

Bonus Tip

This is an update to the original post. Suppose your manual entry form needs more data, you need to add a checkbox which will indicate if the person is married or not. If the person is married, 4 entry fields should appear in the control: Spouse name, spouse last name, spouse age and number of children. If the person is not married, these entry fields should disappear, and the controls should resize to fill the empty space. The final design should look like this:

If the user unchecks the checkbox, the form will change to this, notice how the "Save" and "Cancel" buttons will move up to below the "Is Married" checkbox, and the Grid View will expand to fill up the empty space.

To begin, go the data entry table panel and add three more rows:

Expand the table panel to have more space to add the new controls.

Move the buttons table panel to the last row, the form will now look like below:

Now add 5 labels, set their anchors to left only, and caption them as shown below:

Add a checkbox control to the cell adjacent to the "Is Married" label. Set its Anchor to "Left" and set its Textproperty to empty string. Now add four text boxes next to the four new labels. Set their Anchor property to "Right" and "Left".
Now this is important: Set the buttons table panel Anchor to "Top" and "Right", and for the Data Entry Panel, there is a property named "AutoSize". This will specify if the panel will automatically resize to fits its content, set this property to "True".

Now still in the Data Entry Panel, Open the "Edit Rows and Columns" box from the "TableLayoutPanel Tasks", and set the Size type for the first 6 rows to "Auto Size", and set the Size type for the last row to "Percent", set its value to 100%.

The form will now look like this:

Expand it a little bit, so that the "Save" and "Cancel" buttons are completely visible:

Now double click on the checkbox next to the "Is Married" label and add the below code, this will hide or show all the new labels and text boxes based on whether this checkbox is checked or not. Set the Checked property of the checkbox to true so that it is checked by default when the form runs:
  label8.Visible=label9.Visible=label10.Visible=label11.Visible=checkBox1.Checked;
            textBox7.Visible = textBox8.Visible = textBox9.Visible = 
            textBox10.Visible = checkBox1.Checked;
Now run the form, When expanded, it will look like this:

When you uncheck the "Is Married" checkbox, the form will automatically reposition and resize its controls accordingly:

You can download the updated sample code from the link below, thanks for following up Smile | :) .

Download Sample Code

MultiThreading Using a Background Worker, C#

Introduction

When developing Windows Forms applications, you always notice that the user interface will freeze when your application is doing a time consuming operation, for example processing a large file or requesting data from a remote server. This is because your application is running on a single thread. This thread is responsible for rendering the user interface to the user, and also for handling all of your application events and methods. Thus, a lengthy operation will block your user interface till it is done. Today, what we will be doing is to move these lengthy operations to a different thread, thus keeping the user interface running smoothly while your operation is working on the other side.

Background

For this, we will be using the Microsoft BackgroundWorker class, more information on this class can be found here.
We will be creating a simple application that will be performing a time consuming operation, and displaying the final result to the user. This heavy operation will be carried on a different thread, and during its operation it will be constantly updating the user interface with its progress. We will also allow the user to cancel the operation at any time.
Please keep in mind that only the main thread will have access to the user interface, in other words, you cannot access any user control from the other thread. We will see more on this later.

Using the Code

I will be showing the code used in the application as we progress, and at the end, I will attach the final source code.

Creating the Solution

We will begin by creating a simple Windows Forms application form Microsoft Visual Studio, I will be using Visual Studio 2010. Create a new Windows Forms Application as in the below figure, I prefer to use C#, but you can use VB.NET if you like.
Set up your form designer as below. I personally like to use table layout panels to organize my controls. This will also make it a lot easier for me to keep the controls in order if the form is expanded or resized. What we will need to add is a text box (set to multiline mode) to show the results coming from our working thread, a numeric box to allow us to choose a number, a start button and a cancel button.
From the toolbox menu, under the Menus & Toolbars section, add a "Status Strip". This will allow us to add a status label, where we will be showing progress to the end user.
Inside the status strip, click the small arrow on the left corner and add a "Status Label". Rename the label tolblStaus, and set its Text property to an empty string.
Inside the code class, declare an object of type BackgroundWorker:
private BackgroundWorker myWorker = new BackgroundWorker();
In the Form Constructor, initialize the following properties of the worker we just created:
  • The DoWork event handler, which will be called when the background worker is instructed to begin its asynchronous work. It is here inside this event where we do our lengthy operations, for example, call a remote server, query a database, process a file... This event is called on the new thread, which means that we cannot access the user controls from inside this method.
  • The RunWorkerCompleted event handler, which occurs when the background worker has finished execution has been canceled or has raised an exception.This event is called on the main thread, which means that we can access the user controls from inside this method.
  • The ProgressChanged event handler which occurs when the ReportProgress method of the background worker has been called. We use this method to write the progress to the user interface. This event is called on the main thread, which means that we can access the user controls from inside this method.
  • The WorkerReportsProgress property, which is needed to instruct the worker that it can report progress to the main thread.
  • The WorkerSupportsCancellation property, which is needed to instruct the worker that it can be canceled upon the user request.
Below is the complete code for the constructor after the declarations:
public Form1()
{
    InitializeComponent();

    myWorker.DoWork+=new DoWorkEventHandler(myWorker_DoWork);
    myWorker.RunWorkerCompleted+=new RunWorkerCompletedEventHandler(myWorker_RunWorkerCompleted);
    myWorker.ProgressChanged+=new ProgressChangedEventHandler(myWorker_ProgressChanged);
    myWorker.WorkerReportsProgress = true;
    myWorker.WorkerSupportsCancellation = true;
}
Now let us declare the event handlers for our worker:
  • The DoWork event handler will take two parameters, a sender object, and a DoWorkEventArgsargument:
    protected void myWorker_DoWork(object sender, DoWorkEventArgs e)
    {
    
    }
  • The RunWorkerCompeleted event handler will take two parameters, a sender object, and aRunWorkerCompletedEventArgs argument:?
    protected void myWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
    
    }
  • The ProgressChanged event handler will take two parameters, a sender object, and aProgressChangedEventArgs argument:?
    protected void myWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
    
    }
Now we will create a helper method that will simply accept an integer, multiply this integer by 1000, sleep for 250 milliseconds, and then return the result. This is just a simulation for a heavy operation that your application might do, you can change the sleep duration to any value you want. Please note that since this method will be called inside the DoWork event, it is the Background thread that will sleep for the given duration, and not the main thread. The function will be as follows:
private int PerformHeavyOperation(int i)
{
    System.Threading.Thread.Sleep(250);
    return i * 1000;
}
Switch to the Designer mode, double click the Start button to begin handling its click event. What we will do is to capture the numeric value from the numeric up down control, pass this value to the asynchronous thread, and instruct the background worker to start working. We need to capture the numeric value at this stage, because once we are inside the new thread, we will not be able to capture any input from the user controls. To start the execution of the background thread, we will call the RunWorkerAsync method. This method can accept an object argument, this object will be passed to the background thread. Inside this object, we can put any values that we captured from the user controls. To pass more than one value, we can use an array of objects. Below is the complete code for the btnStart_Click event handler, Please note that a worker that is already in progress cannot be called again, you will get a runtime error if you attempt to do so.
private void btnStart_Click(object sender, EventArgs e)
{
    int numericValue = (int)numericUpDownMax.Value;//Capture the user input
    object[] arrObjects = new object[] { numericValue };//Declare the array of objects
    if (!myWorker.IsBusy)//Check if the worker is already in progress
    {
        btnStart.Enabled = false;//Disable the Start button
        myWorker.RunWorkerAsync(arrObjects);//Call the background worker
    }
}
Now inside the DoWork event handler, we will do all of our heavy work. First, we will capture the objects we received from the main thread, then we will process them, finally we will pass the result back to the main thread, to be able to display it to the user. Keep in mind that only the main thread has access to the user controls. Also while we are processing the values, we will be continuously doing two things:
  • Reporting progress to the main thread using the ReportProgress method, to show the user where we are now.
  • Checking the CancellationPending property of the background worker, to check it the user has issued a cancellation command.
Finally, we will be putting our result inside the Result property of the DoWorkEventArgs argument to be captured by the main thread inside the RunWorkerCompleted event. Below is the complete code for ourDoWork handler:
protected void myWorker_DoWork(object sender, DoWorkEventArgs e)
{
    BackgroundWorker sendingWorker = 
    (BackgroundWorker)sender;//Capture the BackgroundWorker that fired the event
    object[] arrObjects = 
    (object[])e.Argument;//Collect the array of objects the we received from the main thread

    int maxValue = (int)arrObjects[0];//Get the numeric value 
            //from inside the objects array, don't forget to cast
    StringBuilder sb = new StringBuilder();//Declare a new string builder to store the result.

    for (int i = 1; i <= maxValue; i++)//Start a for loop
    {
        if (!sendingWorker.CancellationPending)//At each iteration of the loop, 
                    //check if there is a cancellation request pending 
        {
            sb.Append(string.Format("Counting number: {0}{1}", 
            PerformHeavyOperation(i), Environment.NewLine));//Append the result to the string builder
            sendingWorker.ReportProgress(i);//Report our progress to the main thread
        }
        else
        {
            e.Cancel = true;//If a cancellation request is pending, assign this flag a value of true
            break;// If a cancellation request is pending, break to exit the loop
        }
    }

    e.Result = sb.ToString();// Send our result to the main thread!
}
Now we will handle the ProgressChanged event. Here, we will just capture the integer value we have sent from the background thread when we called the ReportProgress method. Please note that you can pass objects of any datatype, by using the UserState of the ProgressChangedEventArgs argument. One thing you can do here other than showing a status message is to use a progress bar to show the current progress.
protected void myWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    //Show the progress to the user based on the input we got from the background worker
    lblStatus.Text = string.Format("Counting number: {0}...", e.ProgressPercentage);
}
Now for the RunWokerCompleted event. Here, we first need to check if the worker has been canceled, or if an error had occurred. After that, we will collect the result that the background worker has calculated for us, and display it to the user:
protected void myWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    if (!e.Cancelled && 
    e.Error == null)//Check if the worker has been canceled or if an error occurred
    {
        string result = (string)e.Result;//Get the result from the background thread
        txtResult.Text = result;//Display the result to the user
        lblStatus.Text = "Done";
    }
    else if (e.Cancelled)
    {
        lblStatus.Text = "User Canceled";
    }
    else
    {
        lblStatus.Text = "An error has occurred";
    }
    btnStart.Enabled = true;//Re enable the start button
}
One last thing remains, we need to implement the cancellation button. Double click the cancel button, and inside the code class, call the CancelAsync method of the background worker. This will set theCancellationPending flag to true. We were checking this flag at each loop iteration back in the DoWorkevent handler. From this, we can conclude that terminating a backgroundworker currently in progress will not happen immediately, if the background worker is doing something, we have to wait for the worker to finish it before being able to cancel the operation. Below is the code for the btnCancel_Click:
private void btnCancel_Click(object sender, EventArgs e)
{
    myWorker.CancelAsync();//Issue a cancellation request to stop the background worker
}

 

Finally, here is a snapshot of the application in progress:
And here is a snapshot when the operation completed:
You can download the source code from the link at the top of this article, thanks for following up. Smile | :)
Next: Check my updated basic sample article on the new C# Task Factory: