BrownBot Logo BrownBot Head

Stacking DataTemplates – Code Reuse in WPF

11:10 pm Filed under: WPF

Today I was working on the UI for our Product business objects which use a bit of inheritance, it turned into a nice concise example of a DataTemplate stacking technique that I use.

First have a quick look at the class diagram, you’ll see some basic inheritance and a few fields added at each level Product – Ingredient – Drug.

ProductsDiag Now check out the UI, you’ll see the fields added at each inheritance level correspond to a Blue expander.

ProductUI To achieve this effect I use a series of ContentControls, DataTemplates and a TemplateSelector to handle the splitting of the object types.

The main DataTemlpate for all Products looks like this:

<DataTemplate x:Key="ItemReadOnlyTemplate">
    <StackPanel>
        <Expander Header="Product Detail" IsExpanded="{Binding Path=ROExpanderStates[0], Source={StaticResource ViewModelODP}}">
            <Grid >
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="0.3*" />
                    <ColumnDefinition Width="0.7*" />
                </Grid.ColumnDefinitions>
                <Grid.RowDefinitions>
                    <RowDefinition />
                    <RowDefinition />
                    <RowDefinition />
                </Grid.RowDefinitions>
                <TextBlock Grid.Column="0" Grid.Row="0" Text="Code:" Style="{StaticResource LabelTBStyle}"/>
                <TextBlock Grid.Column="1" Grid.Row="0" Text="{Binding Path=Code}" Style="{StaticResource ValueTBStyle}"/>

                <Border Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="2" Style="{StaticResource AlternateGridTextBorderStyle}" />
                <TextBlock Grid.Column="0" Grid.Row="1" Text="Name:" Style="{StaticResource LabelTBStyle}"/>
                <TextBlock Grid.Column="1" Grid.Row="1" Text="{Binding Path=Name}"  Style="{StaticResource ValueTBStyle}"/>

                <TextBlock Grid.Column="0" Grid.Row="2" Text="Type:" Style="{StaticResource LabelTBStyle}"/>
                <TextBlock Grid.Column="1" Grid.Row="2" Text="{Binding Path=Type.Name}" Style="{StaticResource ValueTBStyle}"/>
            </Grid>
        </Expander>
        <ContentControl Content="{Binding}" ContentTemplateSelector="{StaticResource ROTempateSelector}" />
    </StackPanel>
</DataTemplate>

Expander displays all the fields from the base “Product” object, the ContentControl at the bottom is hooked up to a TemplateSelector to feed in any additional DataTemplates based on the object type.

The TemplateSelector definition look like this:

<Qaf_Local:ProductTemplateSelector x:Key="ROTempateSelector"
    DefaultTemplate="{StaticResource ItemRODefaultTemplate}"
    IngredientTemplate="{StaticResource ItemROIngredientTemplate}"
    DrugTemplate="{StaticResource ItemRODrugTemplate}"
    FinishedProductTemplate="{StaticResource ItemROFinishedProductTemplate}"
                                   />

Fairly self explanatory, the only DataTemlpate that does anything tricky is the ItemRODrugTemplate which uses a similar technique as above to stack the ItemROIngredientTemplate in it’s own DataTemplate, which looks like:

<DataTemplate x:Key="ItemRODrugTemplate">
    <StackPanel>
        <ContentControl Content="{Binding}" ContentTemplate="{StaticResource ItemROIngredientTemplate}" />
        <Expander Header="Drug Details" IsExpanded="{Binding Path=ROExpanderStates[2], Source={StaticResource ViewModelODP}}">
            <Grid >
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="0.3*" />
                    <ColumnDefinition Width="0.7*" />
                </Grid.ColumnDefinitions>
                <Grid.RowDefinitions>
                    <RowDefinition />
                </Grid.RowDefinitions>
                <TextBlock Grid.Column="0" Grid.Row="0" Text="Is S4:" Style="{StaticResource LabelTBStyle}"/>
                <TextBlock Grid.Column="1" Grid.Row="0" Text="{Binding Path=IsS4}" Style="{StaticResource ValueTBStyle}"/>
            </Grid>
        </Expander>
    </StackPanel>
</DataTemplate>

The one trick you need to look out for when stacking the DataTemplates like this is the weird binding with no path:

<ContentControl Content="{Binding}" ContentTemplate="{StaticResource ItemROIngredientTemplate}" />

This passes the current DataContext (the Product object) through to the ContentControl, without it you’ll get blank fields.

WPF Nav App With Added ZOOM

10:33 pm Filed under: C#,WPF

Here are the results of today’s work:

  • added some zoom controls on the list and detail views.
  • added scroll viewers for the zoomable parts so they behave nicely at the extremes and don’t zoom the clickable controls.
  • Standardized the detail views to 300px so the edit wrap panel looks good and the read only view can scale properly.
  • general cleaned up and lined up of all the elements and borders.

So now it looks like:

SearchSmall SearchLarge

EditSmall EditLarge

Much better, I’ve still got a ways to go on it, one standard feature I know the users are going to ask for is sorting by the grid header (the main reason the current client is still in Delphi 7)… not so easy to implement, I’m thinking some sort of encoded ButtonBase.Click event that passes a parameter through to the ViewModel and some Linq might be the go.

I’ll keep plugging away.

WPF Nav App Design Considerations

4:42 pm Filed under: C#,WPF

I had a great design session yesterday styling up the navigation/view/model app framework I’ve been designing as the basis for the administration program at the mill.

One thing I’m still struggling with is how to effectively handle the huge difference in screen real estate between 1024×768 and 1680×1050.. more than twice the pixels on the screen. The challenge is to not just to make it look half decent but to actually utilise the extra space for displaying data.

Who knows what res we’ll be running in 5 years time?

This is my first draft, my Widget object only has a hand full of fields so really struggles at high res.

Search/Select Screen

SearchSmall SearchLarge

Edit Screen

EditSmall EditLarge

I’m happy with the 3D grey tones, I think I’ve struck a nice balance between looks a function, everything white is pure data, anything intractable is bordered in mid Grey, anything darker is background. I don’t think it’s too heavy, the grays aren’t that far off your standard windows schemes.

With the edit screen I tried a set width on the group borders in a WrapPanel instead of a stack panel, I think that coupled with some zoom controls might be the ultimate solution.

More to come.

« Previous Page

Powered by WordPress