views:

770

answers:

2

How can I make a Datatemplate for a ViewModel blendable (designable in expression blend). When I go to resources and try to edit the DataTemplate directly all I see on the Drawingborad is a blank rectangle. This is because the DataTemplate is not bound to anything. Of course I can create a UserControl and create some designtime data in code there to see the template but I now would have to switch back and foruce between the resource (to edit) and the usercontrol (to see the result of my edit). Isn't there a more direct way to edit and see my DataTemplate?

+10  A: 

It's a bit of a stretch to use, but Blend has a feature called "Design-Time Data" that can help you out. It's tough to get started at first, but once you do a few it's pretty easy. It kind of forces you into a nice pattern for DataContext as well.

Here's a good link on the subject: http://www.robfe.com/2009/08/design-time-data-in-expression-blend-3/

Here's a few choice excerpts:

On Design-Time Sizes

...design time properties can be safely ignored by other tools and they are ignored at the runtime (mc:Ignorable specifies that the namespace with the "d" prefix can be ignored).

 xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
 mc:Ignorable="d"

Expression Blend uses two design time properties (d:DesignWidth, d:DesignHeight) to specify a size for a control to be used at design time...

On Design-Time Data Sources

I stumbled across d:Datacontext when I was playing with Blend 3 and tried adding a “live datasource” to my window. I thought it was going to behave just like the old way of setting a DataContext, but when I ran my application, there was no data! ...

So the upshot is, now we can write code like this:

...
<Grid ...
      DataContext="{StaticResource GameDataSource}"
      d:DataContext="{StaticResource DesignTime_DateDataSource}">

Note that this is for Blend 3 if you want first-party support for these features. They are pretty good - there's even a designer for the design-time data, though I've not looked into those features yet.

Applying To DataTemplates

This is something I sorta made up, but it seems to work. Here I'm using the Design-Time data feature to pull data into the visual element's d:DataContext. You'd have to do this for every top-level element that needed a DataContext set.

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">
    <!-- Resource dictionary entries should be defined here. -->
    <DataTemplate x:Key="MyTemplate">
     <TextBlock Text="{Binding Text}" d:DataContext="{StaticResource SampleDataSource}" />
    </DataTemplate>
</ResourceDictionary>

The binding syntax is a little bit more explicit if you are using a DataTemplate with a DataType set, but it still works:

<DataTemplate DataType="{x:Type vm:MyViewModel}" >
   <TextBlock Text="{Binding Text}" 
              d:DataContext="{Binding Source={StaticResource SampleDataSource}}" />
</DataTemplate>

This strategy will allow you to see how the DataTemplate will work while editing it directly, however you won't be able to see the result on any view that utilizes that DataTemplate unless you actually run the app. This is a limitation of Blend at the moment due to the fact that they don't appear to be using Mocks, but rather complete replacement objects. If blend ever adds the ability to create a new fake data source by clicking on "New DataSource -> Based on Referenced Object -> MyCustomerObject", then you will be in business.

It's possible you could overcome this limitation with some attached property trickery of your own, but it would be difficult at best.

Alternative

An alternative that will work in every situation, but is a bit more cumbersome to setup is setting up StaticResources that swap out fake data for real data during runtime, but in the designer show static sample data.

Here's a really great article by Paul Shifflet that includes some of these techniques and a few videos on it: http://karlshifflett.wordpress.com/2008/10/11/viewing-design-time-data-in-visual-studio-2008-cider-designer-in-wpf-and-silverlight-projects/

Hope this helps, Anderson

Anderson Imes
I am aware of those two features. I just can't find a way to use it with datatemplates. How would I assign designtime data to a DataTemplate? A Datatemplate itself doesn't have a DataContext property. I could create a window that references those datatemplates and set the datacontext on the window. So I can see the datatemplates. But I can not edit them there. I have to switch back to the resource tab and select it there to be able to edit it. And there it is just a blank rectangle. Am I missing something here? I would like to avoid switching back and forth between the windows and the resource
bitbonk
Generally you'd test from the control that caused the template to appear, but this would lead to switching back and forth between editing the resource and looking at the result in the hosting view (like you said). I've updated my post with a sample that sets the data context for the template using a Border. It's not ideal. I'll continue to investigate, but this ought to work for you.
Anderson Imes
I updated it, removing the Border. For each element that you want to set data context on, you'd do this, so if you have more than one parent element in your datatemplate, you'd set d:DataContext for each one. If you have one parent visual element (like Border) you'd set d:DataContext on just that and all of the sub-elements will get that datacontext. Hope this helps.
Anderson Imes
Yes, but the binding syntax is *slightly* different. I'll add that.
Anderson Imes
The trick for all of this is to use the blend contextmenu in in the "Objects and Timeline": "Edit Additional Templates"->"Edit Generated Items (ItemTemplate)" or "Edit Layout of Items (ItemsPanel)" or "Edit Generated Contet (ContentTemplate)" . I just havent managed to make all this work when I have DataTemplates with a DataType like this: <DataTemplate DataType={x:Type viewmodel:Company}> in this case the Blend Sample Data is not applied. Do you know a solution for this?
bitbonk
The sample I provided above works with blend sample data (it's what I used to build the sample, in fact). If you would like, I can upload my sample somewhere, but really there's no magic here.
Anderson Imes
I've updated my answer to reflect some of the things that came to light by looking at your sample. You won't be able to see the result of your design changes on a view that *hosts* the data template, but you will see how it looks when editing the data template itself.
Anderson Imes
Awesome post, if you've got a blog this'd make a great article
Paul Betts
A: 

This strategy will allow you to see how the DataTemplate will work while editing it directly, however you won't be able to see the result on any view that utilizes that DataTemplate unless you actually run the app. This is a limitation of Blend at the moment due to the fact that they don't appear to be using Mocks, but rather complete replacement objects. If blend ever adds the ability to create a new fake data source by clicking on "New DataSource -> Based on Referenced Object -> MyCustomerObject", then you will be in business.

If I want to use acutal ViewModel mocks I guess it is the best way to create actual ViewModel instances and the references them with d:DataContext (e.g. using a ObjectDataProvider or x:Static)

bitbonk
Well done, yes. I posted an "Alternative" section that gives information on this approach.
Anderson Imes