WPF : Design Time Data

If you are using WPF, you are more than likely using Expression BLEND to. You may even be one of the lucky ones and have a designer as part of your team.

If you do you may be aware of the whole designer/developer workflow and just how important this has become.

The idea being that XAML/Binding (and MVVM I would say) have now allowed a designer to work on the same project as the developer. Whilst the developer may be coding away the designer can be polishing up the UI to look fantastic. This is all cool.

But sometimes the designer will need some help in certain areas. Design time data is one of these areas. This is especially useful when the designer is trying to create DataTemplates that may be applied to a List of items. Without design time data the designer simply does not know what their design efforts will yield at runtime.

It is true to say that there has been a lot of work around this area done in Visual Studio 2010, but I have always used a dead simple approach.

There are many different ways, for example you could have something like a ViewModelLocator which stores view types against some other type. The other type could be an actual ViewModel or some design time mock object.

You could use MEF as Glenn Block and John Papa talk about in this post:

http://johnpapa.net/silverlight/simple-viewmodel-locator-for-mvvm-the-patients-have-left-the-asylum/

For me I like to keep it simple.

Here is how I do it.

I have 2 attached DPs, one for the real ViewModel and one for the design time ViewModel. It is dead simple if you are in design time use the design time one, if you are not use the actual one.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;

namespace ViewFirstDesignTimeData
{
    public class ViewModelProps
    {

        #region DesignTimeViewModelType
        /// <summary>
        /// DesignTime : The type of the ViewModel 
        /// to use (for design time)
        /// </summary>
        public static readonly DependencyProperty 
            DesignTimeViewModelTypeProperty =
            DependencyProperty.RegisterAttached(
            "DesignTimeViewModelType",
                typeof(Type), typeof(ViewModelProps),
                    new FrameworkPropertyMetadata((Type)null));

        /// <summary>
        /// Gets the DesignTimeViewModelType property.  
        /// </summary>
        public static Type GetDesignTimeViewModelType(DependencyObject d)
        {
            return (Type)d.GetValue(DesignTimeViewModelTypeProperty);
        }

        /// <summary>
        /// Sets the DesignTimeViewModelType property.  
        /// </summary>
        public static void SetDesignTimeViewModelType(DependencyObject d, 
            Type value)
        {
            d.SetValue(DesignTimeViewModelTypeProperty, value);
        }
        #endregion

        #region ViewModelType

        /// <summary>
        /// ViewModelType : The type of the ViewModel 
        /// to use (for non design time)
        /// </summary>
        public static readonly DependencyProperty ViewModelTypeProperty =
            DependencyProperty.RegisterAttached("ViewModelType", 
                typeof(Type), typeof(ViewModelProps),
                    new FrameworkPropertyMetadata((Type)null,
                        new PropertyChangedCallback(
                            OnViewModelTypeChanged)));

        /// <summary>
        /// Gets the ViewModelType property.  
        /// </summary>
        public static Type GetViewModelType(DependencyObject d)
        {
            return (Type)d.GetValue(ViewModelTypeProperty);
        }

        /// <summary>
        /// Sets the ViewModelType property.  This dependency property 
        /// indicates ....
        /// </summary>
        public static void SetViewModelType(DependencyObject d, 
            Type value)
        {
            d.SetValue(ViewModelTypeProperty, value);
        }

        /// <summary>
        /// Handles changes to the ViewModelType property.
        /// </summary>
        private static void OnViewModelTypeChanged(
            DependencyObject d, 
            DependencyPropertyChangedEventArgs e)
        {
            FrameworkElement element = (FrameworkElement)d;
            object vm=null;
            if (Designer.IsDesignMode)
            {
                vm = Activator.CreateInstance(
                    GetDesignTimeViewModelType(d));
            }
            else
            {
                vm = Activator.CreateInstance(
                    GetViewModelType(d));
            }

            if (vm == null)
                throw new InvalidOperationException(
                    "You have to specify a type for the ViewModel");
            
            element.DataContext = vm;
        }

        #endregion

    }
}

 

Then I have 2 ViewModels, one is the actual one, and one is the dummy design time data one.

Something like these

Actual

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;

namespace ViewFirstDesignTimeData
{
    public class Window1ViewModel : INPCBase
    {
        private IList<Person> people;
        public IList<Person> People
        {
            get
            {
                if (people == null)
                    LoadPeople();
                return people;
            }
        }

        private void LoadPeople()
        {
            people = new ObservableCollection<Person>();

            //simulate fetching these from web server 
            //or something
            for (int i = 0; i < 20; i++)
            {
                people.Add(new Person(
                    String.Format("runtime firstname_{0}", 
                    i.ToString()),
                    String.Format("runtime lastname_{0}", 
                    i.ToString()),
                    i));
            }
        }
    }
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Design Time

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;

namespace ViewFirstDesignTimeData
{
    public class DesignWindow1ViewModel : INPCBase
    {
        private IList<Person> people;
        public IList<Person> People
        {
            get
            {
                if (people == null)
                    LoadPeople();
                return people;
            }
        }

        private void LoadPeople()
        {
            people = new ObservableCollection<Person>();
            people.Add(new Person("Sacha","Barber", 37));
            people.Add(new Person("Marlon","Grech", 30));
            people.Add(new Person("Josh","Smith", 36));
            people.Add(new Person("Pete","O'Hanlon", 43));
            people.Add(new Person("Karl","Shifflett", 53));
        }
    }
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Then all I have to do is mark up my View in the XAML as follows:

NOTE : In this example I also show off a ListBox with an example DataTemplate for Person objects.

<Window x:Class="ViewFirstDesignTimeData.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:ViewFirstDesignTimeData;assembly="
    Title="Window1" Height="300" Width="300"
    local:ViewModelProps.ViewModelType=
        "{x:Type local:Window1ViewModel}"    
    local:ViewModelProps.DesignTimeViewModelType=
        "{x:Type local:DesignWindow1ViewModel}" >
    
    <ListBox ItemsSource="{Binding People}">
        <ListBox.ItemTemplate>
            <DataTemplate DataType="{x:Type local:Person}">
                <StackPanel Orientation="Vertical" 
                            TextElement.FontFamily="Arial"
                            HorizontalAlignment="Stretch">
                    <Label FontSize="24" Foreground="Blue"  
                           Content="{Binding FirstName}"/>
                    <Label FontSize="20" Foreground="Black"  
                           Content="{Binding LastName}"/>
                    <StackPanel Orientation="Horizontal">
                        <Label FontSize="14" Foreground="Black"  
                           Content="Age: "/>
                        <Label FontSize="14" Foreground="Black"  
                           Content="{Binding Age}"/>
                    </StackPanel>
                </StackPanel>
            </DataTemplate>
        </ListBox.ItemTemplate>
        
    </ListBox>
</Window>

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Want proof this works have a look at these screen shots from Expression Blend

Here it the solution in Blend

DesignData1[1]

Extreme close up:

DesignData2[1]

And here is another one, showing the designer surface (right hand side of image) next to the runtime app (left hand side of image):

DesignData3[2]

As always here is a small demo project: demo project.zip

About these ads

8 thoughts on “WPF : Design Time Data

  1. Mike Strobel says:

    “If you are using WPF, you are more than likely using Expression BLEND”

    Really? People actually use that? I can’t stand it–it’s like coding with one arm tied behind my back.

  2. Günter Schwaiger says:

    A minute ago I receveid an email from Amazon introducing your book “Building Wpf and Silverlight Application”. I ordered it and I am very excited to get it. I believed you have posted that you stoped writing a book, aren’t you?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s