MVVM Design Time Data

When designing a WPF or Silverlight view in Blend, it is absolutely necessary to be able to see sample data. Blend gives you several options for generating design time data. Data sources work best with Update Controls.

Create a UserControl for each view

When you create your WPF application, you will be given a MainWindow. In Silverlight, it’s a MainPage. It is tempting to start drawing controls directly in this window or page. Don’t. This is just a container.

All of your UI should be in UserControls. This has several advantages. First, UserControls are composable. You can put UserControls inside of other UserControls. This lets you adjust the flow of your application without breaking the UI components. Second, each UserControl can have its own design time data. Third, this makes navigation easy. The main page or window can switch from one UserControl to the next.

In the Model-View-ViewModel pattern, UserControls are in the View layer. The MainWindow/MainPage is not.

Create a data source for each UserControl

image Each user control is going to be data bound to a view model. Whatever container creates the user control is responsible for settings its DataContext. Think of this as a form of dependency injection. The Model is injected into the View Model, and the View Model is injected into the View.

Blend does not know what to inject into your view. Instead, your view needs to pull in some sample data itself. This is backwards, but it’s the only way that the designer can work. Start by creating a class with one property named ViewModel. This class should populate some sample data and inject it into the view model.

public class CatalogDesignerData
{
    public CatalogViewModel ViewModel
    {
        get
        {
            Catalog catalog = new Catalog();
            Product pie = catalog.NewProduct();
            pie.Name = "Pie";
            pie.BasePrice = 3.14m;
            Product oiler = catalog.NewProduct();
            oiler.Name = "Oiler";
            oiler.BasePrice = 2.72m;

            return new CatalogViewModel(catalog);
        }
    }
}

If the view model needs access to a service, inject a stub right here. View models should call services through interfaces rather than concrete classes so that you can swap them out at unit test and design time.

In blend, create a UserControl for your view. Add an object data source to the user control. Select the class that you just defined. You will have to build before it appears in the list.

image

Drag the ViewModel property onto the LayoutRoot. This will databind the view to the view model. Unfortunately, this databinding will be in effect at runtime as well as design time. To fix that, open the XAML editor and put d: in front of the DataContext attribute:

<Grid x:Name="LayoutRoot" d:DataContext="{Binding ViewModel, Mode=OneWay, Source={StaticResource CatalogDesignerDataDataSource}}">

The design time data will still appear in Blend, but it will no longer be used at runtime. Now you can drag and drop properties from the data source to bind to controls in the view.

Load the real data on MainWindow/MainPage Loaded

Bring these two ideas together.

  1. MainWindow/MainPage is just a container.
  2. The view model is injected into the view.

From this we conclude that the main window/page should perform the dependency injection. Handle the Loaded event. In the code behind, construct the services, models, and view models needed for the initial view. Set the DataContext of the main window so that the embedded UserControl will inherit it. (This would be a good place to use an IoC container if you are so inclined.)

private void Window_Loaded(object sender, RoutedEventArgs e)
{
    CatalogService catalogService = new CatalogService();
    Catalog catalog = catalogService.LoadCatalog();
    DataContext = ForView.Wrap(new CatalogViewModel(catalog));
}

Blend will not invoke the Loaded event. So you can be confident that this code executes only at runtime. This gives you a clean way of separating runtime data and services from design time data and service stubs. Runtime code goes in Window_Loaded. Design time code goes in the data source.

Some MVVM libraries recommend the View Model Locator pattern. This pattern is backwards. It has the View settings its own DataContext. The container of the View should inject the DataContext. The only time that a View should assign its own DataContext is at design time.

The advantage of setting design time data using a data source is that you can use your actual view model. Most of your view-specific logic is going to end up in the view model. If you use XML sample data based on the view model, it won’t necessarily reflect that logic. The technique described here gives you the best design-time experience while upholding the best runtime dependency injection principles.

Comments

Create data from class

I find it easier to create sample data from the view model class first. Blend automatically generates some sample data to get me started, and I don't have to write any code.

Furthermore, when I tried your technique, Blend didn't recognize the properties of items in a collection. I could no longer drag a collection onto the art board to create a list.

So I'm going to keep generating sample data from the view model class. If I run into one of the problems you talk about, maybe I'll switch at that point.