ViewModel and Dependency Injection

In my last post – Silverlight Navigation Part 3 – I outlined a mechanism whereby you could navigate to a View and have the application create and wire up the associated ViewModel automatically. I also showed the inverse of this, where you could navigate to a ViewModel and have the application automatically create and wire up the associated View automatically.

Whether you prefer the View-First or the ViewModel-First approach, the essential feature that really supports the ViewModel pattern is that the system should be able to create a ViewModel for a View (or vice versa) automatically. Otherwise, the burden falls to the developer to do this in code, which can result in a complicated architecture that’s either inflexible or difficult to understand and maintain.

Let’s say, for example, that you have adopted a View-First approach. Now, you could just have some code in your application (say, in a controller component or event handler somewhere) that simply creates the required View and it’s associated ViewModel and wires them together like this:

 public ShowCustomer()
 {
     CustomerView view = new CustomerView();
     CustomerViewModel viewModel = new CustomerViewModel();
     view.DataContext = viewModel;
  
     myContentControl.Content = view;
 }

You can use this approach with a ViewModel-First approach too. Another simple approach is to just get the View to create its own ViewModel, either in its constructor:

 public partial class CustomerView : UserControl
 {
     public CustomerView()
     {
         InitializeComponent();
         this.DataContext = new CustomerViewModel();
     }
 }

or via XAML:

 <UserControl.DataContext>
     <CustomerViewModel />
 </UserControl.DataContext>

All three of these approaches work great, except that they hard code your application to use specific View and ViewModel types. This may very well not be an issue for you, and if it isn’t, I say go for it!

However, there are times when you’ll want to have the View and the ViewModel types more loosely coupled. An example might be when you want to decide at run-time which View to display for a particular ViewModel based on, say, the user’s role, chosen UI theme, screen size, or even the type of device that the user is using(!). So, while the approaches above are simple, they are relatively inflexible.

Dependency Injection To The Rescue?

It is at this point that the Dependency Injection folks will start to gesticulate wildly and remind us that this is exactly the situation in which DI excels. Remember that with DI, if you have a dependency between one class and another, the system (i.e. the DI container) can automatically fulfill that dependency dynamically at run-time for you. This allows the classes to be more loosely coupled and removes the need for you to manage the creation of dependent classes yourself. Let’s see how this works…

Say we have a class ‘A’ that depends on (i.e. has a reference to) a class ‘B’. Typical DI containers (including Unity) require that we express the dependency between A and B using a type – typically (but not necessarily) an intermediate interface type, say IB – and that we define a constructor on A that takes a reference to that type. We then register our implementation of the intermediate interface type with the container – so that it knows that whenever anybody wants an implementation of IB it will create an instance of B.

So, when we want an instance of class A, we just ask the container to create one for us. It spots that class A requires a reference to an IB implementation, so it goes and creates a B for us automatically and passes a reference to it to A’s constructor. The container manages the creation of dependent classes for us!

It is of course true that in many situations DI is a good solution for this kind of problem. I think though, that for the ViewModel scenario DI is not ideal. To illustrate why, let’s see how DI typically works in View-First and ViewModel-First scenarios…

The Perils of IViewModel

In our View-First example above, we have a View that wants a reference to (depends on) a ViewModel, so we’d first register an implementation of our ViewModel with the container:

 _container.RegisterType<ICustomerViewModel, CustomerViewModel>();

and we’d design the View so that it gets a reference to the ViewModel implementation in its constructor:

 public CustomerView( ICustomerViewModel viewModel )
 {
     this.DataContex = viewModel;
 }

Finally, we’d get the container to create an instance of the View. The container will automatically create an instance of the registered ViewModel type and pass a reference to it into the View’s constructor.

 CustomerView view = _container.Resolve<CustomerView>();

Ok this works, but I think there are a number of problems with this approach:

  • To create the View we have to use the DI container. We can’t just create the View using ‘new’ and we can’t create it declaratively via XAML (since that will use the default constructor which won’t pass in a reference to the required ViewModel). In other words, we have to have a controller or event handler or some code somewhere that creates the View via the container by calling Resolve.
  • The UI designer (even if the UI designer is really a developer) should not have to write code in the View just to make it work. If there is some complicated UI behavior that can’t be done declaratively, then code may be required, but this should be a special case and not required every time just to get stuff wired up and working. Even implementing the simple constructor above takes some explanation and can be easily forgotten or incorrectly implemented.
  • We have defined a ‘marker’ interface type that we don’t really need – i.e. the interface has no properties or methods, we’re just using it as a marker. Yes, we could define some properties and method on it, but the View should not be programmatically calling into the ViewModel. Remember, UI designers should not have to write any code. All interactions between View and ViewModel should be done via bindings and attached behaviors so that the View is just a loosely coupled observer of the ViewModel.

The Evils of IView

So that’s Dependency Injection in a View-First world. What about a ViewModel-First world? Well, things are no better here either. Similar to the process outlined above, we’d probably define an IView interface; register an implementation of it with the container; define a constructor on the ViewModel that accepts an IView reference; then create an instance of the ViewModel using the container:

 _container.RegisterType<ICustomerView, CustomerView>();
 ...
 public CustomerViewModel( ICustomerView view )
 {
     ((FrameworkElement)view).DataContext = this;
 }
 ...
 CustomerViewModel viewModel = _container.Resolve<CustomerViewModel>();

Again, a couple of problems with this approach:

  • Again, we can’t create a View declaratively using XAML – the container will create the View as a result of creating an instance of the ViewModel class – so we’d have to have a controller or event handler or some code somewhere to kick things off.
  • We’d have a marker interface that we’d don’t really need and that won’t define any properties or methods – the ViewModel should not be programmatically calling into a View.
  • The UI designer would have to implement an interface on the View each and every time they create one. It’s not terribly complicated, but the UI designer should not be required to write any code except for advanced scenarios.
  • The ViewModel-First approach is actually slightly worse that the View-First approach because of the nasty cast in the constructor. This is required in order to set the view’s data context to the ViewModel.

Wouldn’t It Be Nice If…

In my last post on navigation, I showed a couple of different approaches to the problem of conjuring up a View for a ViewModel (or vice versa) without using a Dependency Injection approach.

  • For View-First, instead of using DI and defining interfaces and constructors, I just used an attached property on the View to specify the type name of the ViewModel (ViewModel.TypeName) and the system automatically constructed the ViewModel for me and wired it up to the View’s DataContext.
  • Going the other way (ViewModel-First) , I just used a naming convention to infer the name of the View from the ViewModel’s type name, created an instance of that View and set it’s DataContext.

Neither of these approaches are ideal though, so I got to thinking what an ideal approach might look like… The key is that we need a simple way of expressing the relationship between the View and the ViewModel types so that the system can just make the right things happen!

If you’re familiar with DataTemplates in WPF, you know that it’s possible to define a DataTemplate (somewhere in the application’s resources) for a particular (non UI) type. Whenever the system has to display an object of that type, the DataTemplate is automatically used to visually represent it. Under the covers, the system creates an instance of the DataTemplate, sets its DataContext to the object that it represents, and then displays it in the UI. Silverlight does not yet support implicit data templating – you have to specify the DataTemplate explicitly for each control – but the mechanism is very similar.

I think this is close to what we want…

I think of DataTemplates simply as “Thin Views” – i.e. purely declarative Views with no code behind. They are very useful, but sometimes you just need to have some code in the View (for complex visual behavior that can’t be done declaratively). So, if the system could be extended to include Views that could have code behind, it would all fit together in a nice, simple, consistent model. It could look something like this…

We define a View as usual and simply declare that it is associated with a ViewModel data type:

 <UserControl x:Class="MySample.CustomerView"
              DataType="{x:Type MySample.CustomerViewModel}"

For the ViewModel-First approach, we simple create (or navigate to) an instance of the ViewModel and display it. For example,

 MyContentControl.Content = new CustomerViewModel();

The system would then step in and create an instance of the View that corresponds to this type and would wire up the DataContext for us – just like the system does today with DataTemplates!!!

Similarly, for View-First, where we simply create (or navigate to) a View, the system would automatically create an instance of the associated ViewModel type for us and wire up the DataContext. There would likely be another property to control this behavior.

I think this approach has a number of benefits:

  • The system carries the burden of finding and creating corresponding Views and ViewModels, making our application code much, much simpler. And simpler means that it’s faster to write, easier to debug, and easier to unit test!
  • It gives us a single consistent model that works for Views with and without code behind.
  • It works declaratively as well as programmatically giving us a lot of freedom to create Views or ViewModels in code or in XAML as desired. It works great with navigation too.
  • It allows Views to express a dependency on a particular ViewModel, while allowing ViewModels to remain completely View agnostic. This opens up a whole host of interesting design-time tooling possibilities – like you get with DataTemplates today – but with more explicit support for the ViewModel pattern. For example, you can imagine that the UI designers can provide intelli-sense or drag & drop support for binding expressions and behaviors (because the design-time system can determine the ViewModel’s commands, notifications and properties).

You’ll have noticed that we’re not using an intermediate interface type to loosely couple the View and ViewModel. The approach could be extended to support that though, for example by using the upcoming Managed Extensibility Framework (MEF), or by building in some form of simple Dependency Injection mechanism into the XAML loader system.

Until I prototype this approach more fully, it’s not clear whether it will really work as well as expected. Also, it’s not clear at this stage when (or if) we could expect it this approach to be implemented in the platform.

We can but dream though…