DesignData MVVM support in Blend, VS2010 and WPF/Silverlight
When developing a solution using WPF or Silverlight, there are normally designers working in Blend and developers working with them in Visual Studio 2010. Often it is impractical for designers to use data from live systems (e.g. they don’t want to install SQL etc.) but they need data to flesh out the user interface so that they can style it and edit templates. For example a ListBox with no data is difficult to style in Blend, and certainly impossible to visualise. For this reason there is often a need to provide design time data that mimics the real runtime data.
The first cut approach to this is to put the mock data into XML files and then parse them into Plain-Old CLR Objects (POCOs), but this requires writing and maintaining an XML parser in your application. You’ve probably already realised that the XAML parser is pretty good at creating POCOs from XML – it is what it does for a living – so a far better approach than plain XML files is to create your POCO object graph through XAML and let the Silverlight/WPF XAML parser do the heavy lifting. You can then bind your user interface to this dummy data and get all your databindings correct and working, then switch it out at runtime for the real thing. Indeed this approach has been adopted by the Blend team, and there is UI support for this in Blend (the DATA tab) to allow you to create, edit and maintain the tree of design time data.
However, this situation isn’t ideal as you have a graph of dummy data which is included in your application at runtime but not used (it is only used during design time). Enter the DesignData markup extension and build action.
You’ve probably noticed that Blend adds the following namespace declarations to your XAML files:
mc:Ignorable="d" xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
so what’s this all about? Well this is a pretty neat feature that allows Blend to set properties and attributes that are ignored at runtime. You’ve probably seen:
d:DesignHeight="350" d:DesignWidth="525"
which hints to Blend what size to draw a control at when viewed in the designer, without affecting runtime layout. Pretty neat! Well the DesignData markup extension takes this a step further.
To use DesignData, first create, as normal, your POCO objects that will hold your data in your view model. You should implement INotifyPropertyChanged so that your bindings update when the data changes. I am going to use the classic Person object as an example:
using System.Collections.ObjectModel;
using System.ComponentModel;
namespace WpfApplication5
{
public class Person : INotifyPropertyChanged
{
private string personName;
public string PersonName
{
get
{
return this.personName;
}
set
{
if (this.personName != value)
{
this.personName = value;
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs("PersonName"));
}
}
}
}
private int age;
public int Age
{
get
{
return this.age;
}
set
{
if (this.age != value)
{
this.age = value;
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs("Age"));
}
}
}
}
private string phoneNumber;
public string PhoneNumber
{
get
{
return this.phoneNumber;
}
set
{
if (this.phoneNumber != value)
{
this.phoneNumber = value;
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs("PhoneNumber"));
}
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
public class Persons : ObservableCollection<Person> { }
}
Next, in order to use DesignData, create a separate standalone XAML file that defines your view model:
<Persons xmlns="clr-namespace:WpfApplication5">
<Person PersonName="Joe Soap" Age="21" PhoneNumber="0118 909 1234"/>
<Person PersonName="Fred Bloggs" Age="35" PhoneNumber="0118 909 4321"/>
<Person PersonName="Bill Gates" Age="46" PhoneNumber="0118 909 1111"/>
<Person PersonName="Steve Jobs" Age="40" PhoneNumber="0118 909 2222"/>
</Persons>
Now set the build action in VS2010 for this file to DesignData:
Now you have a design time object graph! Its as simple as that. This will not be loaded or created at runtime, it is not part of your running application.
In order to use this graph, you can set the d:DataContext anywhere in your application, using the DesignData markup extension and specifying the name of your data file. Note that this only affects the DataContext at design time, not the runtime DataContext. Also note that you need “..\” in your path if you are using Silverlight.
<Grid>
<ItemsControl d:DataContext="{d:DesignData Source=DesignTimeData.xaml}" ItemsSource="{Binding .}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBox Text="{Binding PersonName}"/>
<TextBox Text="{Binding PhoneNumber}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
and that’s it! Your will see data in Blend and the VS2010 designer and you can style to your heart’s content:
Also the data binding dialog in Blend will pick up your properties:
For completeness, there’s a couple of extra details that you might like to know. Blend and the VS2010 designer actually clone your POCO objects and create new objects that have the same properties as your objects. This is so that the designer doesn’t have to create your objects at design time as they may throw exceptions and interfere with the designer. If you want to suppress this behaviour, you can set your BuildAction to DesignDataWithDesignTimeCreatableTypes:
The designer will now create your real objects.
There is also a d:DesignInstance markup extension. This just defines the type to use without actually creating any data objects. It is useful to light up the data binding dialog in Blend even when you haven’t set up any mock test data. It allows Blend to know the data type of the data context so it can suggest suitable data bindings. It also has a IsDesignTimeCreatable attribute to control cloning.
<ItemsControl d:DataContext="{d:DesignInstance local:Persons, IsDesignTimeCreatable=True}">
Resources:
Design Data: https://msdn.microsoft.com/en-us/library/ff602279(VS.95).aspx
DesignData msbuild target: https://www.robfe.com/2009/12/the-curious-case-of-the-designdata-msbuild-target/
Design Instance: https://www.codeproject.com/Articles/43335/d-DesignInstance-d-DesignData-in-Visual-Studio-201.aspx
Written by Paul Tallett
Comments
Anonymous
September 05, 2010
As obvious as this seems, I haven’t been able to find any sample code online showing how to load the "real" ViewModel from a XAML file at runtime. How is this best done? Calling XAMLReader.Load() didn't work for me. Do I need to use the DesignerSerializationManager? Can I just compile with the Page build action?Anonymous
September 05, 2010
I always just compiled a "real" ViewModel into the app by setting the Page build action and then referencing the Resource.Anonymous
June 11, 2011
Using this with your sample Data works finde, thx. Only Using it with MY Sample Data only works in Blend.... >> looking what's going wrong -.-'Anonymous
October 18, 2011
My model uses classes generated from code from wsdl in Visual Studio using the "Add Service Reference". The collection members on these classes do not get initialized, causing the designer to throw a null reference exception when I attempt to use DesignData. As a workaround I've added initialization to the code, but it will get overwritten next time I regenerate it. Is there some way to tell either the code generator or the DesignData to initialize collection members? See also here: stackoverflow.com/.../how-can-i-generate-classes-from-wsdl-that-work-at-design-time-in-visual-studio Thanks!Anonymous
October 21, 2014
Thanks. How to set DateTime in XAML?