C# Threading: Windows Forms and WPF InvokeRequired / CheckAccess
October 26, 2010 by
Christoff Truter
C#
Threading
When interacting with UI via threads (using Windows Forms) you might have
seen the following exception:
Cross-thread operation not valid: Control 'x' accessed from a thread
other than the thread it was created on.
A Simple way to reproduce this exception, is to simply create a Windows Forms
app, drop a button on the form, attach an event handler on the click event
(of the button) and create a thread that attempts to access the button, for example:
private void action()
{
button1.Text = "Clicked";
}
private void button1_Click(object sender, EventArgs e)
{
Thread t = new Thread(action);
t.Start();
}
The reason behind this exception is because Windows Form controls aren't thread safe, so in order to
achieve some kind of thread safety, the UI controls are bound to the thread it was created
on and in order to access them, one needs to reside in the same thread.
How does one achieve this
(reside in the same thread) exactly?
One way, is to check the InvokeRequired property on the control and use the Invoke
(synchronous)
or BeginInvoke
(asynchronous) method on the control in order to send some code
(via delegate) to the
thread that owns it, observe:
private void action()
{
if (button1.InvokeRequired)
{
button1.Invoke(new Action(action));
return;
}
button1.Text = "Clicked";
}
If we try to do the same thing
(access controls that reside in the UI thread) in a WPF Application, we are confronted with the same issues,
private void action()
{
button1.Content = "Clicked";
}
private void button1_Click(object sender, RoutedEventArgs e)
{
Thread t = new Thread(action);
t.Start();
}
The exception message looks a little bit different though:
The calling thread cannot access this object because a different thread owns it.
And unlike the Windows Forms solution, you won't find the InvokeRequired property on the WPF controls,
instead one can use the Dispatcher property, observe:
private void action()
{
if (button1.Dispatcher.CheckAccess())
{
button1.Content = "Clicked";
return;
}
button1.Dispatcher.Invoke(
System.Windows.Threading.DispatcherPriority.Normal,
new Action(action));
}
Something interesting to note, is that CheckAccess method
(and a few overloads within the invoke method)
is hidden to intellisense, which is achieved by the following attribute:
[EditorBrowsable(EditorBrowsableState.Never)]
Looking at other solutions, you might want to have a look at the AsyncOperationManager & SynchronizationContext
classes - perhaps a future post.
Senior Principal Engineer November 7, 2017 by Andrew
Awesome explanation, nothing like it - anywhere. Well done!