views:

191

answers:

4

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?

A: 

Can you solve it using hierarchical view models?

To me it becomes more and more clear that I need one ViewModel per View and one ViewModel per model item or collection when building larger application.

That way we can build up ViewModels hierarchically, matching the XAML hierarchy.

The required objects can be defined or injected at the top level by app's main view model then. The nested view models can then access anything the way you design it to make things reachable by them.

About Caliburn, I don't know any specific things about that framework, sorry.

herzmeister der welten
+3  A: 

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)

Marco Amendola
Looks good, I will try and post my results.
Jonathan.Peppers
I'm using a pretty new svn checkout of Caliburn with WPF. I have found Screen<T> has this functionality, but cannot find IHasSubject<T>, has the interface name changed?
Jonathan.Peppers
It is IHaveSubject, actually. It's in Caliburn.PresentationFramework.Screens namespace.
Marco Amendola
IHaveSubject works so far for me. Any suggestions for when you have to pass 2 model objects? Just upgrade to Castle Windsor in that case? I am working on a project with little control over the model, and I can bet this will happen.
Jonathan.Peppers
Again, it seems to me a design/modeling concern: if you want to enforce the VM to be created against one or more Model specified in the construction phase, you should go with a factory pattern. This is very elegant and intention revealing; it also will avoid you to forget calling the initialization method.I believe that a similar design could be achieved with most of existing DI containers; if you choose Windsor (which I strongly recommend) you might have a look at the sample application indicated by Krzysztof.
Marco Amendola
On the other side, you can keep the current design creating a composition of the Models you want to set as a subject; the composite model will hold references to whatever Model you need to pass to your VM.This will better model scenarios in which the subject of the VM is intended to change during its lifetime.
Marco Amendola
A: 

I'm using the ServiceLocator also. And I also "feel dirty" in doing this. But I have resolved to use the YAGNI principle and keep this pattern until I find a compelling payback to the complexity of adding 5 IServices into my constructors, passing them up via 3-4 layers of inheritance to the base classes in which they are needed, and creating everything via the container. Of course my app is evolving, and YAGNI doesn't always last...

David Lynch
A: 

I suggest you have a look at sample Castle Windsor application created by Hadi Eskanderi that shows nicely how to do it.

Krzysztof Koźmic