1

Implementing the MVVM pattern in C#: How can I create a ViewModel instance, given a Model object whose exact type is only known at runtime?

I have an abstract ModelBase class and an abstract ViewModelBase class as well as several derived classes, e.g. FirstModel : ModelBase, SecondModel : ModelBase and so on, as well as FirstViewModel : ViewModelBase, SecondViewModel : ViewModelBase etc.

Now I want to implement a function that creates the appropriate view model for a given model object. Something along these lines:

ViewModelBase CreateViewModel(ModelBase someObject)
{
    return new ViewModelBase(someObject);
}

The above code does not work of course, because ViewModelBase is abstract. I rather want to create a new FirstViewModel, SecondViewModel and so on depending on the type of someObject which is only known at runtime.

How would I achieve this?

bovender
  • 1,838
  • 15
  • 31
  • You need to use reflection. Check this thread: http://stackoverflow.com/questions/981330/instantiate-an-object-with-a-runtime-determined-type – Shawn Song Jul 29 '15 at 03:30
  • Normally any 1-to-1 relation between Model and ViewModel is coincidence. – H H Jul 29 '15 at 07:59
  • @HenkHolterman I don't understand what you mean. Do you imply that normally there is one ViewModel class for several different Models? Doesn't this make things complicated if Models have different properties? For example, it is obviously impractical for a ViewModel to be able to wrap both a Car and a House. I must be missing your point. – bovender Aug 01 '15 at 04:03
  • A ViewModel shouldn't _wrap_ a Model, it should _support_ one or more Views. What if when your view is about parking Cars in (near) Houses, which model do you pick? – H H Aug 15 '15 at 15:46

2 Answers2

1
static Dictionary<Type, Type> TypeMap = new Dictionary<Type, Type>
{
    {typeof(ModelA), typeof(ViewModelA)},
    {typeof(ModelB), typeof(ViewModelB)},
    {typeof(ModelC), typeof(ViewModelC)}
};

static ViewModelBase CreateViewModel(ModelBase someObject)
{
    return Activator.CreateInstance(TypeMap[someObject.GetType()]);
}
Mark Feldman
  • 15,731
  • 3
  • 31
  • 58
  • 1
    Although I think this is the correct answer to my problem, it made me realize that it can get quite cumbersome to maintain the dictionary declaration as I add more models and ViewModels... – bovender Aug 01 '15 at 04:00
  • I agree. You could solve this "loosely" by calling ToString() on the model type and fudging the string, although that locks you into a specific nomenclature. Another option would be to decorate your viewmodels with a custom attribute and use it to generate your table automatically, i.e.: [Model Class="ModelA"] class ViewModelA {...etc...}. – Mark Feldman Aug 01 '15 at 04:19
1

It wouldn't be wise to allow any model to be coupled with a view model, simply because of type safety. For example, if your view model expects a Movie, and instead, at run-time, you get a Genre, how can the view model be expected to behave using this model?

Maybe I'm completely missing your requirements, but you could instead use a generic view model base:

public class ViewModel<TModel> : ViewModelBase
    where TModel : ModelBase
{
    public TModel Model { get; protected set; }

    public ViewModel(TModel model)
    {
        Model = model;
    }

    ...
}

You can then create your view models based on their mapping. For example:

public class MovieViewModel : ViewModel<Movie>
{
    ...
}

public class GenreViewModel : ViewModel<Genre>
{
    ...
}
Mike Eason
  • 9,525
  • 2
  • 38
  • 63
  • Thanks for the suggestion, I like this idea. Type safety in my case is provided by the inheritance pattern -- the ViewModel factory expects the Model to be a descendant of ModelBase -- I should have pointed out that `ModelBase` in my example is not the godfather of all my models, but the common ancestor of a couple of specialized classes. – bovender Aug 01 '15 at 03:58