I'm fairly new to working with threads. I was trying to set a DependencyProperty
's value:
public States State
{
get { return (States)GetValue(StateProperty); }
set
{
Dispatcher.BeginInvoke(DispatcherPriority.Background,
//(SendOrPostCallback)delegate { SetValue(StateProperty, value); }, //works
(Action)(()=> SetValue(StateProperty, value)), //doesnt
value);
}
}
public static readonly DependencyProperty StateProperty =
DependencyProperty.Register("State", typeof(States), typeof(FTPDownload), new UIPropertyMetadata(States.Idle));
I realized the hard way that in the setter you have to use SendOrPostCallback (as it provides an argument when calling the method). it does NOT work with Action (because of the missing argument. And, wpf is really a bitch about it, debugging and finding the cause of the TargetParameterCountException with "no source available" and no clue at all.
Why do I have to use SendOrPostCallback there? and how should I know that in this case this is the right one? Because actually calling the setter works via:
Dispatcher.BeginInvoke((Action)(()=>State=States.Updating), null);
and using the SendOrPostCallback instead of course results in a TargetParameterCountException..
Just wondering if seemingly inconsistent thing like that are just common knowledge? Feeling a bit lost here, at least since googling around with SendOrPostCallback, Action and BeginInvoke as keywords had no meaningful results.
The relevant pieces of information:
1.The overload of Dispatcher.BeginInvoke
that you are using is:
public DispatcherOperation BeginInvoke(
DispatcherPriority priority,
Delegate method,
Object arg
)
method
: A delegate to a method that takes one argument, which is pushed onto the Dispatcher event queue.
2.The SendOrPostCallBack
delegate is declared as:
public delegate void SendOrPostCallback(object state)
3.As for Action
:
public delegate void Action()
Clearly, the SendOrPostCallBack
delegate is compatible since it takes a single argument but Action
is not, since it is parameterless.
Of course, you can use the Action<T>
delegate, which does take a single argument, if you prefer:
Dispatcher.BeginInvoke(DispatcherPriority.Background,
new Action<States>(arg => SetValue(StateProperty, arg)),
value);
Alternatively, you can use a different overload of Dispatcher.BeginInvoke
that expects an argument that is of a delegate-type that takes no arguments, and get the C# compiler to do the dirty work for you in the closure:
Dispatcher.BeginInvoke(DispatcherPriority.Background,
new Action(() => SetValue(StateProperty, value));
Notice that value
is a captured variable, so please be careful.
(Also, this answer doesn't deal with any thread-safety issues, only about the delegate signatures involved.)