Search code examples
c#.netwpfmvvmcaliburn

MVVM and DI - How to handle Model objects?


I'm using Caliburn and C#, but I feel like this is a generic MVVM/DI question.

Let's say I have a view model, NoteViewModel, that is passed a model object called Note.

Here is some code:

class NoteViewModel : PropertyChangedBase
{
  private readonly Note _note;

  public NoteViewModel(Note note)
  {
    _note = note;
  }

  public string Title
  { 
    get { return _note.Title; } 
    set { _note.Title = value; NotifyOfPropertyChange(() => Title); }
  }
}

Right now this object is created by calling new() and passing a model object.

Well, that works great, but now I need to add a method that requires an imported class from my DI container.

So do I merely call ServiceLocator.Current.GetInstance() to get it? Or should I design this view model to be created via the DI container and somehow setup a way to pass a Note object?

What is the proper way to design this view model? Basically a "PerInstance" view model that requires a model object for it's use. Does Caliburn have a built-in way to do this?


Solution

  • Caliburn has an interface (IHaveSubject and its typed version IHaveSubject) addressing this kind of scenario: basically it allows a mean to configure the ViewModel with a "subject" after its instantiation, tipically through the container:

    class NoteViewModel : PropertyChangedBase, IHasSubject<Note> {
      ...   
    } 
    
    myNoteViewModel = ... //obtain an instance
    myNoteViewModel.WithSubject(new Note());
    

    This solution also integrates well with ISubjectSpecification / Conductor infrastructure.

    Even though post-construction initialization is a simple and effective solution, you may not want (from a pure design perspective) to renounce to an explicit constructor parameter to enforce the need for a Note to istantiate the ViewModel. In this case I think you have to leverage peculiar features of your DI container, because you may have some parameters of the constructor representing a "real" input parameter, while other may be service dependencies.

    Castle Windsor, for example, has a nice feature allowing you to quickly build an explicit (typed) factory for your ViewModel; the factory method will only allow to set the "real" parameters, while all dependencies are managed by the container (see this post for an extensive description of this Windsor feature: http://kozmic.pl/archive/2009/12/24/castle-typed-factory-facility-reborn.aspx)