27

In this article:

http://msdn.microsoft.com/en-us/library/ms171728(VS.80).aspx

The author uses the following method to make thread-safe calls to a Windows Forms control:

private void SetText(string text)
{
    // InvokeRequired required compares the thread ID of the
    // calling thread to the thread ID of the creating thread.
    // If these threads are different, it returns true.
    if (this.textBox1.InvokeRequired)
    {    
        SetTextCallback d = new SetTextCallback(SetText);
        this.Invoke(d, new object[] { text });
    }
    else
    {
        this.textBox1.Text = text;
    }
}

Is there a shorter way to accomplish the same thing?

Justin Tanner
  • 14,062
  • 17
  • 82
  • 103

5 Answers5

37

C# 3.0 and after:

An extension method would generally be the way to go, since you're always going to want to perform an action on an ISynchronizeInvoke interface implementation, it's a good design choice.

You can also take advantage of anonymous methods (closures) to account for the fact that you don't know what parameters to pass to the extension method; the closure will capture the state of everything needed.

// Extension method.
static void SynchronizedInvoke(this ISynchronizeInvoke sync, Action action)
{
    // If the invoke is not required, then invoke here and get out.
    if (!sync.InvokeRequired)
    {
        // Execute action.
        action();

        // Get out.
        return;
    }

    // Marshal to the required context.
    sync.Invoke(action, new object[] { });
}

You'd then call it like this:

private void SetText(string text)
{
    textBox1.SynchronizedInvoke(() => textBox1.Text = text);
}

Here, the closure is over the text parameter, that state is captured and passed as part of the Action delegate passed to the extension method.

Before C# 3.0:

You don't have the luxury of lambda expressions, but you can still generalize the code. It's pretty much the same, but not an extension method:

static void SynchronizedInvoke(ISynchronizeInvoke sync, Action action)
{
    // If the invoke is not required, then invoke here and get out.
    if (!sync.InvokeRequired)
    {
        // Execute action.
        action();

        // Get out.
        return;
    }

    // Marshal to the required context.
    sync.Invoke(action, new object[] { });
}

And then you call it with anonymous method syntax:

private void SetText(string text)
{
    SynchronizedInvoke(textBox1, delegate() { textBox1.Text = text; });
}
casperOne
  • 73,706
  • 19
  • 184
  • 253
  • 1
    Yeah works great, only thing you need to note is that you need to include: using System.ComponentModel; and using System; – Justin Tanner Feb 21 '09 at 00:23
  • 1
    I know this is quite old, but it still pops up near the top of searches for this solution... The problem with this is that you're assuming the parent class is static. You can't add a static extension to a regular class. Most forms/WPFs that contain the UI you want to access are regular classes with instances. So in those instances you're still left with the only option of the anonymous delegate as demonstrated below. – John Suit Jan 27 '16 at 13:23
  • @JohnSuit You certainly can add a static method to a "regular" class. The post 3.0 version is an extension method, and would appear as an instance method. The pre-3.0 version doesn't have to live on the class, it can live on another class, or you can make the method an instance method if you really need to (but you don't since you can add a static method anyways). – casperOne Jan 27 '16 at 16:41
  • Perhaps I should define "regular" class better for you. Instead, I think the official specs for the C# language lay it out best... C# 4.0 official specification, 10.6.9 ... "Extension methods can only be declared in non-generic, non-nested static classes. The first parameter of an extension method can have no modifiers other than this, and the parameter type cannot be a pointer type." – John Suit Jan 27 '16 at 21:00
  • @JohnSuit Still not seeing the problem. Is there something preventing you from creating a *new* class which offers extension methods? The C# spec doesn't place a limit on how many classes you can define in your project... – casperOne Jan 28 '16 at 18:19
10

1) Using anonymous delegate

private void SetText(string text)
{
    if (this.InvokeRequired)
    {    
        Invoke(new MethodInvoker(delegate() {
            SetText(text);
        }));
    }
    else
    {
        this.textBox1.Text = text;
    }
}

2) AOP approach

[RunInUIThread]
private void SetText(string text)
{
    this.textBox1.Text = text;
}

http://weblogs.asp.net/rosherove/archive/2007/05.aspx?PageIndex=2

3) Using lambda expressions (outlined by others).

alex2k8
  • 42,496
  • 57
  • 170
  • 221
6

Edit: I should mention I would not consider this to be a Best Practice

If you are using 3.5 you can make an extension method to the effect of:

public static void SafeInvoke(this Control control, Action handler) {
    if (control.InvokeRequired) {
        control.Invoke(handler);
    }
    else {
        handler();
    }
}

this is basically taken from: Here

Then use it like:

textBox1.SafeInvoke(() => .... );

Of course modify the extension etc for your usages.

Quintin Robinson
  • 81,193
  • 14
  • 123
  • 132
2

This may be obvious to most, but you can take the accepted answer and add another method if you need to retrieve the value...

public static T SynchronizedFunc<T>(this ISynchronizeInvoke sync, Func<T> func)
{
    if (!sync.InvokeRequired)
    {
        // Execute the function
        return func();
    }

    // Marshal onto the context
    return (T) sync.Invoke(func, new object[] { });
}

I used this recently to get handle of the form in a thread-safe way...

var handle = f.SynchronizedFunc(() => f.Handle);
Paul Hatcher
  • 7,342
  • 1
  • 54
  • 51
1

The shortest solution I have found is shown in the button example below where the goal is to change the text of a button.

    if (buttonX.InvokeRequired)
        buttonX.Invoke((Action)(() => buttonX.Text = "Record"));
    else
        buttonX.Text = "Record";
Spydee
  • 11
  • 1