I moved the creation of one of my classes to a background thread in my C#/WPF app. It exposes a Prism DelegateCommand object. Suddenly my app starts crashing. I think my hack of a fix is the wrong way to go but I don't know a better one. Can someone tell me?
This is the command I create in the constructor.
AnalyzeCmd = new DelegateCommand(() => Analyze(), () => CanAnalyze)
.ObservesProperty(() => CanAnalyze);
When I used to create this in the UI thread, everything worked fine, no matter what thread changed the CanAnalyze property. But now, whenever CanAnalyze changes on a background thread, my application crashes. The button to which the command is bound is complaining about lack of access of the current thread.
Looking at Prism's DelegateCommandBase, I can see why. DelegateCommandBase captures the SynchronizationContext in its constructor and then tries to invoke CanExecuteChanged on that context whenever it needs to.
protected DelegateCommandBase()
{
_synchronizationContext = SynchronizationContext.Current;
}
protected virtual void OnCanExecuteChanged()
{
var handler = CanExecuteChanged;
if (handler != null)
{
if (_synchronizationContext != null && _synchronizationContext != SynchronizationContext.Current)
_synchronizationContext.Post((o) => handler.Invoke(this, EventArgs.Empty), null);
else
handler.Invoke(this, EventArgs.Empty);
}
}
But on my background thread, that's the wrong context. And there does not seem to be any way for me to manually set the _synchronizationContext member of my DelegateCommand object. Once it is constructed, that thing is locked in.
About the only way I could make this work was to "fake" the context while creating the commands. This looks terrible even to me but it does make the CanExecuteChanged get sent to the correct thread.
// Fake the UI thread's context for `DelegateCommand`
var prevCtx = SynchronizationContext.Current;
SynchronizationContext.SetSynchronizationContext(new DispatcherSynchronizationContext(Application.Current.Dispatcher));
AnalyzeCmd = new DelegateCommand(() => Analyze(), () => CanAnalyze)
.ObservesProperty(() => CanAnalyze);
// Now restore the previous context
SynchronizationContext.SetSynchronizationContext(prevCtx);
From what I have read the user generally should not be setting the Synchronization context in a WPF application. So is there a better, recommended way to do this?