Hooch
Hooch

Reputation: 29673

MethodInvoker + lambda + arguments + CrossThread Operation

I'm using this to change something on other thread:

        MethodInvoker m = () => { login_submit.Text = "Login"; };
        if (InvokeRequired)
        {
            BeginInvoke(m);
        }
        else
        {
            Invoke(m);
        }

this is working fine.

How can I pass argumets to that lamba expression?

I want to do sth like that:

        MethodInvoker m = (string txt) => { login_submit.Text = txt; };
        if (InvokeRequired)
        {
            BeginInvoke(m); // I need to pass txt string in some way here.
        }
        else
        {
            Invoke(m); // I need to pass txt string in some way here.
        }

Upvotes: 2

Views: 5793

Answers (4)

Eli Arbel
Eli Arbel

Reputation: 22739

If this is a common scenario for you, I suggest writing an extension method:

public static class ControlExtensions
{
  public static void EnsureInvokeAsync(this Control control, Action action)
  {
     if (control.InvokeRequired) control.BeginInvoke(action);
     else action();
  }
}

class MyControl : UserControl
{
    void M(string s)
    {
       // the lambda will capture the string in a closure
       // the compiler does all the hard work for you
       this.EnsureInvokeAsync(() => _button.Text = s);
    }
}

Also, you should look into using BackgroundWorker or tasks for async operations.

Upvotes: 3

Russell Troywest
Russell Troywest

Reputation: 8776

If InvokeRequired is false then you don't need to worry about invoking anything at all - you're already on the right thread.

A better solution might be something like this:

public delegate void InvokerDelegate(string data);
public void DoStuff(string data){
  login_submit.Text = data;
}

and then when calling it do:

if (InvokeRequired){
  Invoke(InvokerDelegate(DoStuff), "something");
}
else{
  DoStuff("Something");
}

A fairly common pattern you will see is to do something like this for functions that manipulate the GUI in a multithreaded environment

public delegate void InvokerDelegate();
public void DoGuiStuff(){
  if (login_submit.InvokeRequired){
    login_submit.Invoke(InvokerDelegate(DoGuiStuff));
    return;  
  }

  login_submit.Text = "Some value";
}

If you use the above pattern the function checks to see if an invoke is required and if so Invokes itself on the right thread. It then returns. When it invokes itself the check to see if an invoke is required returns false so it doesn't bother invoking itself again - it just runs the code.

Edit: I just went back to winforms and tried to use that pattern only to spend a couple of frustrating minutes trying to work out why I couldn't invoke a lambda. I thought I'd better come back and update this answer to add the required casting in case anyone else tried to use it.

Upvotes: 2

psurikov
psurikov

Reputation: 3458

You can use closures to pass the value into the lambda's body.

string value = "Login";
MethodInvoker m = () => { login_submit.Text = value; };
if (InvokeRequired)
{
    BeginInvoke(m); // I need to pass txt string in some way here.
}
else
{
    Invoke(m); // I need to pass txt string in some way here.
}

or you can use class member's data

Upvotes: 0

svick
svick

Reputation: 244757

MethodInvoker is a delegate type that doesn't have any parameters. If I understand you correctly, you can do it like this:

string txt = "some text";
MethodInvoker m = () => { login_submit.Text = txt; };

Upvotes: 1

Related Questions