Brunov's blog

Sergey Vyacheslavovich Brunov's blog

WPF: DataTemplates and Design-Time data

2014-08-27 23:42:51 Moscow time

It seems to be useful to have a possibility to edit DataTemplate rendered with Design-Time data.

First, let's consider the following ViewModel interface:

internal interface IBookViewModel
{
    string Author { get; set; }

    string Title { get; set; }
}

Let's also create a separate XAML-file for ResourceDictionary where the DataTemplate will be located:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:viewModel="clr-namespace:App.ViewModel"
                    mc:Ignorable="d">
    <DataTemplate DataType="{x:Type viewModel:IBookViewModel}">
        <StackPanel Orientation="Vertical">
            <TextBlock Text="{Binding Author}" />
            <TextBlock Text="{Binding Title}" />
        </StackPanel>
    </DataTemplate>
</ResourceDictionary>

Second, let's edit the DataTemplate using Blend for Visual Studio 2012 (see Expression Studio 4.0: Create or modify a template):

  1. Open the solution in Blend.
  2. Locate the XAML-file containing the ResourceDictionary using the "Resources" panel.
  3. Locate the DataTemplate in the list of resources by its Key.
  4. Click "Edit Resource" button.

Blend will show the message "BookViewModelTemplate.xaml cannot be edited in the Design view."

Third, to display Design-Time data attribute d:DataContext must be used to provide the Design-Time data for the designer.
Also, there is d:DesignInstance markup extension to simplify the Design-Time instance creation.
Blend uses the following Design-Time-related XAML-namespaces (see DesignData MVVM support in Blend, VS2010 and WPF/Silverlight):

xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"

To provide the Design-Time Data instance, let's create a class that will implement the ViewModel interface and contain the appropriate data:

internal sealed class DesignBookViewModel : IBookViewModel
{
    public DesignBookViewModel()
    {
        Title = "Test title";
        Author = "Test author";
    }

    public string Author { get; set; }

    public string Title { get; set; }
}

Finally, after experiments it seems the d:DataContext attributes does not work for DataTemplate element directly, but the immediate child element of the DataTemplate.

The following result examples are identical. Both work just fine.

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:viewModel="clr-namespace:App.ViewModel"
                    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                    mc:Ignorable="d">
    <DataTemplate DataType="{x:Type viewModel:IBookViewModel}">
        <StackPanel Orientation="Vertical"
                    d:DataContext="{d:DesignInstance Type=viewModel:DesignBookViewModel, IsDesignTimeCreatable=True}">
            <TextBlock Text="{Binding Author}" />
            <TextBlock Text="{Binding Title}" />
        </StackPanel>
    </DataTemplate>
</ResourceDictionary>
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:viewModel="clr-namespace:BlendabilityDataTemplate.ViewModel"
                    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                    mc:Ignorable="d">
    <viewModel:DesignBookViewModel x:Key="DesignBookViewModel" />
    <DataTemplate DataType="{x:Type viewModel:IBookViewModel}">
        <StackPanel Orientation="Vertical"
                    d:DataContext="{Binding Source={StaticResource DesignBookViewModel}}">
            <TextBlock Text="{Binding Author}" />
            <TextBlock Text="{Binding Title}" />
        </StackPanel>
    </DataTemplate>
</ResourceDictionary>

P.S. There is also another approach (out of the scope of this post) – create a DataSource using Blend. It implies adding additional XAML-file that defines ViewModel instance (i.e. initialization).
The disadvantage of this approach: the ViewModel can be just an interface or concrete class, but without public setters – no way to set property values using XAML.
More details here:

  1. DesignData MVVM support in Blend, VS2010 and WPF/Silverlight.
  2. Blendability Part I – Design time ViewModel.

References:

  1. Mastering XAML (Design-Time attributes) by Adam Nathan.
  2. WPF 4.5 Unleashed, Adam Nathan.

StackOverflow references:

  1. What approaches are available to dummy design-time data in WPF?.
  2. Setting design time DataContext on a Window is giving a compiler error?.
  3. Making a DataTemplate blendable.
  4. Design time data for datatemplate in xaml.

Tags: mvvm wpf