Justin Tanner
Justin Tanner

Reputation: 14352

Shortest way to write a thread-safe access method to a windows forms control

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?

Upvotes: 27

Views: 25395

Answers (5)

Spydee
Spydee

Reputation: 11

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";

Upvotes: 1

Paul Hatcher
Paul Hatcher

Reputation: 8156

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);

Upvotes: 2

casperOne
casperOne

Reputation: 74530

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; });
}

Upvotes: 37

alex2k8
alex2k8

Reputation: 43214

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).

Upvotes: 10

Quintin Robinson
Quintin Robinson

Reputation: 82335

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.

Upvotes: 6

Related Questions