Accessing a WinForm/WPF control from another thread through invoke

Accessing a WinForm/WPF control from another thread through invoke

A pretty common scenario: you have some time-consuming calculation to do (or some other requirements giving the necessity for multithreading), so from your main GUI, you start a worker thread (since then your GUI stays nice and responsive, even though now one of the problems you face is preventing your user from doing things he should not be doing while the thread runs). Since the procedure is so long, you maybe want to update a status label in the main window, or at least you want to set some result string when the thread is finished.

Good idea. But.. if you do it in a straightforward way (calling into your class with a static variable of sorts), your application will throw an exception in debug mode and might (very occasionally) cause trouble in release mode (if there are problems with concurrent access – since WinForms as well as WPF is not created for concurrency).

Fortunately, there is a solution.

I stumbled upon a beautful, nicely working solution for the problem for WinForms on StackOverflow.

Winforms solution

In the same namespace as your WinForms class, add the following static class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static class ISynchronizeInvokeExtensions
{
    public static void InvokeEx<t>(this T @this, Action<t> action)
        where T : System.ComponentModel.ISynchronizeInvoke
    {
        if (@this.InvokeRequired)
        {
            @this.Invoke(action, new object[] { @this });
        }
        else
        {
            action(@this);
        }
    }
}

This class extends all classes implementing the ISynchronizeInvoke (like WinForms forms).

Now you can access your methods, fields and the like from any other thread through the power of invoke, which makes sure that the functionality will be executed synchronously.

The method is even clever enough to distinguish whether you actually have to use Invoke or not.

Also note the neat syntax you can use to call it:

1
2
3
4
form.InvokeEx(f => f.EnableChildControls(false));
form.InvokeEx(f => f.textBoxPath.Text = "Success: " + res);
form.InvokeEx(f => f.textBoxPath.SelectionStart = f.textBoxPath.TextLength);
form.InvokeEx(f => f.textBoxPath.ScrollToCaret());

Of course, you would have to somewhere initialize a static variable form that points to your form that is accessible everywhere you need it.

Solution for WPF

Unfortunately, WPF controls don’t implement the System.ComponentModel.ISynchronizeInvoke interface.

Luckily, though, they derive from another class, DispatcherObject, which offers similar functionality:

1
2
3
4
5
6
7
8
public static class ISynchronizeInvokeExtensions
{
    public static void InvokeEx<t>(this T @this, Action<t> action)
        where T : DispatcherObject
    {
        @this.Dispatcher.Invoke(action, new object[] { @this });
    }
}

Usage is the same as for WinForms, only, now you cannot determine anymore whether you need Invoke, but you can use it all the time (and should need it all the time) for typical scenarios, where you access your GUI.

Conclusion

For such a standard application I would have expected a standard solution. At least the exception that is being thrown in debug mode points one eventually (via Google) to a solution, and later maybe to an explanation.

Anyhow, the solution as found here or as similar ones (which can be found e. g. on StackOverflow) seem decent enough and do what they are supposed to.

Now all those worker threads (and yes, multithreaded has been the future by now for several years) can also properly interact with your GUI.

5 thoughts on “Accessing a WinForm/WPF control from another thread through invoke

  1. Accesing a Winform/WPF control from another thraed ….

    Kompliment, genial!
    Alle aktuellen Technologien vereint, Linq, Generics, Extensions. Deshalb nur eine Zeile Code. Ich liebe diese feinen Spezialitäten.

    Grüße aus einem „altbackenen“ Versicherungsprojekt.

  2. I think you have an error in your code snippet. You use capital T’s and smal T’s:

    …InvokeEx(this T @this, Action action)… gives me an error, while changing it to:
    …InvokeEx(this T @this, Action action)… works. But sadly I can not use your scheme because I do my GUI with the Humble Dialog box pattern which uses just an Interface for accessing the Form, but I will just insert the method in the interface and implement it in the forms I need it. Thanks anyways.

    Greetings.

  3. You are right, thanks for the correction. It should be

    1
    2
    3
    4
    5
    6
    7
    8
    
    public static class ISynchronizeInvokeExtensions
    {
        public static void InvokeEx<T>(this T @this, Action<t> action)
            where T : DispatcherObject
        {
            @this.Dispatcher.Invoke(action, new object[] { @this });
        }
    }

    Hmm.. don’t know where the error is from, I thought I just copy/pasted.

    Plus, as you said. you can use it to call interface functions as well. The problem is with one thread not being the GUI thread but accessing GUI elements (in any way: directly, through interface, …).

    Humble Dialog Box. 🙂 New interesting pattern (MVCish?!) every day!

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.