I do not know if you still need an answer, but maybe someone will observe the same error.
So the problem is that, as you rightly said, the RaiseCanExecuteChanged() method does not always send an event handler call to the UI thread synchronization context.
If we look at the implementation of WeakEventHandlerManager , we will see two things. Firstly, this static class has a private static field:
private static readonly SynchronizationContext syncContext = SynchronizationContext.Current;
Secondly, there is a private method that should use this synchronization context and actually send calls to the event handler in this context:
private static void CallHandler(object sender, EventHandler eventHandler) { if (eventHandler != null) { if (syncContext != null) { syncContext.Post((o) => eventHandler(sender, EventArgs.Empty), null); } else { eventHandler(sender, EventArgs.Empty); } } }
So it looks good, but ...
As I said, this call message does not always occur. βNot alwaysβ means, for example, this circumstance:
- Your build was built in release configuration and with optimization enabled
- you do not attach a debugger to your assembly
In this situation, the .NET platform optimizes code execution and, importantly, can initialize the syncContext static field at any time, but before it is used for the first time. So, this happens in our case - this field is initialized only when you first call the CallHandler() method (of course, indirectly by calling RaiseCanExecuteChanged() ). And since you can call this method from the thread pool, in this case the synchronization context will not be set, so the field will be only null , and the CallHandler() method CallHandler() call the event handler in the current thread, but not to the User Interface.
The solution to this is, from my point of view, hacking or some kind of code smell. I donβt like it anyway. You just need to make sure that CallHandler() is called for the first time from the user interface thread, for example, by calling the RaiseCanExecuteChanged() method RaiseCanExecuteChanged() DelegateCommand instance that has valid CanExecuteChanged subscriptions.
Hope this helps.