Thomas Lebrun

All you need about WPF, Silverlight & LINQ !

[WPF / MVVM] How to get data in “design time” ?

When you are working with WPF and MVVM Pattern, you can regret that the designer doesn’t get data, in design time, that allow him to simply modified the graphical interface.

To help my developments, I’ve created a little attached property which will create an instance of the type passed in parameter:

public static class DesignTimeHelper

{

    /// <summary>

    /// DependencyProperty used to store the DesignTime property.

    /// </summary>

        public static readonly DependencyProperty DesignTimeDataProperty =

        DependencyProperty.RegisterAttached("DesignTimeData", typeof(Type), typeof(DesignTimeHelper), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnDesignTimeDataChanged)));

 

    /// <summary>

    /// Gets the design time data.

    /// </summary>

    /// <param name="obj">The obj.</param>

    /// <returns></returns>

    public static Type GetDesignTimeData(DependencyObject obj)

    {

        return (Type)obj.GetValue(DesignTimeDataProperty);

    }

 

    /// <summary>

    /// Sets the design time data.

    /// </summary>

    /// <param name="obj">The obj.</param>

    /// <param name="value">The value.</param>

    public static void SetDesignTimeData(DependencyObject obj, Type value)

    {

        obj.SetValue(DesignTimeDataProperty, value);

    }

 

    /// <summary>

    /// Called when DesignTimeData changed.

    /// </summary>

    /// <param name="d">The source object.</param>

    /// <param name="e">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> instance containing the event data.</param>

    private static void OnDesignTimeDataChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)

    {

        var isOnDesignMode = DesignerProperties.GetIsInDesignMode(new DependencyObject());

        if (isOnDesignMode)

        {

            var element = d as FrameworkElement;

            if (element == null)

                throw new NullReferenceException("element must not be null and must be an UIElement.");

 

            var designTimeDataType = e.NewValue as Type;

            if (designTimeDataType == null)

                throw new NullReferenceException("designTimeDataType must not be null.");

 

            element.DataContext = Activator.CreateInstance(designTimeDataType);

        }

    }

}

To use this attached property, it’s simple: just create 2 ViewModels (one used for the data in “runtime” and the second one used for the data in “designtime”):

image

Then, use the property:

image

At “design time”, we can see that data are really different from “runtime”:

image image

image

For your information, here is the content of the ViewModel in “design” mode:

namespace TestToolbox.ViewModels.Design

{

    class SampleViewModel : ISampleViewModel

    {

        #region ISampleViewModel Members

 

        public string Text

        {

            get { return "Design time text"; }

        }

 

        public string Title

        {

            get { return "Design time title"; }

        }

 

        #endregion

    }

}

And here is the ViewModel for “runtime” mode:

namespace TestToolbox.ViewModels

{

    public class SampleViewModel : ISampleViewModel

    {

        #region ISampleViewModel Members

 

        public string Text

        {

            get { return "Runtime text"; }

        }

 

        public string Title

        {

            get { return "Runtime title"; }

        }

 

        #endregion

    }

}

That’s all ! Now, your designer is able to work with (sample) data that allow him to design the graphical interface of your application without having to launch it for testing/viewing the results.

 

Bye !

 

PS: Thanks to Simon for the suggestion of using a type instead of a simple string [;)]

Posted: May 04 2009, 03:00 AM by Thomas Lebrun | with 11 comment(s)
Filed under: , ,

Comments

Jeff said:

Well this is a neat way to get introduced into attached properties.

Unfortunately I'm not able to get this to work in my project. I get the two following build errors when trying to set this attached property on my grid.

x:Type p:FakeViewModel' cannot be assigned to property 'DesignTimeData'. Invalid PropertyDescriptor value

The type reference cannot find a public type named 'Type p:FakeViewModel'

In my case "p" is my local namespace where I've also placed a copy of your DesignTimeHelper class and my attached property declaration looks like the following. Maybe I should add as well that I'm doing this in a UserControl but I thought it shouldn't matter?

<Grid p:DesignTimeHelper.DesignTimeData="x:Type p:FakeViewModel">

...

</Grid>

# August 27, 2009 9:29 AM

Jeff said:

Oh, forget my last comment. I caught my own mistake. My binding expression within the attached property assignment should have been written as follows.

<Grid p:DesignTimeHelper.DesignTimeData="{x:Type p:FakeViewModel}">

Notice the crucial curly braces, gosh I hope VS 2010 makes writing binding syntax less error prone.

# August 27, 2009 9:33 AM

Jeff said:

Ok, so I got your example working. The thing I'm realizing now though is that binding to DataContext with design-time data can be useful for substituting a run-time ViewModel but in cases where you just want to have some design-time data within a reusable control or UserControl implementation it doesn't seem as practical.

Imagine that you are writing the CheckBox control and you want it to have some sample text and a checked true state while in design-mode to aid in the visualization (perhaps to make sure the check color and size is correct). Now providing design-time data bound to the DataContext is of little use since what we really would need is to bind to the IsChecked and the Content properties. I'm I making sense?

Do you have any pointers on how one would go about this. I've been trying a few things such as writing an attached property which takes a custom type with a property name and a binding in order to assign that binding at design-time but I couldn't figure out how to specify an instance of my custom within the XAML markup. Any other pointers would be a big help.

# August 27, 2009 1:37 PM

Thomas Lebrun said:

Hi Jeff,

The problem is that your unable to set the IsChecked property of the ComboBoxItem if you have filled the ComboBox with binding (thought ItemsSource).

One idea could be to create an attached property that, when changed, check if ItemsSource/DataContext is not null, find the ComboBoxIem for a particular object in the combobox (with ContainerFromItem: msdn.microsoft.com/.../system.windows.controls.itemcontainergenerator.containerfromitem.aspx) and set its IsChecked property to true.

I've not tested but it should work.

Bye.

# August 28, 2009 2:57 AM

Jeff said:

Ok, thanks for the idea. The case of the CheckBox was actually more hypothetical as I was actually just trying to reduce the example of displaying design time data/(configuration ?) to a very simple form. Maybe a possible method would be to have an alternate style which is applied only at design time and sets various properties to design time defaults. I'm going to play around with some of these ideas to see if I can figure out a solution.

Thanks again for the useful post and helpful comments.

# September 1, 2009 8:04 AM

Thorsten Lorenz said:

Hi Thomas,

I don't know if I made a mistake somewhere along the line, but in order to make your solution work I had to change the DesignTimeHelper class to be non static.

Otherwise the Xaml Parser doesn't find the attached DesignTimeData property.

I should add, that I included the DesignTimeHelper class into my Tools project which lives in a different assembly than the one that my view is in.

Except for that I did everything like you described.

Maybe this helps other people to implement your great and very practical (only one line code added to the view) solution.

Cheers,

Thorsten Lorenz

# September 14, 2009 9:31 AM

Thomas Lebrun said:

Hi Thorsten,

Yes, good catch: no need to have the class static.

Thanks !

# September 14, 2009 9:37 AM

callwardt said:

This is awesome!  I was hoping that I didn't have to change from m-v-vm in order to get design time support!

# February 4, 2010 12:37 AM