December 13, 2010 6 Comments
These days design pattern #1 seems to be the MVVM pattern, at least in the Silverlight/WPF community. As soon as you start to dig deeper into MVVM you get in touch with another design pattern, the ViewModelLocator which is basically the ServiceLocator pattern but instead of locating Services it helps us to locate our ViewModels. Just bing it and you will find a wide variety of implementations. The only reason why I came up with my own implementation is simply because I couldn’t find one which meets my requirements:
- Support for design-time data
- As less as possible code to wire up the ViewModels with the ViewModelLocator
- Easy syntax to bind the ViewModel in XAML
The best implementation so far was the ViewModelLocator from John Papa. Unfortunately it doesn’t support design time data seamless, that means you have to code extra ViewModels for design-time data and set the datacontext twice with help of the designer attribute. But I borrowed some ideas from his implementation as well from Kellabytes blog post on how to use MEF with the ViewModelLocator from the famous MVVM Light Toolkit. What I learned from both is that MEF is the best option to implement a light weight ViewModelLocator.
But before we start lets talk about ViewModels and design-time data. Mostly ViewModels relying on external services to get the data they want and to polish them for the View. So the most obvious option would be to mock the ViewModel and instead of asking the service for data, it provides some fake data by itself. Since we live in a decoupled world their is one problem with this approach and that is the fact that we have to introduce interfaces for our ViewModel and to implement each ViewModel twice, one for design-time and one for run-time. I think it’s a much better approach to inject ServicProvider/Helper into the ViewModels which creates an abstraction layer between the ViewModels and the external services and gives us the possibility to foist our design-time data. Plus, a ServiceProvider/Helper is potentially used by more than one ViewModel. So it makes absolute sense to place the fake data there.
Here it comes, the easy 3+1 steps you need to get your ViewModels with full design-time support:
1. Export ServiceHelper
2. Export ViewModel via contract name and import ServiceProvider
3. Bind the ViewModel
4. Optional add a typed property to the ViewModelLocator if you need support for the PropertyGrid in the VS/Blend
Ok you still here, then lets start with some infrastructure that helps us to define our design-time exports easily. MEF allows us to add meta-data to an export definition. The following class implements a simple Boolean attribute which indicates whether an export is meant to provide a implementation at design-time.
Now we can take advantage of one extensibility point of MEF and code our custom ComposablePartCatalog which filters and prioritize design-time exports (the idea and part of the code comes from here). The overall goal of the strategy is to to replace a ServiceProvider implementation with a dummy implementation at design-time by utilizing the DesignTimeAttribute. Maybe a ServiceProvider doesn’t rely on external source and we don’t want to be forced to provide design-time export, that’s why exports without the DesignTimeAttribute stay valid at design-time. However, at run-time we wanna get rid of design-time data and we exclude all exports with the DesignTimeAttribute set to true.
Now we have the important parts in place and we can code an example on how we take advantage of all this. In order to simulate a more real world project we going to have separated projects for the View, the ViewModel and the Model. The following interface defines the functionality for a ServiceHelper.
Allright, here a “real” implementation of the interface and another one to provide fake data for designer.
The ViewModel imports IServiceHelper but doesn’t know about run-/design-time data. It just expects an implementation of IServiceHelper and exports itself via contract name. The name of the ViewModel that is used as contract name for the export is managed in a sealed class and prevents typos within the export/import definitions.
Finally, we can have a look at the ViewModelLocator. The implementation is pretty simple and straight forward except for getting the assemblies we need for MEF. Since the standard initialization of MEF doesn’t work at design-time, we have to manually add all assemblies which contains export and/or import definitions until MEF gets a better design-time support. It’s a ugly hack, but the easiest way to get the assembly is via the type of a known class or interface. Please let me know if you know about a more elegant way that works also at design-time.
Here we go; after all the efforts we can easily wire our ViewModels in 3 + 1 steps (see above) with first class design-time support. If you prefer to have support for DataBinding in the PropertyGrid of Cider/Blend then step 4 is a matter of seconds with the following code snippet.
Let me know what you think!