135

Having a bit of trouble with the syntax where we want to call a delegate anonymously within a Control.Invoke.

We have tried a number of different approaches, all to no avail.

For example:

myControl.Invoke(delegate() { MyMethod(this, new MyEventArgs(someParameter)); }); 

where someParameter is local to this method

The above will result in a compiler error:

Cannot convert anonymous method to type 'System.Delegate' because it is not a delegate type

Patrick Hofman
  • 148,824
  • 21
  • 237
  • 306
Duncan
  • 10,040
  • 14
  • 62
  • 95

8 Answers8

227

Because Invoke/BeginInvoke accepts Delegate (rather than a typed delegate), you need to tell the compiler what type of delegate to create ; MethodInvoker (2.0) or Action (3.5) are common choices (note they have the same signature); like so:

control.Invoke((MethodInvoker) delegate {this.Text = "Hi";});

If you need to pass in parameters, then "captured variables" are the way:

string message = "Hi";
control.Invoke((MethodInvoker) delegate {this.Text = message;});

(caveat: you need to be a bit cautious if using captures async, but sync is fine - i.e. the above is fine)

Another option is to write an extension method:

public static void Invoke(this Control control, Action action)
{
    control.Invoke((Delegate)action);
}

then:

this.Invoke(delegate { this.Text = "hi"; });
// or since we are using C# 3.0
this.Invoke(() => { this.Text = "hi"; });

You can of course do the same with BeginInvoke:

public static void BeginInvoke(this Control control, Action action)
{
    control.BeginInvoke((Delegate)action);
}

If you can't use C# 3.0, you could do the same with a regular instance method, presumably in a Form base-class.

Patrick Hofman
  • 148,824
  • 21
  • 237
  • 306
Marc Gravell
  • 976,458
  • 251
  • 2,474
  • 2,830
  • How can i pass parameters to your first solution in this answer? I meant this solution: control.Invoke((MethodInvoker) delegate {this.Text = "Hi";}); – uzay95 Nov 26 '09 at 14:05
  • 1
    Why does the Extension Method get invoked without having to do an Explicit cast to Action? – P.Brian.Mackey Jan 23 '13 at 15:38
  • Because the compiler can infer that from the usage. – RoboJ1M Feb 29 '16 at 14:25
  • 1
    It's the same as being able to do `Form.Load += Loader()` instead of the old `Form.Load += new EventHandler(Loader())` – RoboJ1M Feb 29 '16 at 14:25
51

Actually you do not need to use delegate keyword. Just pass lambda as parameter:

control.Invoke((MethodInvoker)(() => {this.Text = "Hi"; }));
abatishchev
  • 95,331
  • 80
  • 293
  • 426
Vokinneberg
  • 1,872
  • 2
  • 18
  • 31
17
myControl.Invoke(new MethodInvoker(delegate() {...}))
abatishchev
  • 95,331
  • 80
  • 293
  • 426
François
  • 937
  • 1
  • 7
  • 11
13

You need to create a delegate type. The keyword 'delegate' in the anonymous method creation is a bit misleading. You are not creating an anonymous delegate but an anonymous method. The method you created can be used in a delegate. Like this:

myControl.Invoke(new MethodInvoker(delegate() { (MyMethod(this, new MyEventArgs(someParameter)); }));
abatishchev
  • 95,331
  • 80
  • 293
  • 426
Jelon
  • 161
  • 5
8

For the sake of completeness, this can also be accomplished via an Action method/anonymous method combination:

//Process is a method, invoked as a method group
Dispatcher.Current.BeginInvoke((Action) Process);
//or use an anonymous method
Dispatcher.Current.BeginInvoke((Action)delegate => {
  SomeFunc();
  SomeOtherFunc();
});
mhamrah
  • 8,328
  • 4
  • 23
  • 22
5

I had problems with the other suggestions because I want to sometimes return values from my methods. If you try to use MethodInvoker with return values it doesn't seem to like it. So the solution I use is like this (very happy to hear a way to make this more succinct - I'm using c#.net 2.0):

    // Create delegates for the different return types needed.
    private delegate void VoidDelegate();
    private delegate Boolean ReturnBooleanDelegate();
    private delegate Hashtable ReturnHashtableDelegate();

    // Now use the delegates and the delegate() keyword to create 
    // an anonymous method as required

    // Here a case where there's no value returned:
    public void SetTitle(string title)
    {
        myWindow.Invoke(new VoidDelegate(delegate()
        {
            myWindow.Text = title;
        }));
    }

    // Here's an example of a value being returned
    public Hashtable CurrentlyLoadedDocs()
    {
        return (Hashtable)myWindow.Invoke(new ReturnHashtableDelegate(delegate()
        {
            return myWindow.CurrentlyLoadedDocs;
        }));
    }
Rory
  • 38,561
  • 50
  • 162
  • 246
1

I like to use Action in place of MethodInvoker, it is shorter and looks cleaner.

Invoke((Action)(() => {
    DoSomething();
}));

// OR

Invoke((Action)delegate {
    DoSomething();
});

Eg.

// Thread-safe update on a form control
public void DisplayResult(string text){
    if (txtResult.InvokeRequired){
        txtResult.Invoke((Action)delegate {
            DisplayResult(text);
        });
        return;
    }

    txtResult.Text += text + "\r\n";
}
Du D.
  • 4,524
  • 2
  • 28
  • 33
0

I never understood why this makes a difference for the compiler, but this is sufficient.

public static class ControlExtensions
{
    public static void Invoke(this Control control, Action action)
    {
        control.Invoke(action);
    }
}

Bonus: add some error handling, because it is likely that, if you are using Control.Invoke from a background thread you are updating the text / progress / enabled state of a control and don't care if the control is already disposed.

public static class ControlExtensions
{
    public static void Invoke(this Control control, Action action)
    {
        try
        {
            if (!control.IsDisposed) control.Invoke(action);
        }
        catch (ObjectDisposedException) { }
    }
}
Jürgen Steinblock
  • 29,009
  • 23
  • 111
  • 177