WPF Design Time Data and Data Templates

514 views Asked by At

I'm developing a WPF user control and I want it to display some design-time data. It's proving to be quite a challenge to get things just right and I could really use your help.

Here's my control so far, basically a <ListBox>:

<UserControl x:Class="TA.Weather.UI.Wpf.WorkingSetEditor"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300"
             Loaded="HandleLoadedEvent"
             >
    <Grid d:DataContext="{d:DesignData Source=/SampleData/WorkingSet.xaml}">
        <ListBox x:Name="WorkingSetListBox"  >
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition MinWidth="30" />
                    <ColumnDefinition MinWidth="30" />
                    <ColumnDefinition MinWidth="30" />
                    <ColumnDefinition MinWidth="30" />
                    <ColumnDefinition MinWidth="30" />
                    <ColumnDefinition />
                </Grid.ColumnDefinitions>
                <TextBlock Grid.Column="0" TextAlignment="Center"
                           Text="{Binding Path=[0].Limits.StationId}"></TextBlock>
                <TextBlock Grid.Column="1" TextAlignment="Center"
                           Text="{Binding Path=[0].Limits.SensorId}"></TextBlock>
                <TextBlock Grid.Column="2" TextAlignment="Center"
                           Text="{Binding Path=[0].Limits.SensorName}"></TextBlock>
                <TextBlock Grid.Column="3" TextAlignment="Center"
                           Text="{Binding Path=[0].Limits.LowerLimit}"></TextBlock>
                <TextBlock Grid.Column="4" TextAlignment="Center"
                           Text="{Binding Path=[0].Limits.UpperLimit}"></TextBlock>
                <CheckBox Grid.Column="5" x:Name="MonitoredCheckBox"
                          IsChecked="{Binding Path=[1].Monitored}"/>
            </Grid>
        </ListBox>
    </Grid>
</UserControl>

I figured out how to create the design-time data in another XAML file, and set the build action to DesignDataWithDesignTimeCreatableTypes. The type is essentially just a DTO so it seems OK to create instances at design time. So here's the data file, containing an List<SensorState> with two members:

<generic:List x:TypeArguments="ws:SensorState"
              xmlns:ws="clr-namespace:TA.Weather.Core;assembly=TA.Weather.Core"
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
              xmlns:generic="clr-namespace:System.Collections.Generic;assembly=mscorlib">
    <ws:SensorState IdleTimeout="300" LastUpdate="2015-08-19T03:39:00.000Z" Monitored="False"
                    WithinAcceptableLimits="False">
        <ws:SensorState.Limits>
            <ws:SensorLimits StationId="12-34-56-78-90-12" SensorId="1" SensorName="Inside Temperature"
                             LowerLimit="-10" UpperLimit="30" />
        </ws:SensorState.Limits>
    </ws:SensorState>
    <ws:SensorState IdleTimeout="300" LastUpdate="2015-08-19T03:39:00.000Z" Monitored="True"
                    WithinAcceptableLimits="False">
        <ws:SensorState.Limits>
            <ws:SensorLimits StationId="12-34-56-78-90-12" SensorId="1" SensorName="Inside Temperature"
                             LowerLimit="-10" UpperLimit="30" />
        </ws:SensorState.Limits>
    </ws:SensorState>
</generic:List>

Thus far, this gets me a user control that looks like this: User control with design-time data

So, success of sorts. However, there are two questions at this point:

  • Why does only one row show up when there are two items in the list? I can control which instance shows up by changing the array index in the binding expression, but how do I get the rows to repeat? I tried removing the array index but that just gives a compile error.
  • I'm getting errors against the data file of the form "The member XXX is not recognized or is not accessible", one for each property that I'm setting, 36 in total. Even though the build succeeds and the design-time data shows up in the editor. They are errors so I'm sure they are important - I just have no idea what the compiler is trying to tell me. So what do these errors mean and what do I need to do so mitigate them?

There's more. I want to use a DataTemplate to create a nicer look for the data; a bit later on there is going to be a slider control for setting the limits, etc. so here's how I've tried to use a template:

<UserControl x:Class="TA.Weather.UI.Wpf.WorkingSetEditor"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:core="clr-namespace:TA.Weather.Core;assembly=TA.Weather.Core"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300"
             Loaded="HandleLoadedEvent"
             >
    <Grid d:DataContext="{d:DesignData Source=/SampleData/WorkingSet.xaml}">
        <Grid.Resources>
            <DataTemplate x:Key="WorkingSetTemplate">
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition MinWidth="30" />
                        <ColumnDefinition MinWidth="30" />
                        <ColumnDefinition MinWidth="30" />
                        <ColumnDefinition MinWidth="30" />
                        <ColumnDefinition MinWidth="30" />
                        <ColumnDefinition />
                    </Grid.ColumnDefinitions>
                    <TextBlock Grid.Column="0" TextAlignment="Center"
                           Text="{Binding Path=[0].Limits.StationId}"></TextBlock>
                    <TextBlock Grid.Column="1" TextAlignment="Center"
                           Text="{Binding Path=[0].Limits.SensorId}"></TextBlock>
                    <TextBlock Grid.Column="2" TextAlignment="Center"
                           Text="{Binding Path=[0].Limits.SensorName}"></TextBlock>
                    <TextBlock Grid.Column="3" TextAlignment="Center"
                           Text="{Binding Path=[0].Limits.LowerLimit}"></TextBlock>
                    <TextBlock Grid.Column="4" TextAlignment="Center"
                           Text="{Binding Path=[0].Limits.UpperLimit}"></TextBlock>
                    <CheckBox Grid.Column="5" x:Name="MonitoredCheckBox"
                          IsChecked="{Binding Path=[1].Monitored}"/>
                </Grid>

            </DataTemplate>
        </Grid.Resources>
        <ListBox x:Name="WorkingSetListBox" ItemTemplate="{DynamicResource WorkingSetTemplate}">
        </ListBox>
    </Grid>
</UserControl>

So basically, I've moved the <Grid> out of the <ListBox> and into the <DataTemplate> resource. As soon as I do this, the design-time data vanishes and I'm left with just a blank control in the designer. So my third question becomes:

  • Why does the design-time data vanish when I use a <DataTemplate>? What do I need to do differently?

You can probably tell that I have almost no experience with WPF... I must admit I am struggling with binding expressions a little bit, they seem like some sort of arcane magic with little rhyme or reason to them. I think maybe I'm missing an important concept somewhere. I hope you can give me a few wise words to push me in the right direction.

0

There are 0 answers