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
  • 153,850
  • 22
  • 249
  • 325
Duncan
  • 10,218
  • 14
  • 64
  • 96

9 Answers9

228

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
  • 153,850
  • 22
  • 249
  • 325
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • 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
52

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

control.Invoke((MethodInvoker)(() => {this.Text = "Hi"; }));
abatishchev
  • 98,240
  • 88
  • 296
  • 433
Vokinneberg
  • 1,912
  • 2
  • 18
  • 31
  • Nice. Do you happen to know if there is any way to do the same for a `Func<>` or `Action<>` with parameters? `MethodInvoker` specifically states it's for parameterless function calls without return value. – Nyerguds Sep 29 '22 at 16:47
17
myControl.Invoke(new MethodInvoker(delegate() {...}))
abatishchev
  • 98,240
  • 88
  • 296
  • 433
François
  • 935
  • 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
  • 98,240
  • 88
  • 296
  • 433
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
  • 9,038
  • 4
  • 24
  • 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
  • 40,559
  • 52
  • 175
  • 261
2

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.
  • 5,062
  • 2
  • 29
  • 34
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
  • 30,746
  • 24
  • 119
  • 189
0

I needed this to show a modal dialog from a separate thread, but in a way that I could actually get a return value from it, too. Vokinneberg's answer put me on the right track, but I still needed something more.

The final solution I came up with was to add this, as function-version of MethodInvoker:

public delegate Object FunctionInvoker();

Now, if I have a function like this, which shows a modal dialog and returns data from it...

Dictionary<int, string> ShowDataDialog(byte[] data, Form parent)
{
    using (DataDialog dd = new DataDialog(data))
    {
        if (dd.ShowDialog(parent) != DialogResult.OK)
            return null;
        return dd.MappedData;
    }
}

...it can be called like this from the different thread:

Dictionary<int, string> mappedData =
    (Dictionary<int, string>)parent.Invoke(
         (FunctionInvoker)(() => ShowNewFromImageDialog(data, parent)));

Now, everything works: the function is invoked on the main form, the dialog is shown as modal dialog of the main form, and the value from the dialog is returned to the other thread, which can continue its processing.

There's still some ugly casts in there, because Control.Invoke happens to return an Object, but still, it can be used for any function, as long as there are no advanced things like output-parameters involved.

It's probably possible to make this cleaner, as extension method on Form or something, that does the casts internally, if I work with generics. But for now, this'll do.

Nyerguds
  • 5,360
  • 1
  • 31
  • 63