tags:

views:

47

answers:

2

I'm currently trying to create a small application using the MVVM pattern. However I don't really know how to correctly wrap up aggregated Model classes in my ViewModel. From what little I know about MVVM, you're not supposed to expose Models in your ViewModel as properties or else you could directly bind to the Model from your View. So it seems I have to wrap the nested Model in another ViewModel, but this imposes some problems while synching Model and ViewModel later on.

So how do you do that efficiently?

I'll give a short example. Let's suppose I have the following model classes:

public class Bar
{
    public string Name { get; set; }
}

public class Foo
{
    public Bar NestedBar { get; set; }
}

Now I create two ViewModel classes accordingly, wrapping the Models, but run into problems with the FooViewModel:

public class BarViewModel
{
    private Bar _bar;
    public string Name 
    { 
        get { return _bar.Name; }
        set { _bar.Name = value; }
    }
}

public class FooViewModel
{
    private Foo _foo;
    public BarViewModel Bar
    {
        get { return ???; }
        set { ??? = value; }
    }
}

Now what do I do with the Bar-property of FooViewModel? For "get" to work I need to return a BarViewModel instance. Do I create a new field of that type in FooViewModel and just wrap the _foo.NestedBar object in there? Changes to that field's properties should propagate down to the underlying Bar instance, right?

What if I need to assign another BarViewModel instance to that property, like so:

foo.Bar = new BarViewModel();

Now that won't propagate down to the model, which still holds the old instance of type Bar. I'd need to create a new Bar object based on the new BarViewModel and assing it to _foo, but how do you do that elegantly? It's pretty trivial in this sample, but if Bar is much more complex with lots of properties, that'll be a lot of typing... not to mention it'd be very prone to errors, if you forget to set one of the properties.

A: 

Okay - first things first - using the term Aggregate implies you are adhering to DDD? If you are - you are doing an encapsulation no-no :-). One Aggregate should never be allowed to edit another Aggregate. If what you have is that both are really Aggregate they would become associated (which is perfectly 'legal' in a DDD-sense - but then your propety on the FooViewModel wouldn't be of type BarViewModel, but rather type Bar. That way Bar would (as it should) be responsible for updating itself - and we only maintain the link in FooViewModel.

However, if what you are doing is AggregateRoot with a ValueType child - then here is what you could do given a slightly modified domain model:

public class Foo
{
    public string SomeProperty { get; set; }
    public Bar Bar { get; set; }
    public void Save()
    {
        //Magically saves to persistent storage...
    }
}
public class Bar
{
    public Bar(string someOtherProperty)
    {
        SomeOtherProperty = someOtherProperty;
    }
    public string SomeOtherProperty { get; private set; }
}

And then for the ViewModels:

public class FooViewModel
{
    private Foo _foo;
    public FooViewModel()
    {
        Bar = new BarViewModel();
    }
    public BarViewModel Bar { get; private set; }
    public void SetFoo(Foo foo)
    {
        _foo = foo;
        SomeProperty = foo.SomeProperty;
        Bar.SetBar(foo.Bar);
    }
    public string SomeProperty { get; set; }
    public void SaveChanges()
    {
        _foo.SomeProperty = SomeProperty;
        _foo.Bar = Bar.CreateUpdatedBar();
        _foo.Save();
    }
}
public class BarViewModel
{
    public string SomeOtherProperty { get; set; }
    public void SetBar(Bar bar)
    {
        SomeOtherProperty = bar.SomeOtherProperty;
    }
    public Bar CreateUpdatedBar()
    {
        return new Bar(SomeOtherProperty);
    }
}

This way - the FooViewModel is now capable of controlling the BarViewModel (which does nothing but accept a valuetype - and create a new one when asked). This also solves a common UI-problem ('How do we edit an object that has no setters?' - answer: 'We don't - we create a new one'). A lot of fleshing out is missing (INotifyPropertyChanged, dirty-tracking etc., but those are easy if you get through this leap of thinking :-).

I hope this makes a wee bit of sense :-) Otherwise, I'll be happy to elaborate.

Goblin
A: 

My above answer only makes sense if you are doing DDD - if you are not - you can solve your problem like this - simply 'flattening' the model:

public class FooViewModel
{
    private Foo _foo;
    public string Name
    {
        get { return _foo.Name; }
        set { _foo.Name = value; }
    }
    public string BarProperty
    {
        get { return _foo.Bar.Property; }
        set { _foo.Bar.Property = value; }
    }
}

Or you could do like I showed in the prior example - just ignore everything about Aggregates... should still work.

Goblin