Cross-thread operations in .NET
Have you ever gotten the exception that you can’t invoke a control’s method from a background thread in .NET? You know the “Cross-thread operation not valid” exception. You’re not alone! There are reasons why this behaviour has been introduced. Like the background thread could lock your UI without any obvious reason and the program would just look hung and nobody would actually understand why.
There are several ways to walk-around this problem. One would be, for example, to use the BackgroundWorker class and communicate with the UI via the ProgressChanged event. Another way is to check if an “Invoke” method call is required and call the control’s (Form inherits from Control) Invoke method by specifying a method and creating a delegate for the method’s signature. That would look like this:
this.Invoke(
else
this.UpdateControls();
The UpdateControls method (in this example) would update the controls on the form.
Always checking for whether an Invoke is required and then doing the appropriate thing or even creating own delegates for method signatures is a lot of work. But we can do better.
That’s when C#’s anonymous methods come into the game. They allow us to create a method and even pass in arguments to the method’s body (this second fact is very useful in our scenario). These methods are delegates and can be executed as delegates that can then communicate with the UI. I have written a short class that encapsulates the functionality. The class holds just two delegates: one returns a generic value and one returns nothing (void). The arguments for the methods called inside the anonymous method are just passed in and the C# compiler will take care for us.
/// This class automates cross thread operations.
/// </summary>
public static class CrossThreadOperation
{
public delegate void Func();
public delegate TResult Func<TResult>();
/// <summary>
/// Invokes a cross-thread operation that doesn’t return a result.
/// </summary>
/// <param name="control"></param>
/// <param name="del"></param>
public static void Invoke(Control control, Func del)
{
if (control == null)
throw
if (del == null)
throw
// Check if we need to use the controls invoke method to do cross-thread operations.
if (control.InvokeRequired)
control.Invoke(del);
else
del();
}
/// <summary>
/// Invokes a cross-thread operation that returns a result.
/// </summary>
/// <typeparam name="TResult"></typeparam>
/// <param name="control"></param>
/// <param name="del"></param>
/// <returns></returns>
public static TResult Invoke<TResult>(Control control, Func<TResult> del)
{
if (control == null)
throw
if (del == null)
throw
// Check if we need to use the controls invoke method to do cross-thread operations.
if (control.InvokeRequired)
return (TResult)control.Invoke(del);
return del();
}
}
The usage is very straight forward. If we have a method without arguments and without return value, only the method name needs to be specified. Everything else is done by the C# compiler.
CrossThreadOperation.Invoke(this, UpdateControls);
If we have an argument for the internally called method (or methods) we need to use the delegate keyword:
CrossThreadOperation.Invoke(this, delegate { UpdateControls(value); });
And if we expect some return value, we need to use the Invoke method that expect a generic argument.
int result = CrossThreadOperation.Invoke<int>(this,
delegate { return UpdateControls(value); });






Published on Oct 19th, 2007 —
Tags:
This is a fantastic solution! Thanks for sharing!
Comment by Shannon Richards — May 16, 2008 @ 6:40 pm
Note that you can also call parameterised functions using lambda syntax:
CrossThreadOperation.Invoke(this, () => UpdateControls(value));
As you have noted elsewhere, you can also do an extension method version, this would result in this:
this.Invoke(() => UpdateControls(value));
Comment by Benjol — September 9, 2008 @ 12:47 am
Eh, having said that, Invoke() is already taken, so for the extension method, it would probably be better to give the method a different name: SafeInvoke?
Comment by Benjol — September 9, 2008 @ 12:50 am
Oh, and one more trap to avoid:
http://ikriv.com:8765/en/prog/info/dotnet/MysteriousHang.html
Last comment, promised!
Comment by Benjol — September 9, 2008 @ 12:51 am
Hi there,
I am using your piece of code that works fine for “void Invoke” method. I wanted to use now the other one, ” Invoke” , TResult is a string in my case…. but I get an exception: Unable to cast “ThreadMethodEntry” into “string” … do you have any ideas.?
Thanks
Comment by Cristi — March 12, 2009 @ 4:25 am
Well, the error above was due to a change: “Invoke” replaced by “BeginInvoke” … of course returning value was different.
Thus I have 3 static methods now in the CrossThreadingOperation class :
public static Invoke(Control control, Func del);
// THIS BELOW IS ADDED!
///
/// Same as void Invoke, but calling Control.BeginInvoke() method
///
///
///
public static void BeginInvoke(Control control, Func del);
Comment by Cristi — March 12, 2009 @ 8:38 am
I’ll resend the code for better visualization due to the html errors:
Thus I have 3 static methods now in the CrossThreadingOperation class :
public static <T> Invoke<T>(Control control, Func<T> del);
// THIS BELOW IS ADDED!
// Same as void Invoke, but calling Control.BeginInvoke() method
public static void BeginInvoke(Control control, Func del);
Comment by Cristi — March 12, 2009 @ 8:50 am