In the ViewModel, aka the MVVM pattern, a view such as a UserControl is bound to its associated view model. The view model manages state exposed as observable properties, operations exposed as methods and raises notifications via events. An interesting question is how a View should specify its view model, and how a view model instance should be created. Like everything else in the ViewModel pattern, this topic is no different. There are different strategies, with pros and cons, and different people have different opinions.
In this post, I'll describe a couple of options, and hope to collect some feedback as well as ideas on alternatives. Part of my goal here is to improve what is supported in Silverlight.FX out-of-the-box. The other goal is to find what resonates, and see if I can collect input on suggestions I'd like to make for future ViewModel support in Blend/Cider - so any thoughts you share will certainly help. Looking forward to hearing them...
My general criteria for evaluating each approach is the following:
- It should be possible to create the view model through an IoC container, so its dependencies (properties, or constructor arguments) should be satisfiable. In other words, its likely a view model can't be instantiated directly in XAML... at least not in the subset of XAML supported by Silverlight today.
- The mechanism should not interfere with designability either in Blend.
- Furthermore, the mechanism should even lend itself to designability. For example, the data-binding picker should allow binding to properties on the view model.
Current/Default Silverlight.FX Approach
Silverlight.FX offers classes such as Form, Window, Page and ViewUserControl that derive from a View base class that in turn derives from UserControl. The View class exposes a ModelType property, and uses the application-scoped IoC container to create an instance of the view model, so that any dependencies of the view model can be satisfied during instantiation. It sets the resulting model as the DataContext, and it also sets it as the Model associated with the View.
The markup looks something like this:
<fxnav:Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:fxnav="clr-namespace:SilverlightFX.UserInterface.Navigation;assembly=SilverlightFX"
xmlns:views="NewsWidget.Views.News"
ModelType="NewsWidget.Views.News.ListPageModel"
d:DataContext={d:DesignInstance Type=views:ListPageModel}">
</fxnav:Page>
This satisfies criteria #1 in wish-list where the View has a type and it can control when and how to create the view model instance.
However it doesn't lend itself well to criteria #2. Blend doesn't like this syntax. At design-time, the Blend parser wants a valid Type for the ModelType property. However at runtime, Silverlight doesn't support properties of type System.Type... well not yet. I did create a user voice item here - go vote for that if you care about being able to declare properties of type Type in XAML.
To satisfy criteria #3, i.e. the binding picker, d:DataContext needs to be set in addition (as shown), which is unfortunate duplication.
Other options are available in Silverlight.FX, but this is what I've been using by default in my samples. I am looking to improve it which brings me to two alternatives: ViewModelLocator and a ViewModel-via-Convention as described below
Quick aside on DataContext and Model
Having the notion of Model as introduced by the View base class in addition to standard DataContext is very useful. Silverlight.FX implements this, but I haven't seen other view model frameworks implement something similar. Having a concrete notion of Model allows any control in the view to access the view model even if it has a different DataContext. Imagine individual items in an ItemsControl - each has a different DataContext, but still share the same Model, which allows invoking methods on the view model from say a Button within the item. Example: You have a list of products, and the template for a product contains a Buy button, that when clicked, you want to call into the Buy method of the view model passing in the product.
ViewModel Locator
Arguably this is the most prevalent approach out there today. The idea is reasonably straightforward. You write an actual class called the ViewModelLocator, which exposes the different view models in your application as properties. Your implementation of the property getter now controls how to create the view model, for example, by invoking the IoC container, using MEF, or any other mechanism, and you have full control. Next you declare an instance of this locator in your application's resources, and then bind your view's DataContext to it.
public class AppViewModelLocator {
public ListPageModel ListPage {
get {
// code to create and return an instance
}
}
}
<Application>
<Application.Resources>
<app:AppViewModelLocator x:Key="vml" />
</Application.Resources>
</Application>
<fxnav:Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:fxnav="clr-namespace:SilverlightFX.UserInterface.Navigation;assembly=SilverlightFX"
DataContext={Binding ListPage, Source={StaticResource vml}, Mode=OneTime}">
</fxnav:Page>
Pros:
- It allows full control over creation of the view model instance.
- It keeps the designer happy, and binding pickers also work as expected.
Cons:
- It requires me to write an AppViewModelLocator class and furthermore, each time a new view model is added to the project, the locator class needs to be updated.
- Views now depend on the key assigned to the ViewModelLocator resource. This works in small applications, but introduces less than ideal string name interdependencies when you have views spread across projects in a larger application.
Convention, rather than Configuration
In all my app-building I've noticed I end up having 1:1 correspondence between my View and its associated ViewModel. Yes, this isn't necessarily true in general with the ViewModel pattern, but I've also found the best way to explain the ViewModel pattern to those new to it are to explain it as a refactoring of code-behind into a separate class that isn't coupled to UI concepts. As such it becomes 1:1. What is more, I've also been using a naming convention. If I have a view called FooPage, then I have a corresponding view model called FooPageModel.
If I have that convention baked in into the View base class in Silverlight.FX, I would not need to specify the Model explicitly. This would be all the markup I need:
<fxnav:Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:fxnav="clr-namespace:SilverlightFX.UserInterface.Navigation;assembly=SilverlightFX">
</fxnav:Page>
Much simpler!
Pros:
- Still allows control over creation of the view model instance by letting the View create one via a container.
- Blend is happy.
- Doesn't require me to write a ViewModelLocator class.
Cons:
- I still have to specify d:DataContext to get the binding pickers to work. This is not a non-starter however, as the designer might very well be using sample design-time data rather than the view model itself at design-time. I still do wish the designer would also understand and honor this convention, and automatically infer a design-time DataContext if nothing is specified. My hope is to convince the Cider and Blend folks on thinking about this approach. What are your thoughts on a convention-based approach like this one? Should I pursue and run this by the designer teams?
Time for Your Opinions
Given both alternatives aren't without some cons, which do you think is preferable? If I optimize for the simple case today, the ViewModelLocator is slightly better. If I optimize for larger scenarios, and look forward optimistically, the convention-based approach seems like the better long term approach.
Or do you have your own approach or some variant of the above that you'd like to share for consideration?