Introduction
When writing multi-threaded WPF applications, you must ensure that you don't attempt to access any UI elements from any thread other than the main thread. This can lead to repetitive and untidy looking code. Using extension methods, we will look at a simplified method of providing thread safe UI updating.
Background
Sacha Barber presented a method for thread safe UI updating in his article, Useful WPF Threading Extension Method. This article extends his idea and provides a simpler coding construct.
The Example Application
When you run the example application, you can click on the Start button. This will start a timer that will fire every second. Click Stop to stop the timer. When the Thread Safe Mode CheckBox is checked, the UI Elements in the blue box will update every second. If the Thread Safe Mode checkbox is not checked, an exception will occur. When the timer is not running, you can also click on the Update from UI button. This will update the controls in the blue box regardless of Thread Safe Mode being checked or not because it calls UpdateControls from the UI thread.
Using the Code
Using Sacha's original Extension
method, the text for textBox1
would be set like this:
this.InvokeIfRequired(() =>
{
textBox1.Text = "Hello World";
},
DispatcherPriority.Background);
Using my extension methods, the text for textBox1
would be set like this:
textBox1.SetTextThreadSafe("Hello World");
To get the Text
of textBox1
using Sacha's extension method would be:
string s = "";
this.InvokeIfRequired(() =>
{
s = textBox1.Text = "Hello World";
},
DispatcherPriority.Background);
Setting the Text
using my extension methods would be:
string s = textBox1.GetTextThreadSafe();
How Does it Work?
We simply define a Get
and Set
method for the value we want to get or set. For convenience, I have put these methods in the same static
class as the InvokeIfRequired
method.
An example Set
method:
public static string SetTextThreadSafe(this TextBox tb)
{
string s = "";
InvokeIfRequired(tb, () => { s = tb.Text; }, DispatcherPriority.Background);
return s;
}
An example Get
method:
public static void SetTextThreadSafe(this TextBox tb, string s)
{
InvokeIfRequired(tb, () => { tb.Text = s; }, DispatcherPriority.Background);
}
An extension method must be contained in a static
class. Extension methods must also be declared as static
methods. The definition of extension methods looks slightly strange because the first parameter is defined with the keyword this
followed by the type that the method extends and then the name to use in the method for the instance. Only the first parameter is defined this way. The rest of them are defined normally.
It is best to try and create these methods for the most general class that has the property you want to use. This will prevent you from having to create similar methods for each control type. An example would be the Visibility
property. This is a property of UIElement
so it would make sense to create a Set
and Get
method for UIElement
rather than one for RadioButton
and another for Label
, etc.
Once an extension method is defined, it can be used by any instance of the type defined in the declaration or any type that derives from that type. It can also be called statically.
Questions and Answers
Wouldn't It Be Easier to Just Use the InvokeIfRequired Method?
If you only had to use it once or twice, this may be the case. If you are going to access multiple UI elements multiple times, I think my extension methods are easier.
Do I Have to Write an Extension Method for Every UI Element Property?
Otherwise you will need to use the InvokeIfRequired
method each time you access a property of a UI element, so why not wrap it inside an extension method and then you only have to do it once? You can then use your WPFThreadingExtensions
class in any applications that need it.
I Don't See a Set or Get Extension Method for the Property I Require?
I have provided a few examples of Get
and Set
extension methods in the test application so you can use these to help you see how to write your own extension methods.
What Do I Do if I Want to Use a Different DispatcherPriority?
I have used DispatcherPriority.Background
in my extensions methods. You could change this, create an extension method that takes the DisplatcherPriority
you require as a parameter or use the InvokeIfRequired
extension method.
History
- 20th October, 2009: Initial version