Dispatcher BeginInvoke Syntax
Asked Answered
D

3

62

I have been trying to follow some WCF Data Services examples and have the following code:

private void OnSaveCompleted(IAsyncResult result)
    {
        Dispatcher.BeginInvoke(() =>
        {
            context.EndSaveChanges(result);
        });
    }

Which is called by the following:

this.context.BeginSaveChanges(SaveChangesOptions.Batch, this.OnSaveCompleted, null);

Now I am getting a little confused here. Firstly, the first bit of code is showing a syntax error of

Argument type lambda expression is not assignable to parameter type System.Delegate

So instead of blindly trying to follow the example code, I tried to understand what was going on here. Unfortunately, I am struggling to understand the error plus what is actually happening. Can anyone explain?

I feel a bit stupid as I am sure this is easy.

Drugge answered 21/9, 2010 at 13:31 Comment(0)
B
119

The problem is that the compiler doesn't know what kind of delegate you're trying to convert the lambda expression to. You can fix that either with a cast, or a separate variable:

private void OnSaveCompleted(IAsyncResult result)
{        
    Dispatcher.BeginInvoke((Action) (() =>
    {
        context.EndSaveChanges(result);
    }));
}

or

private void OnSaveCompleted(IAsyncResult result)
{
    Action action = () =>
    {
        context.EndSaveChanges(result);
    };
    Dispatcher.BeginInvoke(action);
}
Battista answered 21/9, 2010 at 13:34 Comment(15)
Thanks, but now I am getting "Cannot access non-static method 'BeginInvoke' in static context. I am more confused now, as this isn't a static method?Drugge
@Jon: It thinks you're trying to use BeginInvoke as a static method on the Dispatcher class - whereas you want to use the Dispatcher property and then call BeginInvoke on the relevant instance. My guess is that this isn't in an appropriate class with a Dispatcher property. Having just seen that this is WCF, I'm not sure sure where you'd get a Dispatcher from. I'm more used to using it from WPF and Silverlight.Battista
This is actually on a ViewModel class in a WPF applicationDrugge
The ViewModel typically has no knowledge of the view, much less an associated dispatcher. You could go about using Dispatcher.CurrentDispatcher, but I strongly advise against it (you could easily end up in the wrong thread and the delegate will never be invoked); the best way I think would be to use something like MVVMLight's Messenger and send a message to the view - the message could contain the Action and the View could invoke it using its dispatcher.Alanaalanah
@Jon: It would definitely be worth updating the question to reflect that. The WCF part is irrelevant, but the WPF part is highly relevant. As for getting the dispatcher to the ViewModel - you could use the messenger as Alex suggested, or you could inject the Dispatcher nito the ViewModel... not as a Dispatcher, but in terms of a wrapper around it, implementing your own interface. That way you can test that you're doing the right thing in terms of thread safety. I've done this before, and it's worked well.Battista
@Jon (Skeet) - Interesting idea; however I vaguely remember running into problems with something similar because under a test environment like MSTest (console app), the Dispatcher isn't started automatically and you must jump through some hoops to get your delegates to be invoked correctly. However I might be confusing it with something else... not sure anymore, it's late and I'm tired.Alanaalanah
@my previous comment: Actually nevermind, of course if you inject the dispatcher using an interface you can mock it while testing.Alanaalanah
What was the solution? I'm having the same problem where I get: An object reference is required for the non-static field, method, or property 'System.Windows.Threading.Dispatcher.BeginInvoke(System.Delegate, params object[])'. Can't use an outside framework or nuGet package.Milstone
@KalaJ: Well presumably you're within a static method... you have to call it on a particular dispatcher. Your problem is entirely different to the one in this question, where Dispatcher means the Dispatcher property within a control.Battista
@JonSkeet, Nope my method is not static. The solution I found was, I had to get the dispatcher from the UI thread then pass it to my task.Milstone
@Kala: Well you have to get it from a UI component... Not from a thread.Battista
@JonSkeet, What's the difference? The way I thought of it was that I have a UI thread and a Background thread. I had to get it from the UI thread and pass it to the background thread.Milstone
@KalaJ: There's all the difference in the world between a component and a thread. But so long as you've got your solution, that's probably fine. If you want to know more, ask a new question.Battista
oops, just had the same problem (with the non-static mentioned above) getting a CS0120 "An object reference is required for the non-static field, method, or property" when doing public void Speak(string text) { Dispatcher.BeginInvoke(new Action(() => speechSynthesizer.Speak(text))); } - TURNS OUT I needed to do public void Speak(string text) { Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() => speechSynthesizer.Speak(text))); }. I think Visual Studio 2015 should add an extra lightbulb hint for this one, bit hard to catchTecu
Seems people get confused and do Dispatcher.BeginInvoke instead of Dispatcher.CurrentDispatcher.BeginInvoke. This is because there is a Dispatcher property available at WPF so they try to reuse code pattern they remember in code outside of a WPF windowTecu
C
17

Answer by Jon Skeet is very good but there are other possibilities. I prefer "begin invoke new action" which is easy to read and to remember for me.

private void OnSaveCompleted(IAsyncResult result)
{       
    Dispatcher.BeginInvoke(new Action(() =>
    {
        context.EndSaveChanges(result);
    }));
}

or

private void OnSaveCompleted(IAsyncResult result)
{       
    Dispatcher.BeginInvoke(new Action(delegate
    {
        context.EndSaveChanges(result);
    }));
}

or

private void OnSaveCompleted(IAsyncResult result)
{       
    Dispatcher.BeginInvoke(new Action(() => context.EndSaveChanges(result)));
}
Comprador answered 14/9, 2012 at 21:44 Comment(1)
maybe it costs more to do a "new Action" compared to just do (Action) cast?Tecu
N
6

If your method does not require parameters, this is the shortest version I've found

Application.Current.Dispatcher.BeginInvoke((Action)MethodName); 
Newlywed answered 25/4, 2017 at 8:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.