MVVM with aggregate model classes - how to wrap in ViewModels?

I'm currently trying to create a small application using the MVVM pattern. However, I do not know how to properly complete the aggregated model classes in my ViewModel. From the fact that little is known about MVVM, you should not show models in your ViewModel as properties, otherwise you can directly bind to the model from your view. So it seems to me that I should wrap the nested model in another ViewModel, but this poses some problems while synchronizing the Model and ViewModel.

So how do you do this efficiently?

I will give a brief example. 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 respectively, wrapping Models, but running into problems with 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 should I do with the FooViewModel Bar property? For get to work, I need to return an instance of BarViewModel. Create a new field of this type in FooViewModel and just wrap the _foo.NestedBar object? Changes to the properties of this field should propagate to the base instance of Bar, right?

What if I need to assign another instance of BarViewModel to this property, for example:

 foo.Bar = new BarViewModel(); 

Now this will not apply to the model, which still stores the old instance of the Bar type. I need to create a new Bar object based on the new BarViewModel and comprehend it on _foo, but how do you do it elegantly? This is pretty trivial in this example, but if Bar is much more complicated with many properties, it will gain a lot ... not to mention that it will be very error prone if you forget to set one of the properties.

+4
source share
3 answers

My answer above makes sense only if you do DDD - if you don't - you can solve your problem as follows: just “smooth out” 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 as I showed in the previous example - just ignore everything about Aggregates ... should still work.

+3
source

@Goblin

There are some flaws in the code: for example. What if I get a list of Foo objects from the database and I want to wrap them in an ObservableCollection?

then your FooViewModel constructor should accept the Foo model as a parameter, and not create it inside the constructor!

Usually you do this to wrap the model in the viewmodel and place it at the same time in the bindable Collection:

 IEnumerable<Foo> foos = fooRepository.GetFoos(); foos.Select( m => viewmodelCollection.Add(new ViewModel(m,egService))); 

Model properties are not copied to ViewModel hell no !!! ViewModel delegates its properties to model properties, for example:

 public class FooViewModel { private Foo _foo; public FooViewModel(Foo foo,IService service) { _foo = foo; } public string FoosName { get{return _foo.Name }; set { if(_foo.Name == value) return; _foo.Name = value; this.NotifyPropertyChanged("FoosName"); } } } 

And, as Goblin said, all UI-Specific interfaces, such as:

 IDataErrorInfo INotifyPropertyChanged IEditableObject 

etc...

implemented ONLY by ViewModel.

+7
source

Well - first at first - using the term Aggregate, do you mean that you adhere to DDD? If you - you do no-no encapsulation :-). You cannot allow one unit to edit another unit. If you have the fact that both of them are really combined, they will become associated (which is quite "legal" in the DDD sense), but then your project on FooViewModel will not be of type BarViewModel, but rather of type Bar. (as it should) be responsible for the update itself - and we only communicate in the FooViewModel.

However, if you do this AggregateRoot with a child of ValueType, then here is what you could do, given the 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 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 method - now the FooViewModel is able to control the BarViewModel (which takes nothing but the valuetype attribute, and creates a new one when asked). It also solves a common problem with the user interface ("How to edit an object that does not have setters?" - the answer: "We do not do this - create a new one"). There is a lot of phishing (INotifyPropertyChanged, dirty tracking, etc., but it's easy if you go through this leap of thinking :-).

Hope this makes a little sense :-) Otherwise, I will be happy to develop.

+1
source

All Articles