tags:

views:

204

answers:

4
  • Collections that are bound in a WPF View must be updated on the UI thread.
  • ViewModel exposes a collection
  • Therefore when collection in the ViewModel is modified it must be done in the UI thread
  • Best practice is to keep ViewModels ignorant of View and presumably such details as Dispatcher.

What is the cleanest way to resolve this while keeping view model testable?

+1  A: 

You are right in that WPF gives us a Dispatcher to make multi-threading simple; but if you want to separate concerns with the MVVM pattern, you'll need to implement a different threading strategy.

I've always found the BackgroundWorker class more than enough to satisfy this, pushing updates back to the UI thread, such that you can update the ObservableCollection in the VM and have changes propagate to the view.

IanR
+2  A: 

One option here is to expose a SynchronizationContext usable from within the ViewModel. This is the mechanism the BackgroundWorker class uses to synchronize with the UI without introducing a dependency on WPF or Windows Forms, and allowing it to work with multiple technologies.

This would allow you to marshal back to the UI thread without taking a reference to the UI itself, including the Dispatcher.

Reed Copsey
Not a bad idea, but I wonder about the flexibility of SyncContext, implications of taking a dependency on it etc... Any thoughts?
Schneider
It has a bit of "least common denominator", but Send() and Post() provide the necessary functionality, and it doesn't force a dependency on any specific framework. As I mentioned, this is how BackgroundWorker can function with WPF + Win FOrms without a dependency.
Reed Copsey
A: 

One option is to create a subclass of ObservableCollection that overrides OnPropertyChanged and OnCollectionChanged and dispatches the appropriate events back to the UI thread (through something like SynchronizationContext).

This allows the ViewModel to be more agnostic when it comes to threading, it makes the code under test a lot easier to manage as well.

Nigel Sampson
I am aware of the general solutions to this problem... my question is specifically about MVVM and the whole keeping the ViewModel clear of this sort of thing. So far its looking like the VM needs to have knowledge of Sync/Threading
Schneider
Exacltly, you won't be able to completely remove knowledge of threading from the ViewModel. But it can be "hidden" by a base ViewModel and a subclass of ObservableCollection.By doing this the ViewModel (ViewAccountsViewModel for instance) will be agnostic about threading and therefore a lot more testable.
Nigel Sampson
+1  A: 

This is quite well solved by abstracting all your logic of adding/deleting/updating observablecollections + any other complex logic that in your scenario occurs on different threads.

This controller class can be responsible for updating the ViewModel (It can have a reference to the VM by interface) on the correct thread.

Ray Booysen
Care to give a more detailed example?
Schneider
Hard to do in Stack Overflow. You can imagine a controller class that has all the complex interaction with the model in it. As a property of this controller,is the ViewModel for binding.The controller can react to data on different thread, marshal to the UI thread and then just set properties on the ViewModel since it has a reference to it.Also, any commands executed on the ViewModel, can be propagated up via events to the controller, for it to push the action to another thread (if required)
Ray Booysen
Not many examples of such ModelViewViewModelController on the web, but sounds like it has potential
Schneider