Viewing Design Time Data in Visual Studio 2008 Cider Designer in WPF and Silverlight Projects

SilverlightOne

“I did this ListBox DataTemplate layout in a fraction of the time because I didn’t have to re-run my project between edits to see the results.

I could see the changes on the Cider design surface as I was editing the template XAML.”

They say a picture is worth a thousand words.  I’m sure the above picture certainly got your attention.  Let’s understand how you can do this in a few minutes in your projects TODAY.

This simple solution significantly raises the productivity bar when working with DataTemplates, DataGrids, ListBoxes or ListViews because the Cider Designer will simply re-render as you edit the XAML.

You no longer have to keep running the project and tweaking between runs, just follow these super simple instructions and the Cider Designer will be your new best friend.  This solution works in WPF and Silverlight TODAY.

Background

I came into work earlier this week and mentioned to Mark Boulter a Microsoft Principal Program Manager Lead on the Cider Team that I was working on a complex ComboBox DataTemplate the previous night and I wished that I could have accomplished the task with design time data displayed in the Cider Designer.  That this would have made the DataTemplate editing task much easier for me.

He just came out and said, “you can.”  Most of you that know me, can picture Karl’s ears perking up and an above indoor voice replying with excitement, “what!”

There is no magic, no monkey business going on.  The Visual Studio 2008 Cider Designer instantiates controls and renders them on the design surface.  If that control has a DataContext with data in it and the control is data bound, the data displays.  It’s that simple.

I’m not sure why this light did not come on in my brain before.  Of course Cider instantiates controls.  It has to so that it can render the control properly when properties like Background and Width are set on the control.  This is why all controls must have a default empty constructor, so that they can be instantiated by the designer.

So with 5 minutes of brain storming and 15 minutes of trail and error coding, Mark, Ben and I came up with the solution that is being presenting here.  Ben Wulfe is a Microsoft Senior Software Development Engineer on the Cider Team.

We sincerely hope you find this solution easy to use and that you get a greater design time experience by using it.

So as many of my super geek friends would say, “let’s feel the love and write some code!”

The Data

WPFOne

SilverlightThree

The Customer class is a from the WPF project and is your typical WPF business entity object.  Customers represents the collection of Customer.  These are similar to your standard classes already in your current WPF and Silverlight projects.

The DesignTimeCustomers class provides the Cider Designer the design time data.  Rather than create a special design time data class with the same shape as Customer and Customers, I chose to derive from Customers.  Now I’m guaranteed that any breaking changes to Customers or Customer will cause a compile time error and I can quickly resolve the issue.

I’ve also placed DesignTimeCustomers in its own Namespace to avoid any problems on large projects like a developer using a design time class instead of a real run-time class.

Now for the secret sauce.  By loading the individual Customer objects into the DesignTimeCustomers collection in the constructor, I have a class that when instantiated will be a class with its collection fully populated and ready to be consumed, even at design time.

Namespace DesignTimeData

    Public Class DesignTimeCustomers
        Inherits Customers

        Public Sub New()
            Me.Add(New Customer(True, 55000, 1, "Tim", "Smith"))
            Me.Add(New Customer(True, 75000, 2, "John", "Johnson"))
            Me.Add(New Customer(True, 52000, 3, "Susan", "Washington"))
        End Sub

    End Class

End Namespace

For each of the DataTemplates, DataGrids, ListBoxes, ListViews or ComboBoxes you need design time data viewing, you’ll create a class similar to the above.  I was thinking about writing and Add-In that creates these classes but that’s for another day.

The Resource

This solution has two methods of implementation.

  • Solution One:  Design Time and Run-Time DataContext – you assign the DataContext to a resource that points to the above DesignTimeCustomers
  • Solution Two:  Different Design Time and Run-Time DataContext – using the DesignAndRunTimeDataContext class allows specifying both a design time and run-time DataContext.  This provides the ability to consume the above DesignTimeCustomers class at design time and when the project is run, the DataContext can be set in code, displaying records from a data base for example.  This class only works in WPF because Silverlight does not have a DataSourceProvider class which is what DesignAndRunTimeDataContext derives from.

I think implementation comes down to each developer’s workflow.  If you want automatic design time and run-time DataContext switching, use the second solution above.  Otherwise use the first solution.

Solution One
<UserControl 
    x:Class="SilverlightTestingDesignTimeData.Page"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:DesignTimeData="clr-namespace:SilverlightTestingDesignTimeData.DesignTimeData"         
    Width="325" Height="200">
    
    <UserControl.Resources>
        <DesignTimeData:DesignTimeCustomers x:Key="designTimeCustomersDS" />
    </UserControl.Resources>

The above code snippet is from the Silverlight demo project included in the project downloads.  I’ve just created a resource that points to the above DesignTimeCustomers class.  The resource will be instantiated by the Cider Designer at design time.  Since the collection is populated in its constructor, we now have a populated collection that can be consumed at design time.  How cool is that!

Solution Two

Solution two can be implement one or more ways also.

Below the DesignTimeDataContext and the RunTimeDataContext have both been assigned to the same resource.  I did this to keep the example as simple as possible.  If I had a resource that pointed to another collection at run-time I could have used it.

Don’t forget to add the namespace declaration for DesignTimeData.

<Window 
    x:Class="DesignTimeDataTwo"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:DesignTimeData="clr-namespace:VisualStudio2008DesignTimeData.DesignTimeData" 
    xmlns:local="clr-namespace:VisualStudio2008DesignTimeData"
    Title="Design Time Data Demo Two" Height="300" Width="300">
    <Window.Resources>
        
        <DesignTimeData:DesignTimeCustomers x:Key="designTimeCustomersDS" />
        
        <local:DesignAndRunTimeDataContext 
            x:Key="customersDS"
            DesignTimeDataContext="{StaticResource designTimeCustomersDS}" 
            RuntimeDataContext="{StaticResource designTimeCustomersDS}" />

    </Window.Resources>

In the below code the DesignTimeDataContext is being set in XAML and the RunTimeDataContext is being set in the code behind file at run-time.

<Window 
    x:Class="DesignTimeDataTwo"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:DesignTimeData="clr-namespace:VisualStudio2008DesignTimeData.DesignTimeData" 
    xmlns:local="clr-namespace:VisualStudio2008DesignTimeData"
    Title="Design Time Data Demo Two" Height="300" Width="300">
    <Window.Resources>
        
        <DesignTimeData:DesignTimeCustomers x:Key="designTimeCustomersDS" />
        
        <local:DesignAndRunTimeDataContext 
            x:Key="customersDS"
            DesignTimeDataContext="{StaticResource designTimeCustomersDS}" /> 

    </Window.Resources>

At run-time when the Window loads the below code assigns run-time data to the RunTimeDataContext property and then the DesignAndRunTimeDataContext.Refresh method is called which will cause this custom DataSourceProvider to re-query its data.  You can have a look at the DesignAndRunTimeDataContext class in the WPF project included in the downloads below.  Its a very simple class that provides DataContext switching between design time and run-time.

Partial Public Class DesignTimeDataTwo

    Private Sub DesignTimeDataTwo_Loaded( _
            ByVal sender As Object, _
            ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded

        Dim objDS As DesignAndRunTimeDataContext = _
            CType(Me.FindResource("customersDS"), DesignAndRunTimeDataContext)

        'could load any data here, I'm just using what we have for grins
        objDS.RuntimeDataContext = New DesignTimeData.DesignTimeCustomers

        objDS.Refresh()

    End Sub

End Class

The DataContext

Wiring up the DataContext to the above resource is very simple.  The ListBoxes ItemsSource property is also set and the ListBox performs a data binding operation at design time.

<Grid DataContext="{StaticResource customersDS}" >
    <ListBox x:Name="lbCustomer" ItemsSource="{Binding}">

Data Triggers

After I did the video, I got to thinking about Data Triggers, so I wrote this simple Data Trigger to see if the Cider Designer would render it or not.  Oh yes!  It works great!

<DataTemplate.Triggers>
    <DataTrigger Binding="{Binding Path=Id}" Value="1">
        <Setter TargetName="tbId" Property="Foreground" Value="Red" />
    </DataTrigger>
</DataTemplate.Triggers>

Look close at the below image, notice that the Id for the first row has a Foreground of Red.  This should give you enough to go with and learn to use the Data Triggers in your Data Templates.

WPFTwo

Actual Usage

Don’t forget to build your projects after editing the entity classes.

I have seen an error sometimes when working with this solution in Silverlight projects.  If you get an error when loading the designer, just leave the designer displayed and rebuild the project; the Cider Designer will correctly render your UserControl content.

Blend Compatibility

Guess I really don’t need to say much.  Here is Blend 2 with Blend SP1 applied editing a ListBox DataTemplate with design time data using this solution.  You can read about the latest Blend releases on Scott Guthrie’s blog.

BlendToo

After I posted this solution my friend and Microsoft MVP Laurent Bugnion wrote me about a solution he came up with last year that focuses on Blend.

Here is the link to his article:

http://blog.galasoft.ch/archive/2007/09/14/WPF-Simulating-data-in-design-mode-in-Microsoft-Expression-Blend.aspx

Also of interest, he has a sample WPF application demonstrating this technique:

http://blog.galasoft.ch/archive/2008/03/27/techdays-2008-slides-and-code-of-my-presentation-available-online.aspx

Thanks Laurnet!

Let’s Go!

Mark, Ben and I hope this simple solution provides each Visual Studio 2008 WPF & Silverlight developer a boost in design time productivity using the Cider Designer.  I don’t know about you, but I’m so juiced over this and now look forward to that next complex DataTemplate or DataGrid layout task!

Videos

The video links require Microsoft Silverlight 1.0.  If you do not have it, you will be prompted to install it when you click one of the links.  After the short installation is completed, close the browser window that you did the install in and re-click the video you want to watch.   You can also download it here. Windows XP or Vista required.

  Design Time Data Tutorial (only 5:46 long)

Downloads

After downloading the below package you must rename the extension from .DOC to .zip.  This is a requirement of WordPress.com.

Download Source WPF Project 279KB

Download Silverlight Source 548KB

Have a great day and feel the love!

Just a grain of sand on the worlds beaches.

Comments are closed.

Follow

Get every new post delivered to your Inbox.

Join 248 other followers