One thing I've done, with some success, is to move the ObservableCollection out of the model. Here's my general pattern:
- In the model objects, expose a property of type
IEnumerable
that gives read-only access to the collection. Use a plain old List
, not an ObservableCollection, as the backing collection.
- For code that needs to mutate the models' collections (add, delete, etc.), add methods to the model object. Don't have outside code directly manipulating the collection; encapsulate that inside methods on the model.
- Add events to the model for each type of change you allow. For example, if your model only supports adding items to the end of the collection, and deleting items, then you would need an ItemAdded event and an ItemDeleted event. Create an EventArgs descendant that gives information about the item that was added. Fire these events from the mutation methods.
- In your ViewModel, have an
ObservableCollection
.
- Have the ViewModel hook the events on the model. Whenever the model says an item was added, instantiate a ViewModel and add it to the ViewModel's ObservableCollection. Whenever the model says an item was deleted, iterate the ObservableCollection, find the corresponding ViewModel, and remove it.
- Apart from the event handlers, make sure all of the collection-mutation code is done via the model -- treat the ViewModel's ObservableCollection as strictly something for the view's consumption, not something you use in code.
This makes for a lot of duplicate code for each different ViewModel, but it's the best I've been able to come up with. It does at least scale based on the complexity you need -- if you have a collection that's add-only, you don't have to write much code; if you have a collection that supports arbitrary reordering, inserts, sorting, etc., it's much more work.