views:

580

answers:

4

I am wondering about how to approach inheritance with View Models in the MVVM pattern. In my application I have a Data Model that resembles the following:

class CustomObject
{
    public string Title { get; set; }
}

class CustomItem : CustomObject
{
    public string Description { get; set; }
}

class CustomProduct : CustomItem
{
    public double Price { get; set; }
}

In my application I have a ViewModelBase class and then was going to have the following View Models:

  • CustomObjectViewModel
  • CustomItemViewModel
  • CustomProductViewModel

A rough implementation of the CustomObjectViewModel would resemble the following:

class CustomObjectViewModel : ViewModelBase
{
    private readonly CustomObject _customObject;

    public CustomObjectViewModel( CustomObject customObject )
    {
        _customObject = customObject;
    }

    public string Title
    {
        // implementation excluded for brevity
    }
}

It seems logical to me that my View Models would extend themselves in the same manner as my Model did (CustomItemViewModel extends CustomObjectViewModel and so on). However, I have noticed that as I go down the inheritance tree I'll be adding additional references to the same object. This seems rather excessive to me and was wondering how to approach this problem and if it were possible to make it much cleaner.

+1  A: 

I'd be interested to see if there is a better answer for this, but when I have had the same problem I've always enclosed an explicit cast of the object as a private property like so:

class CustomObjectViewModel : ViewModelBase
{
    protected readonly CustomObject CustomObject;

    public CustomObjectViewModel( CustomObject customObject )
    {
        CustomObject = customObject;
    }

    public string Title
    {
        // implementation excluded for brevity
    }
}

class CustomItemViewModel : CustomObjectViewModel 
{
    protected CustomItem CustomItem  { get { return (CustomItem)CustomObject; } }

    public CustomItemViewModel( CustomItem customItem )
        :base(customItem)
    {
    }
}

It works, and it's the best I've come up with, but has never felt very clean to me.

Martin Harris
I think it's actually a good idea... this way you don't replicate the model object in inherited ViewModels
Thomas Levesque
+4  A: 

Generally I would recommend you not to have inheritance between different ViewModel classes, but instead having them inherit directly from a common abstract base class.
This is to avoid introducing unnecessary complexity by polluting the ViewModel classes' interfaces with members that come from higher up in the hierarchy, but are not fully cohesive to the class's main purpose.
The coupling that comes with inheritance will also likely make it hard to change a ViewModel class without affecting any of its derived classes.

If your ViewModel classes always will reference a single Model object, you could use generics to encapsulate this rule into the base class:

public abstract class ViewModelBase<TModel>
{
    private readonly TModel _dataObject;

    public CustomObjectViewModel(TModel dataObject)
    {
        _dataObject = dataObject;
    }

    protected TModel DataObject { get; }
}

public class CustomObjectViewModel : ViewModelBase<CustomObject>
{
    public string Title
    {
        // implementation excluded for brevity
    }
}

public class CustomItemViewModel : ViewModelBase<CustomItem>
{
    public string Title
    {
        // implementation excluded for brevity
    }

    public string Description
    {
        // implementation excluded for brevity
    }
}
Enrico Campidoglio
I've used the generics pattern as illustrated here, and I have to say, I haven't really had any problems with having an inheritance hierarchy for ViewModels.
Samuel Jack
It will surely work without issues in many cases. However in my opinion I see ViewModel classes as being tightly coupled with Views. Inheritance between ViewModel classes would make sense if their associated Views share some common UI.
Enrico Campidoglio
A: 

Related to the comment above of Enrico. ViewModels should not be tightly coupled with views, it should be the other way around. Views should be loosely coupled to ViewModels. A ViewModel should not know about a view, this allows you to unit test a ViewModel easily.All interactions between Views with the ViewModel should be implemented via properties in the ViewModel(ICommand properties for actions and other properties for databinding).

The one thing that is true is that the ViewModel is tightly coupled with the Model, so the use of generics above allows alot of extensibility. It is the pattern that I would recommend.

By providing a ViewModel class that basically just exposes Properties it should allow you to plop that into any kind of presentation framework and leverage all the code you've used previously. In other words, if properly implemented you could drop your ViewModels assembly into a ASP.NET MVC app and tie the view to the properties and have no code change.

A good article on MVVM basics is : this one. I really think that MVVM is the best thing out there for UI development. Obviously we can't all use it because it requires building an app from the ground up using the MVVM approach, but when you're building a new app that is not an issue.

The one gripe I have with ICommand is that it is in the PresentationCore Assembly which is basically for WPF. If Microsoft wanted loose coupling it should be in another assembly altogether.

Jose
Let me clarify my point: with "coupled" I don't mean that the ViewModel class should have any dependency on the View. However it should expose data/behavior that is relevant to what is displayed by the particular View it is being used with
Enrico Campidoglio
+1  A: 

I think the issue here is that there should be one ViewModel for each View, and not one ViewModel for each model.

The reason for this is fairly obvious, as you can only set one object to be the DataContext, that object should be the ViewModel for that View.

Cameron MacFarland