Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to present a Collection of (View)Models in a ViewModel

I have a question regarding the MVVM design for C#/WPF. I've had a look at several demo applications, but they didn't really handle my problem. My application consists of objects that contain other objects. Much like in a parent - children relationship.

My question now is:

  • does the children attribute have to be a ViewModel
  • and if so, how do I create new Parent objects, which contain the existing Child objects via ViewModels?

I have sth like the following scenario:

class Child {
    string Name;
}

class ChildVM {
    Child _child;
    string Name{return _child.Name;}
}

class Parent {
    string Name;
    List<Child> children;
}

class ParentVM{
    Parent _parent;

    string Name{return _parent.Name;}
    List<ChildVM> children {get;set;}

    ParentVM(Parent p){_parent = p;}
}

void CreateANewParent(){
    List<ChildVM> children = new List<ChildVM>(){new ChildVM(new Child()),...};
    ParentVM parent = new ParentVM(new Parent());
    foreach(ChildVM child in children)
        parent.children.Add(child);
}

The problem here is, that the ParentVM contains the ChildVM, BUT the actual Parent (which is inside of the ParentVM) does not have the Child objects contained by the ChildVM objects. I also don't think it's a good idea to duplicate the Child objects, as it leads to redundance and in my application context there is also no need/possibility to create new Child objects.

I also thought about the following class design:

class ParentVM {
    Parent _parent;

    string Name{return _parent.Name;}
    List<Child> children {get{return _parent.Children;}}
}

However, this would mean I would directly operate on the Model if I want to manipulate a ParentVM's Child object.

On the other hand I could simply leave the (Model) Parent blank and use the ParentVM to create a new Parent in the database. But is this a good way of handling the problem?

like image 524
Torsten Avatar asked May 08 '09 09:05

Torsten


1 Answers

Actually, the proper way to do this is when you first create a ParentVM, you iterate through the children of the passed-in Parent, creating a ChildVM for each, then add those ChildVM objects to the ParentVM's ChildVMs property. (Some would just call that property 'Children' but I personally like being clear it's a collection of ChildVMs, not a collection of Child objects. Simply adding the 'VM' suffix makes that very clear.

You then also have to listen for change-notification to the actual Parent's Children collection and update your ChildVMs collection accordingly.

That way you have a model with Parent->Children->Child and a ViewModel of ParentVM->ChildVMs->ChildVM which is I believe exactly what you want.

Now I'm also of the belief that you should be able to expose the Parent directly from the ParentVM as well as the Child directly from the ChildVM since your UI might be binding to various properties on those items, such as your Name property above. However M-V-VM purists would say to never do this saying the UI should never know about the model because if the model changes, you have to change the UI. My argument is that if the model changes, you have to change the ViewModel anyway for the exact same reason. The only savings would be if there are several views all sharing the same ViewModel since you'd just have to change it in one place, but realistically, something like 'Name' isn't going to change it's "name" from the model to the ViewModel so in those cases it's a non-argument anyway.

Plus, there is a performance overhead by doing it the 'purist' way in that you can't simply delegate to the model item like you're doing with Name above because the view won't ever know about any model-generated changes to the Name property unless you also add all the extra change notification inside the VM, meaning you now have one change notification in the model who's sole purpose is to fire a second change notification in the VM that then notifies the UI. Pure? Yes. Performance-invasive? You bet, especially when large amounts of changes are going on and you're using the INotifyPropertyChanged interface because that means you have to do string comparisons in the change handler to detect and delegate all those changes! If you bind directly to ParentVM.Parent.Name property however, you would already have that change notification from the model to notify the UI and you also keep your VM clean for things that are only VM- or View-specific.

What I never do however is place anything in the Model that is view-only info. THAT to me is what the ViewModel is for. So for instance, if the children have a specific color based on an enum or whatever, that to me is something that goes in the ChildVM and not the Model itself, and if there are any properties in the model that dictate that color, like a property of that enum, in that case, yes, I would wire up change-notifications from the model inside the ChildVM. (Truthfully, I may even just do it via a color converter in the UI directly, still binding to the model's enum. It's really a case-by-case thing.)

HTH,

Mark

like image 135
Mark A. Donohoe Avatar answered Oct 07 '22 18:10

Mark A. Donohoe