Anonymous method in Invoke call
Asked Answered
C

9

136

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

Caravel answered 31/10, 2008 at 10:49 Comment(0)
R
229

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.

Repartee answered 31/10, 2008 at 10:56 Comment(4)
How can i pass parameters to your first solution in this answer? I meant this solution: control.Invoke((MethodInvoker) delegate {this.Text = "Hi";});Pteranodon
Why does the Extension Method get invoked without having to do an Explicit cast to Action?Trigg
Because the compiler can infer that from the usage.Extremism
It's the same as being able to do Form.Load += Loader() instead of the old Form.Load += new EventHandler(Loader())Extremism
T
52

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

control.Invoke((MethodInvoker)(() => {this.Text = "Hi"; }));
Tareyn answered 31/10, 2008 at 11:0 Comment(1)
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.Delude
P
17
myControl.Invoke(new MethodInvoker(delegate() {...}))
Posticous answered 31/10, 2008 at 10:55 Comment(0)
F
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)); }));
Favors answered 31/10, 2008 at 10:56 Comment(0)
W
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();
});
Weatherly answered 20/12, 2010 at 22:16 Comment(1)
Invoke((Action) Process); is the best answer, thank you!Aristotelian
K
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;
        }));
    }
Kislev answered 12/11, 2008 at 22:22 Comment(0)
H
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";
}
Hyonhyoscine answered 11/4, 2020 at 14:7 Comment(0)
A
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) { }
    }
}
Abbreviated answered 12/9, 2018 at 9:23 Comment(0)
D
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.

Delude answered 9/2, 2023 at 18:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.