WPF MultiThreading

In this article WPF MultiThreading, I will demonstrate how to create multi threaded WPF application. You will learn about :

  1. WPF Threading Model
  2. Creating separate Thread to offload time consuming tasks to be done asynchronously
  3. Using WPF provided classes like BackgroundWorker instead of creating separate thread manually to offload time consuming tasks to be done asynchronously
  4. Updating the UI elements from another thread using Dispatcher class
  5. Using DispatcherTimer

I will demonstrate by creating a sample C# WPF application and creating a Thread to perform time consuming tasks asynchronously and access UI elements and also by using BackgroundWorker and Dispatcher classes. But before that, let’s go through some concepts first.

Source Code : Complete source code can be downloaded from the following GitHub repo :

https://github.com/sudipta-chaudhari/WPFMultiThreading

WPF works in Single Threaded Apartment (STA) model and works with 2 threads –

  • Rendering Thread – hidden and runs in background
  • UI Thread – receives user input, handles events and paints screen

Simple WPF applications use single UI thread. However, for time consuming tasks which can block the UI causing it to freeze, need to use separate threads arises. WPF provided classes to execute time consuming tasks in background thread like BackgroundWorker and update the UI using Dispatcher class.

When creating a multithreaded WPF application, if you create a thread and try to access WPF’s UI elements inside the thread or even the BackgroundWorker, you will receive the following error – “The calling thread cannot access this object because a different thread owns it”. This is because threads can’t access objects created in STA. To overcome this and update UI elements from the thread, you need to use Dispatcher class.

[NOTE]: You can continue to use Thread class/TPL library instead of using BackgroundWorker to achieve the same result

In this article, I will describe how to offload time consuming tasks by use of the following: –

  1. Thread class/TPL Library
  2. BackgroundWorker
  3. Dispatcher
  4. DispatcherTimer

This followed by complete sample application with source code.

(1) Thread class / TPL Library

I believe you are comfortable in creation and use of Threads and using TPL library.

(2) BackgroundWorker

BackgroundWorker class exists in System.ComponentModel namespace. It executes an operation on a separate thread.

In this article, I will focus on the following three events: –

  • DoWork – Executes in background thread
  • ProgressChanged – Executes when ReportProgress method is called.
  • RunWorkerCompleted – Executes when the background operation has completed, has been cancelled, or has raised an exception

For detailed description of all properties, events and methods of Dispatcher class, please visit MSDN – https://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker(v=vs.110).aspx

(3) Dispatcher

Dispatcher class exists in System.Windows.Threading namespace. UI Thread queues work inside Dispatcher object. It doesn’t execute an operation on a separate thread. It manages queue of work items in FIFO manner i.e. queues message calls inside Dispatcher object.

When WPF application is started, new Dispatcher object is created automatically and it’s Run() method is called. Run() method is used for initializing message queue.

WPF works internally with Dispatcher object and we don’t need to work with Dispatcher when we are working on the UI thread.

When we create a new thread for offloading the work and want to update the UI from the other thread then we must need Dispatcher. Only Dispatcher can update the objects in the UI from non-UI thread.

In this article, I will focus on the following event

  • Invoke – takes an Action or Delegate and execute the method synchronously

For detailed description of all properties, events and methods of Dispatcher class, please visit MSDN – https://msdn.microsoft.com/en-us/library/system.windows.threading.dispatcher(v=vs.110).aspx

Let’s now proceed to build the WPF application.

(4) DispatcherTimer

DispatcherTimer class exists in System. Windows.Threading namespace. It is a timer that is integrated into the Dispatcher queue which is processed at a specified interval of time and at a specified priority. It works like the traditional Timer class present in System.Timers namespace, has a Tick event.

For detailed description of all properties, events and methods of Dispatcher class, please visit MSDN – https://msdn.microsoft.com/en-us/library/system.windows.threading.dispatchertimer(v=vs.110).aspx

Sample WPF Application

The XAML file has a simple design having a StackPanel and controls places inside it. I have used a ProgressBar whose progress will be update synchronously and asynchronously.

  • First button uses BackgroundWorker class to update the progress bar asynchronously – Works fines
  • Second button uses System.Timer class and tries to update the ProgressBar – Raises an exception: “The calling thread cannot access this object because a different thread owns it”
  • Third button uses DispatcherTimer class to update the progress bar asynchronously – Works fine
  • Fourth button updates the progress bar synchronously – Works fine
  • Fifth button uses a thread and tries to update the progress bar asynchronously using a thread – Works fine

WPF Multithreading

Source Code

<Window x:Class="WPFMultiThreading.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WPFMultiThreading"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <StackPanel>
            <ProgressBar x:Name="pb" HorizontalAlignment="Center" Height="19" VerticalAlignment="Top" Width="470" Margin="10 50 10 10" />
            <Button x:Name="btnProgressBackgroundWorker" Content="Show Progress - Backgound Worker" HorizontalAlignment="Center" Margin="10 10 10 10" VerticalAlignment="Top" Width="212" Click="BtnProgressBackgroundWorker_Click"/>
            <Button x:Name="btnSystemTimer" Content="Show Progress - System.Timer" HorizontalAlignment="Center" Margin="10 10 10 10" VerticalAlignment="Top" Width="212" Click="btnSystemTimer_Click" />
            <Button x:Name="btnProgressDispatchTimer" Content="Show Progress - Dispatcher Timer" HorizontalAlignment="Center" Margin="190,10" VerticalAlignment="Top" Width="212" Click="BtnProgressDispatchTimer_Click"/>
            <Button x:Name="btnProgressSyncronous" Content="Show Progress - Synchronous" Click="BtnProgressSyncronous_Click" Margin="190,10" HorizontalAlignment="Center" VerticalAlignment="Center" Width="212"/>
            <Button x:Name="btnProgressAsyncronous" Content="Show Progress - Async Thread" Click="BtnProgressAsyncronous_Click" Margin="190,10" HorizontalAlignment="Center" VerticalAlignment="Center" Width="212"/>
            <Label x:Name="lblProgress" Content="" Margin="10 10 10 10" HorizontalAlignment="Center" VerticalAlignment="Top" Width="600"/>
        </StackPanel>
    </Grid>
</Window>
using System;
using System.ComponentModel;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Threading;

namespace WPFMultiThreading
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        BackgroundWorker bg;
        DispatcherTimer timer;
        System.Timers.Timer t;
        public MainWindow()
        {
            InitializeComponent();
            InitilaizeProgressBar();
            InitializeBackgroundWorker();
            InitializeSystemTimer();
            InitializeDispatchTimer();
        }
        private void InitilaizeProgressBar()
        {
            pb.Value = 0;
            pb.Maximum = 10;
        }
        private void InitializeBackgroundWorker()
        {
            bg = new BackgroundWorker();
            bg.DoWork += Bg_DoWork;
            bg.ProgressChanged += Bg_ProgressChanged;
            bg.RunWorkerCompleted += Bg_RunWorkerCompleted;
            bg.WorkerReportsProgress = true;
        }
        private void InitializeDispatchTimer()
        {
            timer = new DispatcherTimer
            {
                Interval = TimeSpan.FromSeconds(1)
            };
            timer.Tick += Timer_Tick;
        }

        private void InitializeSystemTimer()
        {
            t = new System.Timers.Timer(1000);
            t.Elapsed += T_Elapsed;
        }

        private void T_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            try
            {
                pb.Value += 1;
            }
            catch (Exception ex)
            {
                Dispatcher.Invoke(() =>
                {
                    pb.Value = 0;
                    lblProgress.Content = ex.Message;
                });
            }
            finally
            {
                t.Stop();
            }
        }

        private void Timer_Tick(object sender, EventArgs e)
        {
            pb.Value += 1;

            if (pb.Value == 10)
            {
                lblProgress.Content += "nUpdate progress using Dispatch Timer completed!";
                timer.Stop();
            }
        }

        private void Bg_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            lblProgress.Content += "nUpdate progress using BackgroundWorker completed!";
        }

        private void Bg_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            pb.Value += 1;
        }

        private void Bg_DoWork(object sender, DoWorkEventArgs e)
        {
            Dispatcher.Invoke(() =>
            {
                pb.Value = 0;
                lblProgress.Content = "Update progress using BackgroundWorker started!";
            });

            for (int i = 1; i <= 10; i++)
            {
                Thread.Sleep(1000); //do some time consuming task
                bg.ReportProgress(0);//raise background worker's progress changed event
            }

            //Dispatcher.Invoke(() =>
            //{
            //    lblProgress.Content = "nUpdate progress using BackgroundWorker completed!";
            //});
        }

        private void BtnProgressBackgroundWorker_Click(object sender, RoutedEventArgs e)
        {
            lblProgress.Content = string.Empty;
            //Using Bakground Worker -> Progress bar value updated at each step asynchronously.
            bg.RunWorkerAsync();
        }

        private void BtnProgressDispatchTimer_Click(object sender, RoutedEventArgs e)
        {
            Dispatcher.Invoke(() =>
            {
                pb.Value = 0;
                lblProgress.Content = "Update progress using Dispatch Timer started!";
            });
            timer.Start();
        }
        private void BtnProgressSyncronous_Click(object sender, RoutedEventArgs e)
        {
            pb.Value = 0;
            lblProgress.Content = "Update progress started synchronously!";

            for (int i = 1; i <= 10; i++)
            {
                Thread.Sleep(1000); //do some task
                pb.Value += 1;
            }
            lblProgress.Content += "Update progress finished synchronously!";
        }
        private void BtnProgressAsyncronous_Click(object sender, RoutedEventArgs e)
        {
            pb.Value = 0;
            lblProgress.Content = string.Empty;

            Task.Factory.StartNew(async() =>
            {
                await Dispatcher.Invoke(async() =>
                {
                    lblProgress.Content = "Update progress started asynchronously using thread!";
                    for (int i = 0; i < 10; i++)
                    {
                        await Task.Delay(1000);
                        pb.Value += 1;
                    }
                    lblProgress.Content += "nUpdate progress completed asynchronously using thread!";
                });
            });
        }

        private void btnSystemTimer_Click(object sender, RoutedEventArgs e)
        {
            t.Start();
        }
    }
}

Source Code Walkthrough

In the MainWindow class constructor, InitilaizeProgressBar() method initializes the progress bar properties – Value to 0 and Maximum to 10.

Next method in the constructor is InitializeBackgroundWorker() which creates a new object of BackgroundWorker class. It also creates the events – DoWork (Bg_DoWork), ProgressChanged (Bg_ProgressChanged) and RunWorkerCompleted (Bg_RunWorkerCompleted).

Bg_DoWork – Call’s the Dispatcher’s Invoke() method which sets ProgressBar and Label values. These UI element values can’t be set without the Dispatcher. Next in the for loop, Sleep() method of Thread class is called to simulate a long running process. Also, ReportProgress() method of BackgroundWorker class is called to raise the ProgressChanged event.

Bg_ProgressChanged – Increments the ProgressBar value in steps of 1.

Bg_RunWorkerCompleted – Updates the Label content

The first button’s click event handler calls the RunWorkerAsync() method of the BackgroundWorker object to start it. The ProgressBar starts to increment every second and completes after 10 seconds.

WPF Multithreading BackgroundWorker

Next method InitializeSystemTimer() creates a new object of System.Timers.Timer class and sets the timer interval to tick every second. It also registers the Elapsed event (T_Elapsed)

T_Elapsed – Tries to update the ProgressBar value by 1. Since the UI element cannot be accessed, catch block uses Dispatcher object’s Invoke() method to reset the ProgressBar value to 0 Label content to the error message.

Finally block stops the timer.

The second button’s click event handler calls the timer object’s Start() method. An error message is displayed on the label – The calling thread cannot access this object because a different thread owns it.

WPF Multithreading System.Timer

Next method InitializeDispatchTimer() creates a new object of DispatcherTimer class and sets the timer interval to tick every second. It also registers the Tick event (Timer_Tick)

In this event handler, the ProgressBar value is incremented in steps of 1. When the ProgressBar value reaches 10, Label content is updated and timer is stopped.

The third button’s click event handler uses Dispatcher object’s Invoke() method to set ProgressBar to initial value 0 and updates Label content. Next the DispatchTimer object is started using Start() method.

The ProgressBar starts to increment every second and completes after 10 seconds.

WPF Multithreading DispatcherTimer

The fourth button’s click event handler sets the ProgressBar value to 0 and sets the Label content. Next in the for loop, Sleep() method of Thread class is called to simulate a long running process. The ProgressBar value is incremented in steps of 1 with each loop iteration. Finally, the Label content is updated to display the completion of the operation.

In this case, the UI is blocked till the operation completes and the progress bar is incremented at the end synchronously.

WPF Multithreading Synchronous

The fifth button’s click event handler sets the ProgressBar value to 0 and sets the Label content to empty. Next an asynchronous thread is created and started using TPL. Inside this thread, Dispatcher’s Invoke method is called asynchronously. Inside it, Label’s content is set to notify the start of the process. Next, a for loop is created to iterate 10 times. Inside the for loop, Delay() method of System.Threading.Tasks.Task class is called to simulate a long running process with a delay of 1 second. The ProgressBar value is incremented in steps of 1 with each loop iteration. Finally after the for loop, the Label’s content is again set to notify the completion of the process.

WPF Multithreading AsyncThread

With the above 5 scenarios, you must be clear on how to create responsive multi threaded applications in WPF.

Please watch the following video to see how the program works.

This concludes the article – WPF Multithreading. I hope you liked this article. If you have any comments, questions or suggestions, please post them using the comments section below this article. I will try to respond at my earliest or somebody else reading the article and your comment will try to respond.

Please subscribe to my blog via email to get regular updates on the latest articles and share this article over social networks like Facebook, Twitter etc. You can use the social sharing buttons and social sharing bar provided to share this on social media.

Leave a comment