Chapter 8. Multithreading

Internally, WPF leverages multithreading by executing the rendering and composition of elements on a separate thread from the UI thread. However, from the point of view of developers, it is fundamentally a single-threaded apartment (STA) model of threading, like the Windows Forms architecture. Initially, the WPF development team wanted to remove the dependency on the STA model, in favor of a mechanism of thread rental, whereby UI objects could be accessed on any thread. However, this design introduced substantial complexity for singlethreaded applications and made it more difficult to interoperate with existing services (such as the Win32 API, Clipboard, Internet Explorer, and so on).

The fact that WPF objects have STA threading means that they can be accessed only on the thread that created them. However, the .NET Framework provides great support for creating and using multiple threads, and the WPF team has exposed a number of useful classes and mechanisms to allow WPF developers to leverage multithreading within their applications.

The recipes in this chapter describe how to:

  • Schedule operations for execution on the UI thread (recipes 8-1, 8-2, and 8-3)

  • Check and ensure that code is running on the UI thread (recipes 8-4 and 8-5)

  • Execute a method asynchronously using a background thread (recipes 8-6, 8-7, 8-8, and 8-9)

  • Update the UI asynchronously on a thread-safe timer (recipe 8-10)

  • Show a continuous animation during an asynchronous process (recipe 8-11)

  • Show a progress bar while processing on a background thread (recipes 8-12, 8-13, and 8-14)

  • Implement Application.DoEvents in WPF (recipe 8-15)

  • Create a separate thread for each window in a multiwindow application (recipe 8-16)

Execute a Method Asynchronously Using the Dispatcher Queue

Problem

You need to execute a method asynchronously without blocking the UI thread.

Solution

Invoke the method on the Dispatcher property for a UI element, and specify a System.Windows.Threading.DispatcherPriority that is lower than the Render event (see Table 8-1).

How It Works

In WPF, most objects ultimately derive from System.Windows.Threading.DispatcherObject, and only the thread that created a DispatcherObject may access that object. For example, a background thread cannot update the contents of a System.Windows.Controls.TextBox that was created on the UI thread.

The DispatcherObject has a method called BeginInvoke, which takes a System.Delegate as one of its parameters. When BeginInvoke is called, the Dispatcher will schedule this delegate for execution in its event queue. When it is due execution, the Dispatcher will execute this delegate on the same thread on which its owner was created. The BeginInvoke method is asynchronous and returns to the caller immediately.

The second parameter expected by BeginInvoke is a DispatcherPriority, which controls when the delegate is due execution. It does this by informing the Dispatcher of the priority of this event, relative to the other pending operations in the event queue. Events will be executed only when there are no higher priority events in the queue. This is useful for specifying that a certain process should be executed, for example, in the background or when the application is idle, but should not block more important events such as those concerned with loading or rendering the element.

Note

Microsoft Word accomplishes spell checking using this mechanism. Spell checking is done in the background using the idle time of the UI thread.

Table 8-1 shows the values of the DispatcherPriority enum in ascending order of priority. Because the Dispatcher executes the delegate in the same thread that was used to create the component, the code in the delegate can update and interact with the control. This is the simplest way possible of executing a method asynchronously, without exposing yourself to complex synchronization issues such as race conditions, deadlocks, live locks, and memory corruption. We will cover more advanced ways using the System.ComponentModel.BackgroundWorker later in this chapter.

Table 8-1. Dispatcher Priorities, Lowest to Highest

Name

Value

Invalid

−1

Inactive

0

SystemIdle

1

ApplicationIdle

2

ContextIdle

3

Background

4

Input

5

Loaded

6

Render

7

DataBind

8

Normal

9

Send

10

The Code

The following example demonstrates a simple application with a window containing a System.Windows.Controls.Button and a System.Windows.Controls.TextBlock. When the user clicks the Button, there is code in the code-behind that counts upward from 3, searching for prime numbers. When the program finds a prime number, it updates the TextBlock with the number. The user can click the Button again to stop the search.

The example schedules a prime number check on the window's Dispatcher property. It uses a DispatcherPriority of SystemIdle, which ensures that any pending UI events are processed in between each check.

The XAML for the window is as follows:

<Window x:Class="Recipe_08_01.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="WPF Recipes 8_01" Width="220" Height="104" >
    <StackPanel Orientation="Vertical">
        <Button
            Click="StartStop_Click"
            Name="btnStartStop"
            Margin="5"
            Height="34">Start</Button>
        <StackPanel Orientation="Horizontal">
            <TextBlock Margin="5">Biggest Prime Found:</TextBlock>
            <TextBlock Name="txtBiggestPrime" Margin="5" />
        </StackPanel>
    </StackPanel>
</Window>

The code-behind for the window is as follows:

using System;
using System.Windows;
using System.Windows.Threading;
using Recipe_08_01;

namespace Recipe_08_01
{
    public partial class Window1 : Window
    {
        private bool continueCalculating = false;

        public Window1() : base()
        {
            InitializeComponent();
        }

        private void StartStop_Click(object sender, RoutedEventArgs e)
        {
            if(continueCalculating)
            {
                continueCalculating = false;
                btnStartStop.Content = "Start";
            }
            else
            {
                continueCalculating = true;
                btnStartStop.Content = "Stop";

                // Execute the CheckPrimeNumber method on
                // the current Dispatcher queue
                this.Dispatcher.BeginInvoke(
                    DispatcherPriority.Normal,
                    new Action<int>(CheckPrimeNumber), 3);
            }
        }

        public void CheckPrimeNumber(int current)
        {
            if(PrimeNumberHelper.IsPrime(current))
            {
                txtBiggestPrime.Text = current.ToString();
            }
if(continueCalculating)
            {
                // Execute the CheckPrimeNumber method
                // again, using a lower DispatcherPriority
                this.Dispatcher.BeginInvoke(
                    DispatcherPriority.SystemIdle,
                    new Action<int>(CheckPrimeNumber), current + 2);
            }
        }
    }
}

The code in the PrimeNumberHelper class that searches for prime numbers is omitted for the sake of brevity, but the full code is available with the online examples for this chapter.

Figure 8-1 shows the resulting window. If you resize the window whilst the calculation is running, you can see that whilst the calculation slows down, the repainting of the window does not. This is because the paint messages are given higher priority by the dispatcher queue.

Executing a method asynchronously using the Dispatcher

Figure 8-1. Executing a method asynchronously using the Dispatcher

Load the Data for a Window Asynchronously After It Has Rendered

Problem

You need to show a window or control that loads a lot of data and have it load the data after the control has finished loading and rendering correctly.

Solution

In the Loaded event of the window or control, execute the method to load the data by calling the BeginInvoke method of the System.Windows.Threading.DispatcherObject for the UI element, and specify a System.Windows.Threading.DispatcherPriority of Background.

How It Works

Suppose you have a window or control that displays some data and it takes a noticeable amount of time to generate and display this data. If you execute the method that loads this data in the window or control's constructor, then the entire UI element will not be displayed until after the method has completed. Furthermore, if you execute the method in the Loaded event, then whilst the control or window will be displayed immediately, it will not be rendered correctly until the data has finished being loaded.

When you use the BeginInvoke method of a window or control's Dispatcher property, it adds the delegate to the Dispatcher's event queue but gives you the opportunity to specify a lower priority for it. This ensures that the Dispatcher processes all loading and rendering events before executing the delegate that loads the data. This allows the control or window to be displayed and rendered immediately, whilst the data waits in the background to be loaded.

The Code

The following example displays a window containing a System.Windows.Controls.TextBlock and a System.Windows.Controls.ListBox. In the code-behind for the window, there is a method called LoadNumbers, which generates a list of 1 million numbers and sets them as the ItemsSource for the ListBox.

The XAML for the window is as follows:

<Window x:Class="Recipe_08_02.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="WPF Recipes 8_02" Height="200" Width="300"
    Loaded="Window_Loaded">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="24"/>
            <RowDefinition />
        </Grid.RowDefinitions>

        <TextBlock Margin="4">
            One Million Numbers:
        </TextBlock>

        <ListBox x:Name="listBox"
               Grid.Row="1"
               />
    </Grid>
</Window>

In the code-behind, there are comments that show three possible ways of calling the LoadNumbers method. If you uncomment any one of the three options and comment out the other two, you can see the effect on the loading and rendering of the window. Option 3 is the one that is uncommented by default, and it is the only option that allows the window to be loaded and rendered correctly, before trying to generate and display the million numbers.

It works by calling the BeginInvoke method of the window's Dispatcher property and setting a DispatcherPriority of Background. The code-behind is as follows:

using System.Windows;
using System.Windows.Threading;
using System.Collections.Generic;
namespace Recipe_08_02
{
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();

            // Option 1.
            // If LoadNumbers is called here, the window
            // doesn't show until the method has completed.
            //
            // LoadNumbers();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            // Option 2.
            // If LoadNumbers is called here, the window
            // loads immediately, but it doesn't display
            // properly until the method has completed.
            //
            // LoadNumbers();

            // Option 3.
            // If LoadNumbers is invoked here on the
            // window's Dispatcher with a DispatcherPriority of
            // Background, the window will load and be displayed
            // properly immediately, and then the list of numbers
            // will be generated and displayed once the
            // method has completed.
            //
            this.Dispatcher.BeginInvoke(
                DispatcherPriority.Background,
                new LoadNumbersDelegate(LoadNumbers));
        }

        // Declare a delegate to wrap the LoadNumbers method
        private delegate void LoadNumbersDelegate();

        // Load one million numbers into a list and
        // set it as the ItemsSource for the ListBox
        private void LoadNumbers()
        {
            List<string> numberDescriptions = new List<string>();
for(int i = 1; i <= 1000000; i++)
        {
            numberDescriptions.Add("Number " + i.ToString());
        }

        // Set the ItemsSource
        listBox.ItemsSource = numberDescriptions;
     }
  }
}

Figure 8-2 shows the resulting window.

Loading the data for a control after it has rendered

Figure 8-2. Loading the data for a control after it has rendered

Load the Items in a ListBox Asynchronously

Problem

You need to show a System.Windows.Controls.ListBox, or any System.Windows.Controls.ItemsControl, and load its data one item at a time, allowing the window to receive other input, binding, and rendering events whilst the items are loading.

Solution

In the Loaded event of the window or control, execute the method to load the first item of data by calling the BeginInvoke method of the System.Windows.Threading.DispatcherObject for the UI element, and specify a System.Windows.Threading.DispatcherPriority of Background. When this method has finished generating the data and adding it to the list, add the same method to the Dispatcher's queue recursively, each time adding just one item and then queuing the call to add the next one with a DispatcherPriority of Background.

How It Works

Suppose you have a window or control that displays some data and it takes a noticeable amount of time to generate and display this data. If you have one method that loads all the data at one go, then none of the data will appear until the entire list has been loaded.

When you use the BeginInvoke method of a window or control's Dispatcher property, it adds the delegate to the Dispatcher's event queue but gives you the opportunity to specify a lower priority for it. By executing a method that loads just one item at a time, the window is given the chance to execute any other higher-priority events in between items. This allows the control or window to be displayed and rendered immediately and loads each item one at a time.

The Code

The following example displays a window containing a System.Windows.Controls.TextBlock and a System.Windows.Controls.ListBox. In the code-behind for the window, the ListBox is bound to a System.Collections.ObjectModel.ObservableCollection of strings. The ObservableCollection class represents a dynamic collection that provides notifications when items get added, when items get removed, or when the whole list is refreshed. There is a method called LoadNumber, which adds a number to this list, increments the number, and calls itself recursively 10,000 times. Each time LoadNumber is called, it is executed by a delegate that is added to the window's Dispatcher queue, using a priority of Background. This means any higher-priority events added to the queue while the list items are being generated, such as events concerned with input, loading, binding, or rendering, are executed first.

The XAML for the window is as follows:

<Window x:Class="Recipe_08_03.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="WPF Recipes 8_03" Height="200" Width="300"
    Loaded="Window_Loaded">

    <Window.Resources>
        <DataTemplate
        x:Key="ListItemTemplate">
        <StackPanel
            Orientation="Horizontal">
            <Image
                Margin="4"
                Source="Apress.gif"
                />
            <TextBlock
                Margin="4"
                Text="{Binding}"
                VerticalAlignment="Center"
                />
        </StackPanel>
    </DataTemplate>
</Window.Resources>

<Grid>
<Grid.RowDefinitions>
         <RowDefinition Height="24"/>
         <RowDefinition />
      </Grid.RowDefinitions>

     <TextBlock
         Margin="4">
         Loading 10,000 Numbers, one at a time:
     </TextBlock>

     <ListBox
         x:Name="listBox"
         Grid.Row="1"
         ItemTemplate=
           "{StaticResource ListItemTemplate}"
           />

    </Grid>
</Window>

The code-behind is as follows:

using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Threading;

namespace Recipe_08_03
{
    public partial class Window1 : Window
    {
        // ObservableCollection of strings
        private ObservableCollection<string> numberDescriptions;

        public Window1()
        {
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            // Initialize an ObservableCollection of strings
            numberDescriptions =
                new ObservableCollection<string>();

            // Set it as the ItemsSource for the ListBox
            listBox.ItemsSource = numberDescriptions;
// Execute a delegate to load
            // the first number on the UI thread, with
            // a priority of Background.
            //
            this.Dispatcher.BeginInvoke(
                DispatcherPriority.Background,
                new LoadNumberDelegate(LoadNumber), 1);
        }

        // Declare a delegate to wrap the LoadNumber method
        private delegate void LoadNumberDelegate(int number);

        private void LoadNumber(int number)
        {
            // Add the number to the observable collection
            // bound to the ListBox
            numberDescriptions.Add("Number " + number.ToString());

            if(number < 10000)
            {
                // Load the next number, by executing this method
                // recursively on the dispatcher queue, with
                // a priority of Background.
                //
                this.Dispatcher.BeginInvoke(
                    DispatcherPriority.Background,
                    new LoadNumberDelegate(LoadNumber), ++number);
            }
        }
    }
}

Figure 8-3 shows the resulting window.

Loading the data for a ListBox, one item at a time

Figure 8-3. Loading the data for a ListBox, one item at a time

Check Whether You Are Running on the UI Thread

Problem

You need to determine whether the code being executed is running on the UI thread. This is necessary, for example, if the code needs to interact with a UI element. In such a case, if the code is not running on the UI thread, it will cause a System.InvalidOperationException, with the error message "The calling thread cannot access the object because a different thread owns it."

Solution

Use the Dispatcher property of any of your UI elements, and call the CheckAccess method.

How It Works

System.Windows.UIElement inherits from System.Windows.Threading.DispatcherObject, which exposes a System.Windows.Threading.Dispatcher property. The Dispatcher class has a method called CheckAccess that returns True if the calling thread is the current thread and False if not.

Tip

This CheckAccess method is the WPF equivalent of the InvokeRequired property of the Control object in Windows Forms, which shares similar thread-affinity rules to WPF.

The Code

The following example displays a window containing two System.Windows.Controls.Button controls and a System.Windows.Controls.TextBox. In the code-behind for the window, there is a method called CheckAccess that calls the CheckAccess method of the TextBox's Dispatcher property. In the click event for the button on the left, this CheckAccess method is called on the UI thread. In the click event for the button on the right, the method is invoked asynchronously using the BeginInvoke method of a delegate. The result of the call to Dispatcher.CheckAccess is then displayed in the TextBox.

The XAML for the window is as follows:

<Window x:Class="Recipe_08_04.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="WPF Recipes 8_04" Height="120" Width="364">
    <Grid>
      <Grid.ColumnDefinitions>
          <ColumnDefinition/>
          <ColumnDefinition/>
      </Grid.ColumnDefinitions>
<Grid.RowDefinitions>
                 <RowDefinition/>
                 <RowDefinition Height="30"/>
         </Grid.RowDefinitions>

         <Button
             Grid.Column="0"
             Click="ButtonTrue_Click"
             Margin="4">
             UI Thread
         </Button>

         <Button
             Grid.Column="1"
             Click="ButtonFalse_Click"
             Margin="4">
             Non-UI Thread
         </Button>

         <TextBlock
             Grid.Row="1"
             Margin="4">
             Dispatcher.CheckAccess() =
         </TextBlock>

         <TextBlock
              x:Name="txtResult"
              Grid.Row="1"
              Grid.Column="1"
              Margin="4"
              />
     lt;/Grid>
</Window>

The code for the window is as follows:

using System.Windows;
using System.Windows.Threading;

namespace Recipe_08_04
{
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
        }
private void ButtonTrue_Click(object sender, RoutedEventArgs e)
        {
            // Call CheckAccess on the UI thread
            CheckAccess();
        }

        private void ButtonFalse_Click(object sender, RoutedEventArgs e)
        {
            // Invoke a call to CheckAccess
            // on a different thread
            CheckAccessDelegate del =
                new CheckAccessDelegate(CheckAccess);
            del.BeginInvoke(null, null);
        }

        // Declare a delegate to wrap the CheckAccess method
        private delegate void CheckAccessDelegate();

        // Declare a delegate to wrap the SetResultText method
        private delegate void SetResultTextDelegate(string result);

        private void CheckAccess()
        {
            // Check if the calling thread is in the UI thread
            if(txtResult.Dispatcher.CheckAccess())
            {
                SetResultText("True");
            }
            else
            {
                // The calling thread does not have access to the UI thread.
                // Execute the SetResult method on the Dispatcher of the UI thread.
                txtResult.Dispatcher.BeginInvoke(
                    DispatcherPriority.Normal,
                    new SetResultTextDelegate(SetResultText),
                    "False");
            }
        }

        private void SetResultText(string result)
        {
            // Display the result of the CheckAccess method
            txtResult.Text = result;
        }
    }
}

Figure 8-4 shows the resulting window.

Determining whether the calling thread has access to the UI thread

Figure 8-4. Determining whether the calling thread has access to the UI thread

Ensure That You Are Running on the UI Thread

Problem

You need to verify that the code being executed is running on the UI thread and throw an exception if not.

Solution

Use the Dispatcher property of any of your UI elements, and call the VerifyAccess method.

How It Works

System.Windows.UIElement inherits from System.Windows.Threading.DispatcherObject, which exposes a System.Windows.Threading.Dispatcher property. The Dispatcher class has a method called VerifyAccess that throws a System.InvalidOperationException if the calling thread is not the current thread.

The Code

The following example displays a window containing two System.Windows.Controls.Button controls. In the code-behind for the window, there is a method called VerifyAccess that calls the VerifyAccess method of the window's Dispatcher property. In the click event for the button on the left, this VerifyAccess method is called on the UI thread. In the click event for the button on the right, the method is invoked asynchronously using the BeginInvoke method of a delegate. This results in an InvalidOperationException with an error message stating that "The calling thread cannot access this object because a different thread owns it."

The XAML for the window is as follows:

<Window x:Class="Recipe_08_05.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="WPF Recipes 8_05" Height="100" Width="300">
<Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>

        <Button
            Grid.Column="0"
            Click="ButtonTrue_Click"
            Margin="4">
            UI Thread
        </Button>

        <Button
            Grid.Column="1"
            Click="ButtonFalse_Click"
            Margin="4">
            Non-UI Thread
        </Button>

    </Grid>
</Window>

The code for the window is as follows:

using System.Windows;

namespace Recipe_08_05
{
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
        }

        private void ButtonTrue_Click(object sender, RoutedEventArgs e)
        {
            // Call VerifyAccess on the UI thread
            VerifyAccess();
        }

        private void ButtonFalse_Click(object sender, RoutedEventArgs e)
        {
            // Invoke a call to VerifyAccess
            // on a different thread
VerifyAccessDelegate del =
                new VerifyAccessDelegate(VerifyAccess);
            del.BeginInvoke(null, null);
        }

        // Declare a delegate to wrap the VerifyAccess method
        private delegate void VerifyAccessDelegate();

        private void VerifyAccess()
        {
            this.Dispatcher.VerifyAccess();
        }
    }
}

Figure 8-5 shows the resulting window.

Verifying that the calling threads has access to the UI thread

Figure 8-5. Verifying that the calling threads has access to the UI thread

Execute a Method Asynchronously Using a Background Worker Thread

Problem

You need to execute a method asynchronously on a background thread.

Solution

Create an instance of the System.ComponentModel.BackgroundWorker class, attach event handlers to its DoWork and RunWorkerCompleted events, and call the RunWorkerAysnc method to start the background thread.

How It Works

The BackgroundWorker component gives you the ability to execute time-consuming operations asynchronously. It automatically executes the operation on a different thread to the one that created it and then automatically returns control to the calling thread when it is completed.

The BackgroundWorker's DoWork event specifies the delegate to execute asynchronously. It is this delegate that is executed on a background thread when the RunWorkerAsync method is called. When it has completed the operation, it calls the RunWorkerCompleted event and executes the attached delegate on the same thread that was used to create it.

The DoWork method takes an argument of type System.ComponentModel.DoWorkEventArgs, which allows you to pass an argument to the method. The RunWorkerCompleted event is passed an instance of the System.ComponentModel.RunWorkerCompletedEventArgs class, which allows you to receive the result of the background process and any error that might have been thrown during processing.

If the BackgroundWorker object is created on the UI thread, for example, in the constructor method for a window or control, then you can access and update the UI in the RunWorkerCompleted event without having to check that you are on UI thread again. The BackgroundWorker object handles all the thread marshaling for you.

The Code

The following example creates a window with two System.Windows.Controls.TextBlock controls and a System.Windows.Controls.Button. The values in the TextBlock controls specify a range of numbers. In the constructor for the window, an instance of the BackgroundWorker class is created, and event handlers are attached to its DoWork and the RunWorkerCompleted events.

When the Button is clicked, the RunWorkerAsync method is called, which starts the BackgroundWorker and executes the DoWork event. In the code for this event, a PrimeNumberHelper class is used to search for the largest prime number within the range of numbers. When it finishes, the code for the RunWorkerCompleted event is executed, which updates a System.Windows.Controls.TextBlock on the window.

The XAML for the window is as follows:

<Window x:Class="Recipe_08_06.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="WPF Recipes 8_06" Width="240" Height="140" >
        <StackPanel Orientation="Vertical">
        <StackPanel Orientation="Horizontal">
            <TextBlock Margin="5" VerticalAlignment="Center">From:</TextBlock>
            <TextBox Name="txtFrom" Margin="5" Width="64" Text="1" />
            <TextBlock Margin="5" VerticalAlignment="Center">To:</TextBlock>
            <TextBox Name="txtTo" Margin="5" Width="64" Text="1000000"/>
        </StackPanel>
        <Button
            Click="Start_Click"
            Name="btnStart"
            Margin="5"
            Height="34">Start</Button>
        <StackPanel Orientation="Horizontal">
            <TextBlock Margin="5">Biggest Prime Found:</TextBlock>
            <TextBlock Name="txtBiggestPrime" Margin="5" />
        </StackPanel>
    </StackPanel>
</Window>

The code-behind for the window is as follows:

using System;
using System.Windows;
using System.ComponentModel;
using Recipe_08_06;

namespace Recipe_08_06
{
    public partial class Window1 : Window
    {
        private BackgroundWorker worker;

        private long from;
        private long to;
        private long biggestPrime;

        public Window1()
            : base()
        {
            InitializeComponent();

            // Create a Background Worker
            worker = new BackgroundWorker();

            // Attach the event handlers
            worker.DoWork +=
                new DoWorkEventHandler(worker_DoWork);
            worker.RunWorkerCompleted +=
                new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
        }

        private void Start_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                if(!long.TryParse(txtFrom.Text, out from))
                    throw new ApplicationException("From is not a valid number");

                if(!long.TryParse(txtTo.Text, out to))
                    throw new ApplicationException("To is not a valid number");

                // Start the Background Worker
                worker.RunWorkerAsync();
btnStart.IsEnabled = false;
                txtBiggestPrime.Text = string.Empty;
            }
            catch(Exception ex)
            {
                MessageBox.Show(
                       ex.Message, "Error",
                       MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        private void worker_RunWorkerCompleted(
            object sender, RunWorkerCompletedEventArgs e)
        {
            btnStart.IsEnabled = true;
            txtBiggestPrime.Text = biggestPrime.ToString();
        }

        private void worker_DoWork(
            object sender, DoWorkEventArgs e)
        {
            // Loop through the numbers, finding the biggest prime
            for(long current = from; current <= to; current++)
            {
                if(PrimeNumberHelper.IsPrime(current))
                {
                    biggestPrime = current;
                }
            }
        }
    }
}

Figure 8-6 shows the resulting window.

Executing a method asynchronously using a background thread

Figure 8-6. Executing a method asynchronously using a background thread

Track the Progress of a Background Worker Thread

Problem

You need to track the progress of a background worker thread. This is useful to keep the user informed during a long-running process.

Solution

Create an instance of the System.ComponentModel.BackgroundWorker class to process an operation on a background thread. Set its WorkerReportsProgress property to True, and attach an event handler to its ProgressChanged event. Call the ReportProgress method from the DoWork event handler.

How It Works

The BackgroundWorker component provides built-in support for tracking the progress of its operation. This is useful for displaying how much work has been completed on the UI. Three steps are involved. First, you need to set the BackgroundWorker's WorkerReportsProgress property to True. It is False by default. Second, you need to call its ReportProgress method during the operation, passing in the percentage of work that has been completed. Third, you need to attach an event handler to the ProgressChanged event, which is automatically raised each time the ReportProgress method is called.

The BackgroundWorker class executes the ProgressChanged event on the thread that created it, so if this is the UI thread, you can update the UI directly in this event handler.

The Code

The following example creates a window that instantiates a BackgroundWorker object in its constructor and sets its WorkerReportsProgress property to True. It displays a number of controls, including a System.Windows.Controls.Button. When this Button is clicked, the BackgroundWorker starts an operation to search for the largest prime number within a range of numbers.

After each prime number is found, the ReportProgess method is called, passing in an estimated percentage of completion. This executes the code in the worker_ProgressChanged method, which displays the percentage in a System.Windows.TextBlock on the window.

The XAML for the window is as follows:

<Window x:Class="Recipe_08_07.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   Title="WPF Recipes 8_07" Width="228" Height="168" >
     <StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
           <TextBlock Margin="5" VerticalAlignment="Center">From:</TextBlock>
           <TextBox Name="txtFrom" Margin="5" Width="60" Text="1" />
           <TextBlock Margin="5" VerticalAlignment="Center">To:</TextBlock>
            <TextBox Name="txtTo" Margin="5" Width="60" Text="10000"/>
        </StackPanel>
        <Button
            Click="StartStop_Click"
            Name="btnStartStop"
            Margin="5"
            Height="34">Start</Button>
        <StackPanel Orientation="Horizontal">
            <TextBlock Margin="5">Percent complete:</TextBlock>
            <TextBlock Name="txtPercent" Margin="5" />
        </StackPanel>
        <StackPanel Orientation="Horizontal">
            <TextBlock Margin="5">Biggest Prime Found:</TextBlock>
            <TextBlock Name="txtBiggestPrime" Margin="5" />
        </StackPanel>
    </StackPanel>
</Window>

The code-behind for the window is as follows:

using System;
using System.ComponentModel;
using System.Windows;
using Recipe_08_07;

namespace Recipe_08_07
{
    public partial class Window1 : Window
    {
        private BackgroundWorker worker;

        private long from;
        private long to;
        private long biggestPrime;

        public Window1()
            : base()
        {
            InitializeComponent();

            // Create a Background Worker
            worker = new BackgroundWorker();
            worker.WorkerReportsProgress = true;
// Attach the event handlers
            worker.DoWork +=
                new DoWorkEventHandler(worker_DoWork);
            worker.RunWorkerCompleted +=
                new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
            worker.ProgressChanged += worker_ProgressChanged;
        }

        private void StartStop_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                if(!long.TryParse(txtFrom.Text, out from))
                    throw new ApplicationException("From is not a valid number");

                if(!long.TryParse(txtTo.Text, out to))
                    throw new ApplicationException("To is not a valid number");

                // Start the Background Worker
                worker.RunWorkerAsync();

                btnStartStop.IsEnabled = false;
                txtBiggestPrime.Text = string.Empty;
            }
            catch(Exception ex)
            {
                MessageBox.Show(
                        ex.Message, "Error",
                        MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        private void worker_RunWorkerCompleted(
            object sender, RunWorkerCompletedEventArgs e)
        {
            btnStartStop.IsEnabled = true;
            txtBiggestPrime.Text = biggestPrime.ToString();
        }

        private void worker_DoWork(
            object sender, DoWorkEventArgs e)
        {
            // Loop through the numbers, finding the biggest prime
            for(long current = from; current <= to; current++)
            {
if(PrimeNumberHelper.IsPrime(current))
                {
                    biggestPrime = current;

                    // Call report progress to fire the ProgressChanged event
                    int percentComplete = Convert.ToInt32(
                                                             ((double) current / to)
                                                                   * 100d);
                    worker.ReportProgress(percentComplete);

                    System.Threading.Thread.Sleep(10);
                }
            }
        }

        private void worker_ProgressChanged(
            object sender, ProgressChangedEventArgs e)
        {
            // Update the progress bar
            txtPercent.Text = e.ProgressPercentage.ToString() + "%";
       }
   }
}

Figure 8-7 shows the resulting window.

Tracking the progress of a background worker thread

Figure 8-7. Tracking the progress of a background worker thread

Support the Cancellation of a Background Worker Thread

Problem

You need to allow a user to cancel an operation on a background worker thread.

Solution

Create an instance of the System.ComponentModel.BackgroundWorker class to process an operation on a background thread. Set its WorkerSupportsCancellation property to True, and call the CancelAsync method when the user wants to cancel the operation. In the DoWork event handler, check the CancellationPending property, and if this is True, use the Cancel property of System. ComponentModel.DoWorkEventArgs to notify the RunWorkerCompleted event handler that the operation was cancelled.

How It Works

A few steps are involved in allowing an operation on a background worker thread to be cancelled. First, set the WorkerSupportsCancellation property of the BackgroundWorker to True. It is False by default. Second, you need to call its CancelAsync method when the user has requested the cancellation, for example, by clicking a System.Windows.Controls.Button marked Cancel. This doesn't automatically cancel the operation; it just sets the BackgroundWorker's CancellationPending property to True. So, the next thing you need to do is check the value of this property during the operation. When it is True, you must set the Cancel property of the DoWorkEventArgs parameter to True and exit the method. Finally, in the RunWorkerCompleted event handler, you can check the Cancelled property of the System.ComponentModel.RunWorkerCompletedEventArgs parameter. If it is True, you know that the operation was cancelled by the user.

The Code

The following example creates a window that instantiates a BackgroundWorker object in its constructor and sets its WorkerSupportsCancellation property to True. It displays a number of controls, including a System.Windows.Controls.Button. The first time this Button is clicked, it runs the BackgroundWorker, which starts an operation to search for the largest prime number within a range of numbers. It also sets the text on the Button to Cancel. When it is clicked again, it calls the CancelAsync method of the BackgroundWorker.

Each time the code in the DoWork method checks whether a number is prime, it also checks to see whether the BackgroundWorker's CancellationPending property is True. If it is, it sets the Cancel property of the DoWorkEventArgs parameter to True and exits the method. In the RunWorkerCompleted method, it checks the Cancelled property of the RunWorkerCompletedEventArgs parameter and displays a System.Windows.Messagebox if it is True.

The XAML for the window is as follows:

<Window x:Class="Recipe_08_08.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="WPF Recipes 8_08" Width="230" Height="148" >
    <StackPanel Orientation="Vertical">
        <StackPanel Orientation="Horizontal">
            <TextBlock Margin="5" VerticalAlignment="Center">From:</TextBlock>
            <TextBox Name="txtFrom" Margin="5" Width="64" Text="1" />
            <TextBlock Margin="5" VerticalAlignment="Center">To:</TextBlock>
            <TextBox Name="txtTo" Margin="5" Width="64" Text="1000000"/>
        </StackPanel>
<Button
            Click="StartStop_Click"
            Name="btnStartStop"
            Margin="5"
            Height="34">Start</Button>
        <StackPanel Orientation="Horizontal">
            <TextBlock Margin="5">Biggest Prime Found:</TextBlock>
            <TextBlock Name="txtBiggestPrime" Margin="5" />
        </StackPanel>
    </StackPanel>
</Window>

The code-behind for the window is as follows:

using System;
using System.ComponentModel;
using System.Windows;
using Recipe_08_08;

namespace Recipe_08_08
{
    public partial class Window1 : Window
    {
        private BackgroundWorker worker;

        private long from;
        private long to;
        private long biggestPrime;

        public Window1()
            : base()
        {
            InitializeComponent();

            // Create a Background Worker
            worker = new BackgroundWorker();

            // Enable support for cancellation
            worker.WorkerSupportsCancellation = true;

            // Attach the event handlers
            worker.DoWork +=
                new DoWorkEventHandler(worker_DoWork);
            worker.RunWorkerCompleted +=
                new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
        }
private void StartStop_Click(object sender, RoutedEventArgs e)
        {
            if(!worker.IsBusy)
            {
                try
                {
                    if(!long.TryParse(txtFrom.Text, out from))
                        throw new ApplicationException(
                                     "From is not a valid number");

                    if(!long.TryParse(txtTo.Text, out to))
                        throw new ApplicationException("To is not a valid number");

                    // Start the Background Worker
                    worker.RunWorkerAsync();

                    btnStartStop.Content = "Cancel";
                    txtBiggestPrime.Text = string.Empty;
                }
                catch(Exception ex)
                {
                    MessageBox.Show(
                              ex.Message, "Error",
                              MessageBoxButton.OK, MessageBoxImage.Error);
                }
            }
            else
            {
                // Cancel the Background Worker
                worker.CancelAsync();
            }
        }

        private void worker_RunWorkerCompleted(
            object sender, RunWorkerCompletedEventArgs e)
        {
            if(e.Cancelled)
            {
                // The user cancelled the operation
                MessageBox.Show("Operation was cancelled");
            }

            btnStartStop.Content = "Start";
            txtBiggestPrime.Text = biggestPrime.ToString();
        }
private void worker_DoWork(
            object sender, DoWorkEventArgs e)
        {
            // Loop through the numbers, finding the biggest prime
            for(long current = from; current <= to; current++)
            {
                // Check if the BackgroundWorker
                // has been cancelled
                if(worker.CancellationPending)
                {
                    // Set the Cancel property
                    e.Cancel = true;
                    return;
                }

                if(PrimeNumberHelper.IsPrime(current))
                {
                    biggestPrime = current;
                }
            }
        }
    }
}

Figure 8-8 shows the resulting window.

Supporting the cancellation of a background worker thread

Figure 8-8. Supporting the cancellation of a background worker thread

Create a Background Worker Thread in XAML

Problem

You need to declare a System.ComponentModel.BackgroundWorker in XAML.

Solution

Reference the System.ComponentModel namespace in the XAML for your window, and define an instance of the BackgroundWorker in the Window.Resources collection.

How It Works

When the window is initialized, the instance of the BackgroundWorker will be instantiated. You can set its properties and attach its event handlers using attributes in the XAML.

The Code

The following example demonstrates a window that declares a BackgroundWorker in the Window.Resources collection. It imports the System.ComponentModel namespace by referencing it in the Window attributes. The BackgroundWorker is given a key, has its properties set, and has delegates attached to its events, all in the inline XAML.

The window also declares a System.Windows.Controls.ProgressBar and a System.Windows.Controls.Button. When the Button is clicked, the BackgroundWorker that was declared in the Resources is started asynchronously and reports its progress to the ProgressBar.

In the window's constructor, the FrameworkElement.FindResource method is called to retrieve a reference to the BackgroundWorker declared in the XAML. This reference is then used to start and cancel the BackgroundWorker when the Button is clicked.

The XAML for the window is as follows:

<Window x:Class="Recipe_08_09.Window1"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   xmlns:ComponentModel="clr-namespace:System.ComponentModel;assembly=System"
   Title="WPF Recipes 8_09" Height="100" Width="200">

    <Window.Resources>
        <ComponentModel:BackgroundWorker
            x:Key="backgroundWorker"
            WorkerReportsProgress="True"
            WorkerSupportsCancellation="True"
            DoWork="BackgroundWorker_DoWork"
            RunWorkerCompleted="BackgroundWorker_RunWorkerCompleted"
            ProgressChanged="BackgroundWorker_ProgressChanged"
            />
    </Window.Resources>

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>

        <ProgressBar
            Name="progressBar" Margin="4"/>
<Button
            Name="button"
            Grid.Row="1"
            Click="button_Click"
            HorizontalAlignment="Center"
            Margin="4"
            Width="60">
            Start
        </Button>
    </Grid>
</Window>

The code-behind for the window is as follows:

using System.ComponentModel;
using System.Threading;
using System.Windows;
using System.Windows.Input;

namespace Recipe_08_09
{
    public partial class Window1 : Window
    {
        private readonly BackgroundWorker worker;

        public Window1()
        {
            InitializeComponent();

            // Retrieve a reference to the
            // BackgroundWorker declared in the XAML
            worker = this.FindResource("backgroundWorker")
                     as BackgroundWorker;
       }

       private void button_Click(object sender, RoutedEventArgs e)
       {
           if(!worker.IsBusy)
           {
               this.Cursor = Cursors.Wait;

               worker.RunWorkerAsync();
               button.Content = "Cancel";
           }
           else
           {
              worker.CancelAsync();
           }
        }
private void BackgroundWorker_DoWork(
                       object sender,
                       System.ComponentModel.DoWorkEventArgs e)
      {
         for(int i = 1; i <= 100; i++)
         {
             if(worker.CancellationPending)
                break;

             Thread.Sleep(100);
             worker.ReportProgress(i);
         }
      }

      private void BackgroundWorker_RunWorkerCompleted(
                       object sender,
                       System.ComponentModel.RunWorkerCompletedEventArgs e)
      {
         this.Cursor = Cursors.Arrow;

         if(e.Error != null)
             MessageBox.Show(e.Error.Message);

         button.Content = "Start";
      }

      private void BackgroundWorker_ProgressChanged(
                      object sender,
                      System.ComponentModel.ProgressChangedEventArgs e)
      {
         progressBar.Value = e.ProgressPercentage;
      }
   }
}

Update the UI Asynchronously on a Timer

Problem

You need to execute a method that updates the UI on a timer.

Solution

Use a System.Windows.Threading.DispatcherTimer that utilizes the Dispatcher object of the thread that creates it and raises the Tick event on the Dispatcher queue at a specified time and at a specified priority.

How It Works

There are three timer classes in the .NET Framework Base Class Library (BCL): System.Threading.Timer, System.Timers.Timer, and System.Windows.Forms.Timer. Each of these timers is different, but essentially they all support specifying a time interval between ticks, as well as the code to run when this interval is reached. WPF introduces a new timer that is also designed specifically to utilize the threading model in WPF. It ensures that when the Tick event handler is raised, the delegate is added to the dispatcher queue of the thread that created it. So if the DispatcherTimer is created on the UI thread, the delegate added to the Tick event handler will automatically be executed on the UI thread.

The DispatcherTimer class has an overloaded constructor that allows you to specify a DispatcherPriority parameter. If this parameter is omitted, the default is Normal. It can be used to specify that the tick event should be fired in the background and not interfere with higher-priority events such as those to do with loading, binding, and rendering.

The Code

The following example creates a window with a System.Windows.Controls.Button and a System.Windows.Controls.TextBlock. When the Button is clicked, there is logic in the code-behind to initialize an instance of the DispatcherTimer class and to give it an interval of one second, and there is a Tick event handler that displays the current second in the TextBlock control. Because the DispatcherTimer is created on UI thread, there is no need for any special code in the Tick event handler to ensure it is being executed on the UI thread.

The XAML for the window is as follows:

<Window x:Class="Recipe_08_10.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="WPF Recipes 8_10" Height="100" Width="300">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>

        <Button
            x:Name="button"
            Click="Button_Click">Start Timer</Button>

        <TextBlock
            x:Name="txtStatus"
            Grid.Column="1"
            Margin="4"
            VerticalAlignment="Center"
            HorizontalAlignment="Center">
        </TextBlock>
    </Grid>
</Window>

The code-behind for the window is as follows:

using System;
using System.Windows;
using System.Windows.Threading;

namespace Recipe_08_10
{
   public partial class Window1 : Window
   {
      private DispatcherTimer timer;

      public Window1()
      {
         InitializeComponent();
      }

      private void Button_Click(object sender, RoutedEventArgs e)
      {
         if(timer == null || !timer.IsEnabled)
         {
            timer = new DispatcherTimer();

            timer.Interval = TimeSpan.FromMilliseconds(1000);
            timer.Tick += new EventHandler(timer_Tick);

            timer.Start();
            button.Content = "Stop Timer";
         }
         else
         {
            timer.Stop();
            button.Content = "Start Timer";
         }
      }

      private void timer_Tick(object sender, EventArgs e)
      {
         txtStatus.Text = DateTime.Now.Second.ToString();
      }
   }
}

Show a Continuous Animation During an Asynchronous Process

Problem

You need to show a continuous animation whilst processing an operation on a background thread.

Solution

Create a System.Windows.Media.Animation.Storyboard to animate some visual elements, whilst processing an operation asynchronously using a System.ComponentModel.BackgroundWorker object. Set the RepeatBehavior property of the Storyboard to System.Windows.Threading. DispatcherTimer.Forever. Specify that the Storyboard should be interactively controllable by calling its Begin method with a value of True for the isControllable parameter. In the BackgroundWorker's RunWorkerCompleted event handler, call the Storyboard's Stop method.

How It Works

Setting the RepeatBehavior property of the Storyboard class to Forever ensures that the animation runs continuously until explicitly stopped. It can then be started when the asynchronous process is begun and stopped when it completes.

Warning

If the Begin method of the Storyboard is not called with the isControllable parameter explicitly set to True, then calling the Stop method programmatically will not terminate the animation.

The Code

The following example demonstrates a window that displays a System.Windows.Shapes.Ellipse and a System.Windows.Controls.Button. A Storyboard is declared in the Resources collection of the Window, which changes the color of the Ellipse from gray to green when run.

In the code-behind for Button's Click event, the Storyboard is started, and a BackgroundWorker object is run asynchronously. It simulates a background process by calling System.Threading.Thread.Sleep a number of times. In the code for the RunWorkerCompleted event, the Storyboard is stopped.

The XAML for the window is as follows:

<Window x:Class="Recipe_08_11.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="WPF Recipes 8_11" Height="220" Width="180">
    <Window.Resources>
        <Storyboard x:Key="PulseStoryboard" AutoReverse="True" >
<ColorAnimationUsingKeyFrames BeginTime="00:00:00"
                    Storyboard.TargetName="ellipse"
                    Storyboard.TargetProperty=
         "(Shape.Fill).(GradientBrush.GradientStops)[0].(GradientStop.Color)">
             <SplineColorKeyFrame KeyTime="00:00:00.5000000" Value="Lime"/>
         </ColorAnimationUsingKeyFrames>
         <ColorAnimationUsingKeyFrames BeginTime="00:00:00"
                   Storyboard.TargetName="ellipse"
                   Storyboard.TargetProperty=
         "(Shape.Fill).(GradientBrush.GradientStops)[1].(GradientStop.Color)">
             <SplineColorKeyFrame KeyTime="00:00:00.5000000" Value="Green"/>
           </ColorAnimationUsingKeyFrames>
       </Storyboard>
   </Window.Resources>

   <Grid x:Name="LayoutRoot" >

      <Grid.RowDefinitions>
          <RowDefinition Height="*"/>
          <RowDefinition Height="60" />
      </Grid.RowDefinitions>

      <Ellipse
       Width="100"
       Height="100"
       Margin="10"
       Stroke="{x:Null}" x:Name="ellipse">
           <Ellipse.Fill>
               <RadialGradientBrush GradientOrigin="0.25,0.25">
                   <GradientStop Offset="0" Color="#A8FFFFFF"/>
                   <GradientStop Offset="1" Color="#FF9C9F97"/>
               </RadialGradientBrush>
           </Ellipse.Fill>
       </Ellipse>

       <Button Margin="10"
               Content="Start"
               Grid.Row="1"
               x:Name="button"
               Click="button_Click"/>
    </Grid>
</Window>

The code-behind for the window is as follows:

using System.Windows;
using System.Threading;
using System.ComponentModel;
using System.Windows.Media.Animation;

namespace Recipe_08_11
{
    public partial class Window1 : Window
    {
        private Storyboard pulseStoryboard;
        private BackgroundWorker worker;

        public Window1()
        {
            InitializeComponent();

            pulseStoryboard
                = (Storyboard) this.Resources["PulseStoryboard"];

            // Set the animation to repeat indefinitely
            pulseStoryboard.RepeatBehavior = RepeatBehavior.Forever;

            // Create a Background Worker
            worker = new BackgroundWorker();

            worker.DoWork +=
                new DoWorkEventHandler(worker_DoWork);
            worker.RunWorkerCompleted +=
                new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
         }

         private void button_Click(
             object sender, RoutedEventArgs e)
         {
             // Begin the animation
             pulseStoryboard.Begin(this, true);

             // Start the Background Worker
             worker.RunWorkerAsync();

             button.IsEnabled = false;
          }
private void worker_RunWorkerCompleted(
         object sender, RunWorkerCompletedEventArgs e)
      {
         button.IsEnabled = true;

         // Stop the animation
         pulseStoryboard.Stop(this);
      }

      private void worker_DoWork(
          object sender, DoWorkEventArgs e)
      {
          for(int i = 1; i <= 50; i++)
          {
              Thread.Sleep(50);
          }
       }
       }
}

Figure 8-9 shows the resulting window.

Showing a continuous animation during an asynchronous process

Figure 8-9. Showing a continuous animation during an asynchronous process

Show a ProgressBar While Processing on a Background Thread

Problem

You need to show a System.Windows.Controls.ProgressBar whilst processing an operation on a background thread.

Solution

Create a System.ComponentModel.BackgroundWorker object, set its WorkerReportsProgress property to True, and add an event handler to its ProgressChanged event. Call the ReportProgress method of the BackgroundWorker whilst processing the operation on the background thread, and in the code for this ProgressChanged event handler, update the Value property of a ProgressBar.

How It Works

The BackgroundWorker class has a Boolean property called WorkerReportsProgress, which indicates whether the BackgroundWorker can report progress updates. It is set to False by default. When this is set to True, calling the ReportProgress method will raise the ProgressChanged event.

The ReportProgress method takes an integer parameter specifying the percentage of progress completed by the BackgroundWorker. This parameter is passed to the ProgressChanged event handler via the ProgressPercentage property of the System.ComponentModel. ProgressChangedEventArgs class.

The ProgressBar control sets the default value for its Maximum property to 100, which lends itself perfectly and automatically to receive the ProgressPercentage as its Value property.

The Code

The following example demonstrates a window that declares a ProgressBar control and a Button. An instance of the BackgroundWorker class is created in the window's constructor, and its WorkerReportsProgress property is set to True. Event handlers are added to the ProgressBar's DoWork, RunWorkerCompleted, and ProgressChanged events.

In the code-behind for the button's Click event, the BackgroundWorker is started asynchronously. This executes the worker_DoWork method on a background thread, which loops through 1 to 100, simulating some processing using Thread.Sleep and calling the ReportProgress method of the BackgroundWorker.

Each time ReportProgress is called, the worker_ProgressChanged method is executed, which sets the Value property of the ProgressBar to the ProgressPercentage property of the ProgressChangedEventArgs.

The XAML for the window is as follows:

<Window x:Class="Recipe_08_12.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="WPF Recipes 8_12" Height="100" Width="200">
    <Grid>

        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>

        <ProgressBar
            Name="progressBar" Margin="4"/>
<Button
            Name="button"
            Grid.Row="1"
            Click="button_Click"
            HorizontalAlignment="Center"
            Margin="4"
            Width="60">
            Start
        </Button>
    </Grid>
</Window>

The code-behind for the window is as follows:

using System.ComponentModel;
using System.Threading;
using System.Windows;
using System.Windows.Input;

namespace Recipe_08_12
{
    public partial class Window1 : Window
    {
        private BackgroundWorker worker;

        public Window1()
        {
            InitializeComponent();

            // Create a Background Worker
            worker = new BackgroundWorker();

            // Enable progress reporting
            worker.WorkerReportsProgress = true;

            // Attach the event handlers
            worker.DoWork += new DoWorkEventHandler(worker_DoWork);
            worker.RunWorkerCompleted +=
                      new RunWorkerCompletedEventHandler(
                                 worker_RunWorkerCompleted);
            worker.ProgressChanged += worker_ProgressChanged;
         }

         private void button_Click(object sender, RoutedEventArgs e)
         {
            // Start the Background Worker
            worker.RunWorkerAsync();
this.Cursor = Cursors.Wait;
         button.IsEnabled = false;
      }

      private void worker_RunWorkerCompleted(
          object sender, RunWorkerCompletedEventArgs e)
      {
          this.Cursor = Cursors.Arrow;

          if(e.Error != null)
              MessageBox.Show(e.Error.Message);

          button.IsEnabled = true;
      }

      private void worker_DoWork(
          object sender, DoWorkEventArgs e)
      {
          for(int i = 1; i <= 100; i++)
          {
             // Simulate some processing by sleeping
             Thread.Sleep(100);

             // Call report progress to fire the ProgressChanged event
             worker.ReportProgress(i);
          }
      }

      private void worker_ProgressChanged(
          object sender, ProgressChangedEventArgs e)
      {
          // Update the progress bar
          progressBar.Value = e.ProgressPercentage;
      }
   }
}

Figure 8-10 shows the resulting window.

Showing a progress bar while processing on a background thread

Figure 8-10. Showing a progress bar while processing on a background thread

Show a Cancellable ProgressBar While Processing on a Background Thread

Problem

You need to show a System.Windows.Controls.ProgressBar whilst processing an operation on a background thread and allow the user to cancel the operation during processing.

Solution

Create a System.ComponentModel.BackgroundWorker object, and set its WorkerSupportsCancellation property to True. Add a System.Windows.Controls.Button to your window, and call the CancelAsync method of the BackgroundWorker in its Click event handler.

How It Works

The BackgroundWorker class has a Boolean property called WorkerSupportsCancellation, which, when set to True, allows the CancelAsync method to interrupt the background operation. It is set to False by default.

In the RunWorkerCompleted event handler, you can use the Cancelled property of the RunWorkerCompletedEventArgs to check whether the BackgroundWorker was cancelled.

The Code

The following example demonstrates a window that declares a ProgressBar control and a Button. An instance of the BackgroundWorker class is created in the window's constructor, and its WorkerSupportsCancellation property is set to True.

When the Button is clicked, the code in the Click handler runs the BackgroundWorker asynchronously and changes the text of the Button from Start to Cancel. If it is clicked again, the IsBusy property of the BackgroundWorker returns True, and the code calls the CancelAsync method to cancel the operation.

In the RunWorkerCompleted event handler, a System.Windows.MessageBox is shown if the Cancelled property of the RunWorkerCompletedEventArgs parameter is True.

The XAML for the window is as follows:

<Window x:Class="Recipe_08_13.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="WPF Recipes 8_13" Height="100" Width="200">
    <Grid>

        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>
<ProgressBar
            Name="progressBar" Margin="4"/>

        <Button
            Name="button"
            Grid.Row="1"
            Click="button_Click"
            HorizontalAlignment="Center"
            Margin="4"
            Width="60">
            Start
        </Button>
    </Grid>
</Window>

The code-behind for the window is as follows:

using System.ComponentModel;
using System.Threading;
using System.Windows;
using System.Windows.Input;

namespace Recipe_08_13
{
   public partial class Window1 : Window
   {
      private BackgroundWorker worker;

      public Window1()
      {
         InitializeComponent();

         // Create a Background Worker
         worker = new BackgroundWorker();
         worker.WorkerReportsProgress = true;

         // Enable support for cancellation
         worker.WorkerSupportsCancellation = true;

         // Attach the event handlers
         worker.DoWork +=
            new DoWorkEventHandler(worker_DoWork);
         worker.RunWorkerCompleted +=
            new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
         worker.ProgressChanged +=
            worker_ProgressChanged;
      }
private void button_Click(
         object sender, RoutedEventArgs e)
      {
         if(!worker.IsBusy)
         {
             this.Cursor = Cursors.Wait;

             // Start the Background Worker
             worker.RunWorkerAsync();
             button.Content = "Cancel";
         }
         else
         {
             // Cancel the Background Worker
             worker.CancelAsync();
         }
      }

      private void worker_RunWorkerCompleted(
          object sender, RunWorkerCompletedEventArgs e)
      {
          this.Cursor = Cursors.Arrow;

          if(e.Cancelled)
          {
              // The user cancelled the operation
              MessageBox.Show("Operation was cancelled");
          }
          else if(e.Error != null)
          {
              MessageBox.Show(e.Error.Message);
          }

          button.Content = "Start";
      }

      private void worker_DoWork(
          object sender, DoWorkEventArgs e)
      {
          for(int i = 1; i <= 100; i++)
          {
              // Check if the BackgroundWorker
              // has been cancelled
if(worker.CancellationPending)
            {
               // Set the Cancel property
               e.Cancel = true;
               return;
            }

            // Simulate some processing by sleeping
            Thread.Sleep(100);
            worker.ReportProgress(i);
         }
      }

      private void worker_ProgressChanged(
          object sender, ProgressChangedEventArgs e)
      {
          progressBar.Value = e.ProgressPercentage;
      }
   }
}

Figure 8-11 shows the resulting window.

Showing a cancellable progress bar while processing on a background thread

Figure 8-11. Showing a cancellable progress bar while processing on a background thread

Show a Continuous Progress Bar While Processing on a Background Thread

Problem

You need to show a continuous System.Windows.Controls.ProgressBar whilst processing an operation on a background thread, until the operation either completes or is cancelled by the user.

Solution

Create a System.ComponentModel.BackgroundWorker object, and add a ProgressBar to your window. Before calling the RunWorkerAsync method to start the BackgroundWorker, set the IsIndeterminate property of your ProgressBar to True. When the operation has completed, set the IsIndeterminate property to False again.

How It Works

The ProgressBar class has a Boolean property called IsIndeterminate. When this property is True, the ProgressBar animates a few bars moving across the ProgressBar in a continuous manner and ignores the Value property. It continues indefinitely until IsIndeterminate is set back to False again.

The Code

The following example demonstrates a window that declares a ProgressBar control and a Button. When the Button is clicked, a BackgroundWorker object is run that simulates a long-running operation by counting from 1 to 500 and calling the System.Threading.Thread.Sleep method in between each number. When the BackgroundWorker is run, the IsIndeterminate property of the ProgressBar is set to True, which starts its animation. When the RunWorkerCompleted event is raised, IsIndeterminate is set back to False, and the ProgressBar stops.

The XAML for the window is as follows:

<Window x:Class="Recipe_08_14.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     Title="WPF Recipes 8_14" Height="100" Width="200">
    <Grid>

        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>

        <ProgressBar
           Name="progressBar" Margin="4"/>

        <Button
            Name="button"
            Grid.Row="1"
            Click="button_Click"
            HorizontalAlignment="Center"
            Margin="4"
            Width="60">
            Start
        </Button>
    </Grid>
</Window>

The code-behind for the window is as follows:

using System.ComponentModel;
using System.Threading;
using System.Windows;
using System.Windows.Input;
namespace Recipe_08_14
{
   public partial class Window1 : Window
   {
      private BackgroundWorker worker;

      public Window1()
      {
          InitializeComponent();

          // Create a Background Worker
          worker = new BackgroundWorker();

          // Enable support for cancellation
          worker.WorkerSupportsCancellation = true;

          worker.DoWork +=
              new DoWorkEventHandler(worker_DoWork);
         worker.RunWorkerCompleted +=
            new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
      }

      private void button_Click(
          object sender, RoutedEventArgs e)
      {
          if (!worker.IsBusy)
          {
              this.Cursor = Cursors.Wait;

              // Set the ProgressBar's IsInderterminate
              // property to true to start the progress indicator
              progressBar.IsIndeterminate = true;
              button.Content = "Cancel";

              // Start the Background Worker
              worker.RunWorkerAsync();
          }
          else
          {
              worker.CancelAsync();
          }
       }

       private void worker_RunWorkerCompleted(
           object sender, RunWorkerCompletedEventArgs e)
       {
           this.Cursor = Cursors.Arrow;
if (e.Error != null)
         {
            MessageBox.Show(e.Error.Message);
         }

         button.Content = "Start";

         // Reset the ProgressBar's IsInderterminate
         // property to false to stop the progress indicator
         progressBar.IsIndeterminate = false;
      }

      private void worker_DoWork(
          object sender, DoWorkEventArgs e)
      {
          for (int i = 1; i <= 500; i++)
          {
              if (worker.CancellationPending)
                  break;

              Thread.Sleep(50);
          }
      }
   }
}

Figure 8-12 shows the resulting window.

Showing a continuous progress bar while processing on a background thread

Figure 8-12. Showing a continuous progress bar while processing on a background thread

Implement Application.DoEvents in WPF

Problem

You need to implement the Application.DoEvents method in Windows Forms in order to force the UI thread to process rendering events during a long-running process.

Solution

Use the Invoke method of the current System.Windows.Threading.Dispatcher to queue a placeholder delegate with a System.Windows.Threading.DispatcherPriority of Background.

How It Works

Whereas the BeginInvoke method of Dispatcher is asynchronous, the Invoke method is synchronous. This means that the call to Invoke will not return until the event has been executed. If a dummy event is queued with a Background priority, then it will not be executed until all higher-priority operations have completed.

Warning

Using Application.DoEvents is not always advisable, and it must be used with caution. For example, consider the case where a call to DoEvents is followed by code that accesses a window's Controls collection. It is possible that one of the events processed during the DoEvents may have been the user clicking the window's Close button. This would result in a call to the window's Dispose method, which in turn would clear the Controls collection. If the code following the call to DoEvents makes assumptions about the existence of controls in the window, this would cause an exception.

The Code

The following example displays a window containing two System.Windows.Controls.Button controls and a System.Windows.Controls.ListBox control. In the Click events for the buttons, a method called LoadNumbers is called that loads 10,000 numbers into the ListBox. When the button on the left is clicked, the window freezes whilst the numbers are loaded. It cannot receive any other events, and the numbers don't get displayed until all of them are added to the list.

However, when the button on the right is clicked, a method called DoEvents is called after each number is added to the ListBox. This ensures that the ListBox is updated between numbers and the window can still receive other input events.

In the code for DoEvents, an empty delegate is placed on the dispatcher queue using the Invoke method and given a DispatcherPriority of Background. This ensures the DoEvents method does not return until all the rendering events in the queue are processed.

The XAML for the window is as follows:

<Window x:Class="Recipe_08_15.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="WPF Recipes 8_15" Height="200" Width="300">
    <Grid>

        <Grid.RowDefinitions>
            <RowDefinition Height="48"/>
            <RowDefinition/>
        </Grid.RowDefinitions>

        <StackPanel
            Orientation="Horizontal">
<Button x:Name="btnWithout"
                    Click="btnWithout_Click"
                    Margin="4">
                Without DoEvents
            </Button>

            <Button x:Name="btnWith"
                    Click="btnWith_Click"
                    Margin="4">
                With DoEvents
            </Button>

        </StackPanel>

        <ListBox x:Name="listBox"
                 Grid.Row="1" />
    </Grid>
</Window>

The code for the window is as follows:

using System.Windows;
using System.Windows.Threading;

namespace Recipe_08_15
{
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
        }

        private void btnWithout_Click(
            object sender, RoutedEventArgs e)
        {
            LoadNumbers(false);
        }

        private void btnWith_Click(
            object sender, RoutedEventArgs e)
        {
            LoadNumbers(true);
        }
private void LoadNumbers(bool callDoEvents)
      {
         listBox.Items.Clear();

         btnWithout.IsEnabled = false;
         btnWith.IsEnabled = false;

         // Load ten thousand numbers into a listbox
         for(int i = 1; i <= 10000; i++)
         {
             listBox.Items.Add("Number " + i.ToString());

             // Optionally call DoEvents
             if(callDoEvents)
                 DoEvents();
         }

         btnWithout.IsEnabled = true;
         btnWith.IsEnabled = true;
      }

      /// <summary>
      /// Process all messages in the current dispatcher queue
      /// </summary>
      public static void DoEvents()
      {
          // Add an empty delegate to the
          // current thread's Dispatcher, and
          // invoke it synchronously but using a
          // a Background priority.
          // It won't return until all higher-priority
          // events in the queue are processed.
          Dispatcher.CurrentDispatcher.Invoke(
              DispatcherPriority.Background,
              new EmptyDelegate(
                  delegate{}));
      }

      private delegate void EmptyDelegate();
   }
}

Figure 8-13 shows the resulting window.

Loading a ListBox of numbers and calling DoEvents between each number

Figure 8-13. Loading a ListBox of numbers and calling DoEvents between each number

Create a Separate Thread for Each Window in a Multiwindow Application

Problem

You have an application with multiple windows and need each window to have its own UI thread so that long-running operations in one window do not interfere with the rendering and operations in the other windows.

Solution

Instead of initializing and showing a new window from the UI thread of the main window, create a new System.Threading.Thread, and then create and show a new window from the starting point of this new thread.

How It Works

If you create and show child windows from the UI thread of the main window in an application with multiple windows, then each window shares the same thread and System.Windows.Threading.Dispatcher object. This is perfectly fine for many applications, but it means that each window shares the same event queue for all its UI events. This means that if one window initiates a long-running operation, none of the other windows can receive their events until it is finished.

When you create a new thread, WPF automatically creates a new Dispatcher object to manage it. So if you create each new window from a new thread, they will get their own Dispatcher object and events in the Dispatcher queue because one window will not affect the processing of events in the other windows.

To create a new thread, instantiate a new Thread object, and specify a System.Threading.ThreadStart delegate as its starting point. In this method, create and show a new window and then call Dispatcher.Run to start processing events on the thread's Dispatcher queue.

The Code

The following example demonstrates a window that functions as the main window of a multiwindow application. There is a System.Windows.Controls.Button with the text New Window. When this is clicked, it launches a new window. If the Create Separate Threads System.Windows.Controls.CheckBox is checked, then it creates a new thread to spawn the new window. If it is not checked, then it creates it directly from the UI thread.

The new child windows contain a Button marked Start that, when clicked, executes a method that searches for prime numbers. To simulate a long-running blocking process, there is a CheckBox marked Sleep Between Numbers. If this is checked, the child window sleeps for 200 milliseconds between checking for each number.

To see the effects of creating each window on a separate thread, first create a few child windows with the Create Separate Threads CheckBox unchecked. If you then check the Sleep Between Numbers option on one of the child numbers, you will see that the simulated longrunning process affects the processing on all the child windows. By slowing down the operation on one child window, it slows down the processing of events on all the other windows too.

However, by checking the Create Separate Threads CheckBox before creating new windows, you will see that even when we slow down the operation on one or more of the child windows, the other ones are not affected. They still execute the search for prime numbers and process their rendering and painting events, just as quickly as if they were the only window.

The XAML for the main window is as follows:

<Window x:Class="Recipe_08_16.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="WPF Recipes 8_16"
    Height="108"
    Width="230" >
    <StackPanel Orientation="Vertical">

        <StackPanel Orientation="Horizontal">
            <Button Content="New Window"
                Click="btnNewWindow_Click"
                Margin="4"
                Height="40"
                Width="104"/>
            <Button Content="Close Windows"
                Click="btnCloseWindows_Click"
                Margin="4"
                Height="40"
                Width="104"/>
        </StackPanel>

        <CheckBox x:Name="chkCreateThread"
                  Content="Create separate threads"
                  Margin="4"/>
    </StackPanel>
</Window>

The code-behind for the main window is as follows:

using System.Windows;
using System.Threading;
using System.Collections.Generic;

using Recipe_08_16;


namespace Recipe_08_16
{
    public partial class MainWindow : Window
    {
        private List<ChildWindow> windows = new List<ChildWindow>();
        private List<Thread> threads = new List<Thread>();

        public MainWindow()
        {
            InitializeComponent();
        }

        private void btnNewWindow_Click(object sender, RoutedEventArgs e)
        {
            if(chkCreateThread.IsChecked.Value)
            {
                // Create a new Thread
                // which will create a new window
                Thread newWindowThread =
                          new Thread(
                               new ThreadStart(
                                    ThreadStartingPoint));
                 newWindowThread.SetApartmentState(ApartmentState.STA);
                 newWindowThread.IsBackground = true;
                 newWindowThread.Start();

                 threads.Add(newWindowThread);
             }
             else
             {
                 // Create a new window
                 ChildWindow window = new ChildWindow();
                 window.Show();

                  windows.Add(window);
             }
          }
private void ThreadStartingPoint()
      {
         // Create a new window
         ChildWindow window = new ChildWindow();
         window.Show();

         // Start the new window's Dispatcher
         System.Windows.Threading.Dispatcher.Run();
      }

      private void btnCloseWindows_Click(object sender, RoutedEventArgs e)
      {
          foreach(ChildWindow window in windows)
          {
              window.Stop();
              window.Close();
              }
          windows.Clear();

          foreach(Thread thread in threads)
          {
              thread.Abort();
          }
          threads.Clear();
       }
    }
}

The XAML for the child window is as follows:

<Window x:Class="Recipe_08_16.ChildWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   Title="Child Window" Width="228" Height="128" >
    <StackPanel Orientation="Vertical">
        <Button
            Click="StartStop_Click"
            Name="btnStartStop"
            Margin="4"
            Height="34">Start</Button>
        <CheckBox x:Name="chkSleep"
                  Margin="4"
                  Content="Sleep between numbers"/>
<StackPanel Orientation="Horizontal">
            <TextBlock Margin="4">Biggest Prime Found:</TextBlock>
            <TextBlock Name="txtBiggestPrime" Margin="4" />
        </StackPanel>
    </StackPanel>
</Window>

The code-behind for the child window is as follows:

using System;
using System.Windows;
using System.Windows.Threading;

namespace Recipe_08_16
{
    public partial class ChildWindow : Window
    {
        private bool continueCalculating = false;

        private PrimeNumberHelper primeNumberHelper
            = new PrimeNumberHelper();

        public ChildWindow()
            : base()
        {
            InitializeComponent();
        }

        private void StartStop_Click(object sender, RoutedEventArgs e)
        {
            if(continueCalculating)
                Stop();
            else
                Start();
        }

        public void Start()
        {
            continueCalculating = true;
            btnStartStop.Content = "Stop";

            // Execute the CheckPrimeNumber method on
            // the current Dispatcher queue
            this.Dispatcher.BeginInvoke(
                DispatcherPriority.Normal,
                new Action<int>(CheckPrimeNumber), 3);
        }
public void Stop()
      {
         continueCalculating = false;
         btnStartStop.Content = "Start";

         // Add an empty delegate to the
         // current thread's Dispatcher, and
         // invoke it synchronously but using a
         // a Background priority.
         // This ensures the Stop method won't return
         // until the CheckPrimeNumber method has completed.
         Dispatcher.CurrentDispatcher.Invoke(
             DispatcherPriority.Background,
             new EmptyDelegate(
                 delegate{}));
      }

      public void CheckPrimeNumber(int current)
      {
          if(primeNumberHelper.IsPrime(current))
          {
              txtBiggestPrime.Text = current.ToString();
          }

          if(continueCalculating)
         {
              // Execute the CheckPrimeNumber method
              // again, using a lower DispatcherPriority
              this.Dispatcher.BeginInvoke(
                  DispatcherPriority.SystemIdle,
                  new Action<int>(CheckPrimeNumber), current + 2);

              if(chkSleep.IsChecked.Value)
                  System.Threading.Thread.Sleep(200);
         }
      }

      private delegate void EmptyDelegate();
   }
}

Figure 8-14 shows the resulting windows. The child window with the Sleep Between Numbers option checked takes much longer to find the prime numbers. However, the other windows are not affected.

Creating a separate thread for each window in an application with multiple windows

Figure 8-14. Creating a separate thread for each window in an application with multiple windows

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset