views:

324

answers:

3

A domain model collection (normally a List or IEnumerable) is delegated to a ViewModel.

Thats means my CustomerViewModel has a order collection of type List or IEnumerable.

No change in the list is recognized by the bound control. But with ObservableCollection it is.

This is a problem in the MVVM design pattern.

How do you cope with it?

UPDATE: Sample of how I do it:

 public class SchoolclassViewModel : ViewModelBase
{
    private Schoolclass _schoolclass;
    private ObservableCollection<PupilViewModel> _pupils = new ObservableCollection<PupilViewModel>();        

    public SchoolclassViewModel(Schoolclass schoolclass)
    {
        _schoolclass = schoolclass;
        _schoolclass.Pupils = new List<Pupil>();

        foreach (var p in schoolclass.Pupils)           
            Pupils.Add(new PupilViewModel(p));            
    }

    public Schoolclass GetSchoolclass 
    {
        get { return _schoolclass; } 
    }

    public int ID { get; set; }       

    public string SchoolclassName
    {
        get { return _schoolclass.SchoolclassName;}
        set
        { 
            if(_schoolclass.SchoolclassName != value)
            {                    
                _schoolclass.SchoolclassName = value;
                this.RaisePropertyChanged("SchoolclassName");
            }

        }
    }   

    public ObservableCollection<PupilViewModel> Pupils
    {
        get{ return _pupils;}
        set
        {
            _pupils = value;
            this.RaisePropertyChanged("Pupils");
        } 
    }
}
+1  A: 

I deal with this by not doing it the way you describe.

If I need to present a Foo object and its related Bar objects in the view, the FooViewModel will generally implement a Bars property of type ObservableCollection<BarViewModel>.

Note that this is irrespective of whether or not the underlying Foo class has a Bars property of type IEnumerable<Bar>. The Foo class might not. The application might not even need to be able to iterate over all of the Bar objects for a Foo, except in the UI.

Edit

When my view is a simple representation of the application's object model, I pretty much do things as you do in your sample. The code in my constructor is generally a bit more compact:

_Bars = new ObservableCollection<BarViewModel>(
   _Foo.Bars.Select(x => new BarViewModel(x)));

but it's essentially the same thing.

But this assumes that Foo actually exposes a Bars property. It might not. Or maybe only some Bar objects should appear in the view. Or maybe they should appear intermingled with other objects, and the FooViewModel should expose a CompositeCollection of some kind.

The point I'm making is that the view model is a model of the view. This doesn't necessarily have a direct correspondence to the underlying object model.

To pick a simple example: My program may give the user a way of putting items into five different categories by dragging and dropping them into five different ListBox controls. Ultimately, doing this sets a Category property on the Item object. My view model is going to have a collection of CategoryViewModel objects, each with a property of type ObservableCollection<ItemViewModel>, so that dragging items back and forth between collections is simple to implement.

The thing is, there may not even be a Category class in the application's object model, let alone a collection of Category objects. Item.Category might just be a property of type string. The CategoryViewModel isn't mirroring the application's object model. It only exists to support the view in the UI.

Robert Rossney
@RobertYour comment sounds interesting and confusing to me the same time.I have updated the initial question with a code sample how I do it. Can you describe your way with my sample somehow? I do not understand everything what you described /:
msfanboy
@Robert: I think you are confirming his complaint, which is that every time you want to use a collection from your domain model in your viewmodel, you have to project it onto an ObservableCollection (and project it back if you want to do something like save it).
Phil Sandler
Sure. But the complaint is really, "I want to provide a non-trivial user interface, and my domain model doesn't implement property-change notification and value conversion and commands." If you want to implement that stuff in your domain model, you can, and then refactor it out into a view model once you realize that coupling the view to the domain model causes a lot of problems that you'd rather not have. Creating view models is just doing that refactoring in advance.
Robert Rossney
Given that ObservableCollection has been moved out of the WPF namesapce in .Net 4, I suppose there is no reason your model couldn't implement its collections as an OC. Sure you want your Models to be fairly lightweight, of course if thats the case you should be using arrays, I doubt OC is that much heavier then a List.
Agies
@AgiesThe reason why I can NOT use ObservableCollection in the model is because I get data from a datacontext which returns aggregated models of type IEnumerable => http://loungerepo.codeplex.com/
msfanboy
"...@Robert: I think you are confirming his complaint, which is that every time you want to use a collection from your domain model in your viewmodel, you have to project it onto an ObservableCollection (and project it back if you want to do something like save it)...."@PHILgreat you have really recognized the problem I am having. I hope the other also understand it now!
msfanboy
So these Model object are actually your entity objects? That changes the issue greatly, I thought these were DTOs that you were spawning from your Entity objects.
Agies
@Agiesyes my entity objects there are!How does this change the issue greatly?Regarding the new wisdom you gained what would you advise me Agies? :)
msfanboy
A: 

Ok, I'll go ahead and add my thoughts as an answer instead of in the comments. :)

I think the bottom line is that this is just the reality of the way WPF and databinding work. In order for two-way databinding to operate, collections need a means of notifying controls that are bound to them, and the standard lists and collections used in most domain objects don't/won't/shouldn't support this. As I mentioned in a comment, being required to implement INotifyPropertyChanged for non-collection properties is another requirement that may not be met by a standard domain object.

Domain objects are not intended to to be viewmodels, and for this reason you may find that you need to map back and forth between the two types of objects. This is not dissimilar to having to map back and forth between domain objects and data access objects. Each type of object has a different function in the system, and each should be specifically designed to support their own role in the system.

All that said, Agies's idea of using AOP to automatically generate proxy classes is very interesting, and something I intend to look into.

Phil Sandler
aren`t these proxy classes just a clone of the domain models? like ViewModel is most times a clone of a domain model!?
msfanboy
You might say a "representation" or a "projection" but not a "clone", and for exactly the reasons above. The viewmodel addresses a different concern than the domain object does, just like a domain object addresses a different concern than a DAO. In some cases, if the needs across layers are really that similar, it might be possible to use the same objects. For example, you might have your domain objects implement INotifyPropertyChanged. Completely supporting two way databinding in your lists might be more challenging (again, you might look at AOP).
Phil Sandler
well atm I have 3 aggregated domain models. They are all supporting 2way databinding, PropertyChanges and CollectionChanges.Now need for ViewModels as they would be a pure copy of the models, that would make no sense at all!
msfanboy
A: 

What I do is instead of using ObservableCollection in my domain model is use my own collection type that implements the INotifyCollectionChanged interface amongst other useful standard and custom interfaces. My way of thinking is that much like Rockford Lhotka suggests in his book that change notification is useful in to more than just a presentation layer since other business objects within the domain layer often need some sort of notification when state changes in another object.

With this methodology you could create your own collection type that still has the benefits of change notification and as well as what ever custom behaviors you need. The base class for your collection could be implemented as purely infrastructure code and then a subclass could be created that could contain business logic using the subtype layering techinque used in this book. So in the end you could have a collection that can wrap types of IEnumerable<> and provide the change notification stuff your looking for as well for both your domain model and presentation code.

jpierson