As we saw in the previous article, the multi-threading has the added
advantage of being able to show progress and not having your
application hang while
performing time-consuming operation.
Another problem you'll face if you perform all the work on the UI
thread is the fact that there's no way for the user to cancel a running
task - and why is
that? Because if the UI thread is busy performing your lengthy task,
no input will be processed, meaning that no matter how hard your user
hits a Cancel
button or the Esc key, nothing happens.
Fortunately for us, the BackgroundWorker is built to make it easy
for you to support progress and cancellation, and while we looked at the
part in the previous chapter, this one will be all about how to use
the cancellation support. Let's jump straight to an example:
Title="BackgroundWorkerCancellationSample" Height="120" Width="200">
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center">
<TextBlock Name="lblStatus" HorizontalAlignment="Center" Margin="0,10" FontWeight="Bold">Not running...</TextBlock>
<Button Name="btnStart" Width="60" Margin="10,0" Click="btnStart_Click">Start</Button>
<Button Name="btnCancel" Width="60" Click="btnCancel_Click">Cancel</Button>
public partial class BackgroundWorkerCancellationSample : Window
private BackgroundWorker worker = null;
worker = new BackgroundWorker();
worker.WorkerSupportsCancellation = true;
worker.WorkerReportsProgress = true;
worker.DoWork += worker_DoWork;
worker.ProgressChanged += worker_ProgressChanged;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
private void btnStart_Click(object sender, RoutedEventArgs e)
private void btnCancel_Click(object sender, RoutedEventArgs e)
void worker_DoWork(object sender, DoWorkEventArgs e)
for(int i = 0; i <= 100; i++)
if(worker.CancellationPending == true)
e.Cancel = true;
e.Result = 42;
void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
lblStatus.Text = "Working... (" + e.ProgressPercentage + "%)";
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
lblStatus.Foreground = Brushes.Red;
lblStatus.Text = "Cancelled by user...";
lblStatus.Foreground = Brushes.Green;
lblStatus.Text = "Done... Calc result: " + e.Result;
So, the XAML is very fundamental - just a label for showing the
current status and then a couple of buttons for starting and cancelling
In Code-behind, we start off by creating the BackgroundWorker instance. Pay special attention to the WorkerSupportsCancellation and WorkerReportsProgress properties which we set to true - without that, an exception will be thrown if we try to use these features.
The cancel button simply calls the CancelAsync() method - this will signal to the worker that the user would like to cancel the running
process by setting the CancellationPending property to true, but that is all you can do from the UI thread - the rest will have to be done
from inside the DoWork event.
In the DoWork event, we count to 100 with a 250 millisecond delay on each iteration. This gives us a nice and lengthy task, for which we
can report progress and still have time to hit that Cancel button.
Notice how I check the CancellationPending property on each iteration - if the worker is cancelled, this property will be true and I will
have the opportunity to jump out of the loop. I set the Cancel property on the event arguments, to signal that the process was cancelled -
this value is used in the RunWorkerCompleted event to see if the worker actually completed or if it was cancelled.
In the RunWorkerCompleted event, I simply check if the worker was cancelled or not and then update the status label
So to sum up, adding cancellation support to your BackgroundWorker is easy - just make sure that you set WorkerSupportsCancellation
property to true and check the CancellationPending property while performing the work. Then, when you want to cancel the task, you just
have to call the CancelAsync() method on the worker and you're done.