Silverlight 4, MVVM, and design time DLL’s for example data.

More and more I realize how convenient it is to have design time data show up for your Silverlight project in Blend (and also Cider). But how do you create a “quick and dirty” easily usable design time DLL for a Silverlight project? There are a lot of examples about various types of design time support in Silverlight 4 and here are a couple to start out with:

http://blogs.silverlight.net/blogs/justinangel/archive/2008/11/17/silverlight-design-time-extensibility.aspx
http://www.ningzhang.org/2009/03/31/how-to-write-silverlight-design-time-for-all-designers-visual-studio-2008-blend-2-blend-3-and-visual-studio-2010/

These examples are definitely a good place to start when you want to author a Silverlight control and want design time property editing in Blend. But what if all you want to do is grab some XML containing sample data, manipulate it a little, set some ViewModel properties, and have it show up on the page or user control in Blend? That is, you want to kruft up an ad-hoc quick and dirty design time DLL that you can change just as rapidly as you change your sample data. We can have that, and we don’t have to worry about IRegisterMetadata and all of that. If what you want is to have some mock up data show up in text boxes, combo boxes, grids, and what have you, then read on.

Basically all you need to do is to create an assembly whose name ends in Design.dll. Basically, its name is the same name as the assembly that you want to add design time support for, but you just tack on “Design” before the .dll extension. Review the above links for the naming convention and additional naming conventions if you want design time support for Blend only or Visual Studio 2010. Say you have a project that creates an assembly MySlApp.dll (and MySlApp.xap for that matter). Create a Silverlight library project that generates an assembly called MySlApp.Design.dll and have that design time assembly copied into the output folder of MySlApp.dll after it is built using a post build step. Do not add a reference in MySlApp.dll to MySlApp.Design.dll. We don’t want design time DLL’s showing up in the .xap file for the deployed Silverlight application. You can however reference MySlApp from MySlApp.Design; which you will probably need to do for help om loading the ViewModel for design time support.

This example project uses the GalaSoft MVVM Light framework http://blog.galasoft.ch/. While this demo is MVVM centric, theoretically one could use this simple approach for populating views in project that do not necessarily adhere to an MVVM approach. Lots of code-behind for a view could create problems for code maintainability and design time rendering although.

Below is an example ViewModel class we want to populate with data in design time. The custom LoadViewModelDesign attribute and the call to DesignHelpers.LoadDesignData(this); in the ViewModel constructor is how we accomplish this.

using GalaSoft.MvvmLight;

namespace DesignDllDemo.ViewModel
{
    /// <summary>
    /// This class contains properties that the main View can data bind to.
    /// <para>
    /// Use the <strong>mvvminpc</strong> snippet to add bindable properties to this ViewModel.
    /// </para>
    /// <para>
    /// You can also use Blend to data bind with the tool's support.
    /// </para>
    /// <para>
    /// See http://www.galasoft.ch/mvvm/getstarted
    /// </para>
    /// </summary>
    [LoadViewModelDesign("DesignDllDemo.Design.MainViewModel_Design, DesignDllDemo.Design, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null")]
    public class MainViewModel : ViewModelBase
    {
        private string _welcome = "Welcome to MVVM Light";

        /// <summary>
        /// Display welcome message for this demo.
        /// </summary>
        public string Welcome
        {
            get
            {
                return _welcome;
            }
            set
            {
                _welcome = value;
            }
        }

        /// <summary>
        /// Initializes a new instance of the MainViewModel class.
        /// </summary>
        public MainViewModel()
        {
            if (IsInDesignMode)
            {
                // Code runs in Blend --> create design time data.
                DesignHelpers.LoadDesignData(this);
            }
            else
            {
                // Code runs "for real"
            }
        }

    }
}

The custom attribute and helper method for populating the ViewModel design time are shown below:

using System;
using System.Reflection;
namespace DesignDllDemo
{
    /// <summary>
    /// Decorate the View Model class with this attribute for design time loading.
    /// </summary>
    [AttributeUsage(AttributeTargets.Class)]
    public class LoadViewModelDesignAttribute : Attribute
    {
        private string _designDataLoadType = null;

        /// <summary>
        /// Constructor, the <paramref name="designDataLoadType"/> parameter is the full or partial name of the type.  Include the assembly with the type name at least.
        /// </summary>
        public LoadViewModelDesignAttribute(string designDataLoadType)
        {
            _designDataLoadType = designDataLoadType;
            this.DesignDataLoadMethod = "LoadDesignData";
        }

        /// <summary>
        /// Type to reference for populating view model with data.
        /// </summary>
        public string DesignDataLoadType { get { return _designDataLoadType; } }
        
        /// <summary>
        /// The static method to call to load the view model with design time data.  The default value is <c>LoadDesignData</c>.  An instance of the view model
        /// is passed as the one and only parameter to this method.
        /// </summary>
        public string DesignDataLoadMethod { get; set; }
    }

    public static class DesignHelpers
    {
        /// <summary>
        /// Load a view model class with design time data.  The class must be decorated with the <see cref="LoadViewModelDesignAttribute"/> attribute class.
        /// </summary>
        /// <param name="targetViewModel">The view model to be loaded.</param>
        public static void LoadDesignData(object targetViewModel)
        {
            // get the type for the view model we are going to populate, we need this to get the LoadViewModelDesignAttribute attribute that the class 
            // needs to be decorated with
            Type mClass = targetViewModel.GetType();
            LoadViewModelDesignAttribute att = (LoadViewModelDesignAttribute)Attribute.GetCustomAttribute(mClass, typeof(LoadViewModelDesignAttribute));

            // don't want to throw exceptions in design tools
            if (att == null)
                return;

            // get the type from the design assembly, this assembly should be loaded by Blend or whatever during design tim
            Type tpClass = Type.GetType(att.DesignDataLoadType);
            if (tpClass == null)
                return;

            // get the method to populate the view mode.
            MethodInfo loadMethod = tpClass.GetMethod(att.DesignDataLoadMethod, BindingFlags.Public | BindingFlags.Static);
            if (loadMethod == null)
                return;

            // this will populate the view model
            loadMethod.Invoke(null, new object[] { targetViewModel });

        }
    }
}

Notice that reflection is used to call the method for populating the view model. We have to use reflection because we do not want to couple a Silverlight application assembly with its designer assembly.

The simple example solution below will demonstrate this simple implementation for a Silverlight designer assembly.
DesignDllDemo

Download PDF
This entry was posted in MVVM, Silverlight 4 and tagged , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *