2

In the model of my application I have a list of "parents" each referecing a list of "children" (e.g. a list of footballteams each containing a list of players).

I visualize this list in a tree view. I created a viewmodel-class for the tree and a viewmodel-class for a footballteam (let's call it "footballteamViewModel"). The class for the tree holds an ObservableCollection with footballteamViewModel-items. The items-source for the tree is this ObservableCollection. During initialization, I create for every footballteam in the model a corresponding footballteamViewModel object and add it to the ObservableCollection.

The thing is, that the list of footballteams in my model can be changed from outside of the tree and I want the tree to be updated. So if someone removes a footballteam from my list in the model, I would have to remove the corresponding item in my ObservableCollection of footballteamViewModel-items.

I cannot bind the list of footballteams from the model directly to the view. So I have to update my ObservableCollection in the ViewModel somehow, every time the collection in the model is changed.

My way to handle this is to use an ObservableCollection in the model and register to the collectionChanged-event in the ViewModel, so that I update my ViewModel (the Observable Collection of footballteamViewModel objects) whenever the model-collection is changed. But this does not feel "right". Is there a better way?

While typing this I found another post which describes exactly the same problem: WPF/MVVM: Delegating a domain Model collection to a ViewModel. The answers there encourage me that the way I'm solving this problem is not totally wrong but still I wonder if there is another method.

EDIT: From the first answers that you provided I assume that there is no definite answer to my question. They were all helpful, so it's worth reading them all. I only mark the answer with the reference to the Bindable Linq/Continous Linq/Optics frameworks, because I think it will help other who stumble over my question most.

Community
  • 1
  • 1
schoola
  • 704
  • 6
  • 12

4 Answers4

3

This is one of the more nasty spots of MVVM.

One thing I have done a while ago is create a ViewModelCollection<T> which inherits ObservableCollection<T> and has modificator methods (Add, Remove), that perform operations on both collections,like so:

    public interface IViewModel<T>
{
    T WrappedModel { get; }
}

public class ViewModelCollection<T,M> : ObservableCollection<T,M> where T : IViewModel<M>
{
    private IList<M> _baseCollection;

    public ViewModelCollection(IList<T> baseCollection)
    {
        _baseCollection = baseCollection;
    }

    public override void Add(T objectToAdd)
    {
        IViewModel<M> vm = objectToAdd as IViewModel<M>;
        if (vm != null)
        {
            this.Add(objectToAdd);
            _baseCollection.Add(vm.WrappedModel);
        }
    }

    public override void Remove(T objectToRemove)
    {
        IViewModel<M> vm = objectToRemoveas IViewModel<M>;
        if (vm != null)
        {
            this.Remove(objectToRemove);
            _baseCollection.Remove(vm.WrappedModel);
        }
    }
}

By now I don't do this at all, I just work with Castle Proxies that add the INotifyPropertyChanged functionality to my models - saves a lot of boilerplate code!

Please note, I haven't tested the code, just typed it down from memory.

Sebastian Edelmeier
  • 4,095
  • 3
  • 39
  • 60
2

You said that you cannot bind the model collection directly to the view (which means that the viewmodel needs to make its own ObservableCollection with a copy of what the model collection contains), and additionally that the view needs to be updated live when the model collection changes (which means that the model needs to inform the viewmodel of such changes, and the viewmodel needs to update its internal copy).

All of this doesn't leave much wiggle room really. One variation that might be interesting is making the model's collection a read/write IEnumerable, in which case consumers of the model would be forced to swap it with a new instance whenever they need to modify the collection. In return, the viewmodel's "stay in sync" code can be simplified and sync can be triggered through INotifyPropertyChanged on the collection property.

Jon
  • 428,835
  • 81
  • 738
  • 806
1

Your solution is not wrong at all, but there are some libraries that could help you implement it easier, like BindableLinq, ContinuousLinq or Obtics. You have a discusion about them here. Sadly, none of them seem to be under further development.

My personal experience with Clinq is excellent and i still use it, should work for your case.

Community
  • 1
  • 1
Natxo
  • 2,917
  • 1
  • 24
  • 36
1

Late, but may helps other ppl...

read this excellent 3 Part blog post series about this topic.
Part 3 is about collections and shows some solutions - helps me a lot

MVVM: To Wrap or Not to Wrap? How much should the ViewModel wrap the Model?

Radioactive
  • 91
  • 1
  • 7
  • I would suggest that people **don't** read that blog post - the author is confused about MVVM, and seems to think that a data entity is a model. Or maybe he is just bad at explaining himself. Wrapping data entities in a VM is done in specific circumstances which have little to do with property notification in collections. – slugster Oct 10 '12 at 03:04
  • He show us 3 ways to accomplish the task discussed in this post.(blinq/clinq/self coded). – Radioactive Oct 10 '12 at 11:56