In this post, I will discuss how to update UI elements from a background thread in WPF. In the previous three posts about multithreading, I provided solution for WinForm applications. These techniques can also be applied to the WPF Windows applications. The ReportProgress method in WinFowm is almost identical to that in the WPF, so I’ll not discuss it here.
In WinForm, we can use the delegate + Invoke method to update UI elements. In WPF, we have got something similar but more powerful – the Dispatcher. Note that every Visual in WPF inherits from DispatcherObject, which allows you to get a hold of the UI thread’s Dispitcher. Anytime you update a Visual, the work is sent through the Dispatcher to the UI thread.
In the following I present a WPF example. The layout in this example is similar to that used in Part 3. Here is the layout created using the following XAML
<Window x:Class="WpfBackgroundworkerTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="289" Width="305">
<Grid>
<TextBlock Height="23" HorizontalAlignment="Left"
Margin="28,32,0,0" Name="textBlock1"
Text="TextBlock" VerticalAlignment="Top" />
<TextBox Height="23" HorizontalAlignment="Left"
Margin="28,61,0,0" Name="textBox1"
VerticalAlignment="Top" Width="227" />
<TextBox Height="23" HorizontalAlignment="Left"
Margin="28,90,0,0" Name="textBox2"
VerticalAlignment="Top" Width="227" />
<TextBlock Height="23" HorizontalAlignment="Left"
Margin="28,129,0,0" Name="textBlock2"
Text="TextBlock" VerticalAlignment="Top" />
<Button Content="Start" Height="23"
HorizontalAlignment="Left" Margin="180,169,0,0"
Name="btnStart" VerticalAlignment="Top" Width="75"
Click="btnStart_Click" />
<Button Content="Cancel" Height="23"
HorizontalAlignment="Left" Margin="180,198,0,0"
Name="btnCancel" VerticalAlignment="Top" Width="75"
Click="btnCancel_Click" />
</Grid>
</Window>
And below shows code-behind file:
using System;
using System.Windows;
using System.Windows.Shapes;
using System.ComponentModel;
using System.Threading;
namespace WpfBackgroundworkerTest
{
public partial class MainWindow : Window
{
private delegate void UpdateDelegate(object[] x);
private BackgroundWorker worker1;
public MainWindow()
{
InitializeComponent();
worker1 = new BackgroundWorker();
worker1.WorkerReportsProgress = true;
worker1.WorkerSupportsCancellation = true;
worker1.DoWork += new DoWorkEventHandler(worker1_DoWork);
worker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(
worker1_RunWorkerCompleted);
}
void worker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (!(e.Cancelled))
textBlock2.Text = "Calculation Completed!";
else
textBlock2.Text = "Calculation Cancelled!";
btnStart.IsEnabled = true;
btnCancel.IsEnabled = false;
}
void worker1_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i < 500; i++)
{
Thread.Sleep(100);
if (worker1.CancellationPending)
{
e.Cancel = true;
return;
}
object[] x = new object[] { i, Math.Sin(1.0 * i / 10.0),
Math.Cos(1.0 * i / 10.0) };
UpdateDelegate update = new UpdateDelegate(UpdateUIElements);
this.Dispatcher.BeginInvoke(update, new object[] { x });
}
}
private void btnStart_Click(object sender, RoutedEventArgs e)
{
textBlock2.Text = "Calculation Start!";
worker1.RunWorkerAsync();
btnStart.IsEnabled = false;
btnCancel.IsEnabled = true;
}
private void btnCancel_Click(object sender, RoutedEventArgs e)
{
worker1.CancelAsync();
}
private void UpdateUIElements(object[] x)
{
textBlock1.Text = " i = " + x[0].ToString();
textBox1.Text = "Sin = " + x[1].ToString();
textBox2.Text = "Cos = " + x[2].ToString();
}
}
}
There are two things I want to point out here. First, the BackgroundWorker is not a WPF component, so you won’t see it in the toobox, which means you cannot create a backgroundworker object visually in the Designer. You have to create it in the code-behind file. The other point is that you need to replace the Invoke method in WinForm by the Dispatcher.BeginInvoke.
Running this project produces the similar results as in Part 3.


