Mock Data in Blend - Using custom markup extensions
In an earlier post I talked about using mock data classes to share data between Blend and Unit Tests. The approach was pretty straight forward but it isn't an ideal long term solution.
It requires that your ViewModel class has the logic to construct Mock versions of it's dependencies. This means you will be writing some extra plumbing for each of your ViewModel classes that need this level of support.
So what can we do? Well I have been playing for Custom Markup extensions lately in WPF and the solution I have thus far is pretty handy. It isn't complete yet and I am sure there is room for improvement but I thought I would show what I have.
My thought was, I want some sort of object provider layer that will handle giving me an instance of the object I need, wether that be a mock instance or a real instance. I wanted the solution to be generic so that implementing it would be simple and straightforward.
So the first thing I did was to go into my XAML file where I wanted to use this feature and I took a couple of passes at writing the XAML the way I think I would want it.
As a side note, this process of writing your usage scenarios first then building code to support the usage scenario is really the only way to develop a good framework. From Framework Design Guidelines book
"DO design APIs by first writing code samples for the main scenarios and then defining the object model to support the code samples."
So here is what I tried. First I had this:
<Window.DataContext>
<IoCSample:ViewModel />
</Window.DataContext>
I am setting the Windows DataContext in markup to an instance of my ViewModel object. That is ok and works with the technique described in my earlier post.
Then I thought doing this
<Window.DataContext>
<IoCSample:ObjectProvider DataType="IoCSample:ViewModel"/>
</Window.DataContext>
So now I am setting the window's DataContext to an instance of a more generic object called, ObjectProvider and I am setting a property on the ObjectProvider clas called "DataType". This tells the object provider what type of object I need (in this case a ViewModel). The thinking was that the ObjectProvider class would determine if I was in design mode or not and give me an instance of the ViewModel class with either concrete or mock implementations of my dependencies.
But something about that approach just didn't feel right. So then I wrote this.
<Window x:Class="IoCSample.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:IoCSample="clr-namespace:IoCSample"
xmlns:CustomMarkupExtensions="clr-namespace:IoCSample.MarkupExtensions"
Title="Window1"Height="300"Width="300"
DataContext="{CustomMarkupExtensions:ObjectProvider IoCSample:ViewModel}">
This to me seemed more natural and I can see how this will be more flexible. So what I am doing here? I am setting the Windows' DataContext in attribute syntax (instead of element syntax as we were above). I am using a custom markup extension called ObjectProvider and I am setting the default property of the custom markup extension to my ViewModel type.
So think of the Binding markup extension. It's default property is Path so you can write a Binding like this
{Binding PersonFirstName}
This is saying use the Binding markup extension and set the default property (Path) to PersonFirstName.
Now look at my custom extension again.
{CustomMarkupExtensions:ObjectProvider IoCSample:ViewModel}
Use the ObjectProvider markup extension. Set it's default property (which is of type Type) to IoCSample:ViewModel, that is the ViewModel Type.
I won't go into how to build a custom markup extension in detail here as a colleague of mine, John Bowen, has written a nice post about it already. His post is what I used to get started with custom markup extensions so go read it.
So what does my markup extension do? Well the markup extension has one method called ProvideValue. This method get's called when the XAML parser is evaluating the markup extension. So when the XAML parser is constructing my Window and it hits the DataContext attribute it is going to call the ProvideValue method on my markup extension. Since I have set the ObjectType property to IoCSample:ViewModel in the markup when I get to the ProvideValue method I have the information I need to construct an instance of the object requested.
So now I have centralized the place where I will check for the IsInDesignMode property. This means my ViewModels don't have to know anything about DesignMode or not. Which is ideal since the ViewModel really shouldn't be responsible for making that decision and shouldn't be forked to support the two different scenarios.
So now if I encapsulate the construction of the object being requested then the ObjectProvider just has to call the appropriate factory to get back an instance of the object for the given environment (runtime or design time)
In my next post I will discuss a way to encapsulate the construction of this object to increase the flexibility of this approach.