In a lot of our viewmodels, we have code that looks something like this :
void Process()
{
// do some work
...
// Dispatch some UI notification (purely visual related)
Application.Current.Dispatcher.BeginInvoke(() => ...);
}
We use Application.Dispatcher to ensure it runs on the UI thread, as Dispatcher.Current might not be the UI dispatcher.
The reason for dispatching the call is irrelevant and doesn't affect control flow or logic, however, when we test the viewmodel, we obviously have the issue that Application.Current is null.
Assuming the code being dispatched has no effect on the test. What to you think is the best way to get around the issue of it being null?
I can think of a few
- Simply use
Application.Current?.Dispatcher(). Is this a code smell? - Have some kind of mockable
IApplicationDispatcherthat every ViewModel has access to. This gives us the most power, but at the same time, it's annoying to now have an extra constructor parameter for each viewmodel. - Ensure tests run with a new application being created on initialise. This is ugly, and would need to be a static application that all UI tests share.
The
Dispatcheris usually not required in the view model. This is because the view model should not handle UI elements in general. In other words types that extendDispatcherObjectshould not be referenced outside the view.When your view model class implements
INotifyPropertyChangedsetting a property from a background thread does not require theDispatcher. The binding engine will marshal theINotifyPropertyChanged.PropertyChangedevent to the correct dispatcher thread (UI thread).This does not apply for the
INotifyCollectionChanged.CollectionChangedevent. You would have to configure the binding engine explicitly to marshal the event to the dispatcher thread by using theBindingOperations.EnableCollectionSynchronizationmethod. This way the binding engine will take care to raise theINotifyCollectionChanged.CollectionChangedevent on the correct dispatcher thread:This way the most common uses of the
Dispatcherin the view model (set property that is a binding source and add item to a collection that is a binding source) are eliminated. This removes the static Singleton reference from your code to enable full testability.