Archives

Anticipation

  • No dates present

Templates Galore

(Well, look at that. I did manage to write another Programming post after all.)

One of the really great things about WPF is its composable UI structure and dynamic layouts — the ability to replace one set of controls with another on the fly as things happen (eg. when something is selected by the user) and have everything adjust accordingly. The data binding engine is one of the most important elements in this, but closely linked to it is the templating engine.

In this post I’ll cover a little bit of background behind DataTemplates, but the primary focus is going to be on the DataTemplateSelector — what it is, why you might want to use it, a way to make it easier to use, and finally a better alternative to using them at all.

So, first: DataTemplates. I’m not going to go into too much detail here (there are better references around for people who want to know more); suffice it to say that they are a means of defining a set of controls (or other UI elements, such as text or pictures) which somehow represent a particular model or presentation layer object. Sometimes they create controls that allow the user to edit the data object (via data binding), other times they’re purely for display; often a mix of both.

Many controls in WPF can be linked to DataTemplates to control the display of their contents; the most commonly used is the ItemTemplate property of ItemsControl (or its descendants, such as ListBox and ListView). And most commonly, the link is made directly, like so:

  <Window.Resources>
    <DataTemplate x:Key="FooTemplate">
      <WrapPanel>
        <TextBlock Text="Foo data: " />
        <TextBlock Text="{Binding Data}" />
      </WrapPanel>
    </DataTemplate>
  </Window.Resources>
  ...
  <ListBox ItemsSource="{Binding FooList}" ItemTemplate="{StaticResource FooTemplate}" />

This is a very basic example showing how to link a presentation control directly to a named template; it’s probably the most common type you’ll encounter in WPF code. But this is a bit restrictive — it only lets you define one template per collection. This is ok if your collection is uniform: it contains the same types of data and that data all needs to be rendered the same way; and of course that is indeed the most common case. But what if you have a heterogeneous collection, containing multiple types of items? WPF supports that too — if you don’t specify a template specifically, it will look for a template corresponding to the type of the data item:

  <Window.Resources>
    <DataTemplate DataType="{x:Type app:Foo}">
      <WrapPanel>
        <TextBlock Text="Foo: " />
        <TextBlock Text="{Binding Data}" />
      </WrapPanel>
    </DataTemplate>
    <DataTemplate DataType="{x:Type app:Bar}">
      <WrapPanel>
        <TextBlock Text="Bar: " />
        <CheckBox IsChecked="{Binding State}" Content="Active" />
      </WrapPanel>
    </DataTemplate>
  </Window.Resources>
  ...
  <ListBox ItemsSource="{Binding FooList}" />

A useful tip to know with this technique is that WPF starts searching for templates at the container itself (in this case, the ListBox) and works its way out to its parents; since any control can contain resources, this means that you can define several templates for the Foo type, and have them chosen by context. (It’s first to last, child to parent, and the first matching template wins, so if you want to define templates for derived classes and have a base class template as a fallback, the base class template should either be listed last in the same scope or listed in one of the parent scopes.)

Lists aren’t the only way to use templates, of course. One of the most handy (and seemingly overlooked) controls is the ContentPresenter, which is the “basic presenter” of the template system. You can use it wherever you have a single item of content to present:

  <ContentPresenter Content="{Binding MyFoo}" ContentTemplate="{StaticResource FooTemplate}" />

And of course, it also supports the DataType-based lookup as well.

Another useful ability of the ContentPresenter is that it can be used to “chain” templates, which is particularly handy if you have something in common between your templates (eg. if you have a base class with two derived classes, and want to show data from all three):

  <Window.Resources>
    <DataTemplate x:Key="BaseTemplate">
        ... whatever presentation you want for the base class (common to both derived classes) ...
    </DataTemplate>
    <DataTemplate DataType="{x:Type app:DerivedA}">
      <StackPanel>
        <ContentPresenter Content="{Binding}" ContentTemplate="{StaticResource BaseTemplate}" />
        ... whatever other presentation you want for DerivedA ...
      </StackPanel>
    </DataTemplate>
    <DataTemplate DataType="{x:Type app:DerivedB}">
      <StackPanel>
        <ContentPresenter Content="{Binding}" ContentTemplate="{StaticResource BaseTemplate}" />
        ... whatever other presentation you want for DerivedB ...
      </StackPanel>
    </DataTemplate>

The downside of this is that layout can’t be shared between templates, so you can’t split multiple cells of a grid between two templates (though you can contain a complete child object within one cell of the grid, as normal).

If you’re using it for the base class, as shown here, you have to use {Binding} and bind to a named template (you can’t use the type, since that’d be the derived type again and it’d get into a loop). If you’re using it for some common member (ie. you’re specifying a binding path) then you can use either kind of lookup.

So, to recap thus far, we’ve seen how to locate a template by name or by the type of the data object. But what if your requirements are more complicated? What if some property on the object needs to define which template to use? For example, let’s say that you have a ReadOnlyTemplate (display controls only) and an EditableTemplate (with editing controls), and you want to show one or the other depending on the ReadOnly property of the data item.

One way to do this is to use a DataTemplateSelector. I’m not going to post a full example of this (it’s long and boring), but basically the way this works is that you have to create a new class in your code that inherits from DataTemplateSelector, define an instance of that class in your Resources, and then reference that on your presentation control (via ItemTemplateSelector for item controls or ContentTemplateSelector for the ContentPresenter, for example), instead of specifying the template directly.

Once you’ve done that, at runtime WPF will call the SelectTemplate method of your class with each data item so that you can evaluate whatever you need to in order to decide which template it should be using. This concept is powerful, of course, but it can get tedious to write all those selector classes, especially if the decision is fairly simple (as with our ReadOnly property example).

So now I’m going to introduce a handy little class that should mean you’ll almost never need to write your own template selector classes again — the GenericDataTemplateSelector (the link is to the complete source file).

This class is based on one I found here; I’ve simplified some parts and added some new features. For the most part, it will let you select between your templates with just a bit of XAML:

  <Window.Resources>
    <DataTemplate x:Key="ReadOnlyTemplate">...</DataTemplate>
    <DataTemplate x:Key="EditableTemplate">...</DataTemplate>
    <DataTemplate x:Key="BarTemplate">...</DataTemplate>
    <msi:GenericDataTemplateSelector x:Key="FooTemplates">
      <msi:GenericDataTemplateSelectorItem Template="{StaticResource BarTemplate}"
                                           TemplatedType="{x:Type app:Bar}" />
      <msi:GenericDataTemplateSelectorItem Template="{StaticResource EditableTemplate}"
                                           PropertyName="ReadOnly" Value="false" />
      <msi:GenericDataTemplateSelectorItem Template="{StaticResource ReadOnlyTemplate}" />
    </msi:GenericDataTemplateSelector>
  </Window.Resources>
  ...
  <ListBox Items="{Binding FooList}" ItemTemplateSelector="{StaticResource FooTemplates}" />

Essentially, you’re listing all the possible templates in turn (any number can be listed; the first one that matches the conditions will “win”). Each selector item must have one Template and can have various conditions imposed on it; specifying no conditions will make that template always win when it is reached, so is best used as a catch-all default template at the end of the list. Possible conditions:

Condition Description
PropertyName Specifies the name of the property to look at on the data object. Requires Value, conflicts with PropertyPath.
PropertyPath Specifies the path (in Binding syntax) to the property to look at on the data object. Requires Value, conflicts with PropertyName.
Value Specifies the expected value of the property defined in either PropertyName or PropertyPath.
TemplatedType Indicates that the data item must be (or be derived from) this type (specified with {x:Type ...}).

If possible, you should use PropertyName over PropertyPath, as the former is more efficient. The latter is more flexible, though, since it allows you to specify indexers and subproperties, just like in bindings. In either case, the selector will try to convert the Value you’ve specified to the appropriate type before comparison, so you should be able to use simple strings in place of enums, bools, and other more complex types that provide conversion (such as Color).

This works best when the properties are fairly fixed — eg. any given Foo never changes between ReadOnly and editable. This is because the template selector is only called once for any given item — if you toggle the ReadOnly property of an existing object, by default it won’t update the UI (and show/hide the editable controls).

If you do need to have it changing on the fly (perhaps ReadOnly has been bound to a checkbox, so the user can toggle the state, and you want it to dynamically switch between the editable and readonly templates accordingly), then there are a couple of solutions.

The first was mentioned on the original blog page I linked to earlier — you need to write a bit of code that gets called whenever something changes that may affect which templates need to be used:

  DataTemplateSelector old = listbox.DataTemplateSelector;
  listbox.DataTemplateSelector = null;
  listbox.DataTemplateSelector = old;

“Pulsing” the selector like this will force WPF to update the templates on all of the items; it’s a bit ugly though. Worse than that, if the presentation control is itself within a template then it’s much harder to get a reference to it from code, making this approach a lot more painful.

Finally, here’s an alternative to the whole DataTemplateSelector thing, using another set of WPF’s powerful features: Styles and Triggers.

  <Window.Resources>
    <DataTemplate x:Key="ReadOnlyTemplate">...</DataTemplate>
    <DataTemplate x:Key="EditableTemplate">...</DataTemplate>
    <DataTemplate DataType="{x:Type app:Bar}">...</DataTemplate>
    <DataTemplate DataType="{x:Type app:Foo}">
      <ContentPresenter Content="{Binding}">
        <ContentPresenter.Resources>
          <!-- this is just to help prevent cycles; you can skip it if this
                ContentPresenter is in a named template -->
          <DataTemplate DataType="{x:Type app:Foo}" />
        </ContentPresenter.Resources>
        <ContentPresenter.Style>
          <Style TargetType="{x:Type ContentPresenter}">
            <Setter Property="ContentTemplate" Value="{StaticResource ReadOnlyTemplate}" />
            <Style.Triggers>
              <DataTrigger Binding="{Binding ReadOnly}" Value="false">
                <Setter Property="ContentTemplate" Value="{StaticResource EditableTemplate}" />
              </DataTrigger>
            </Style.Triggers>
          </Style>
        </ContentPresenter.Style>
      </ContentPresenter>
    </DataTemplate>
  </Window.Resources>
  ...
  <ListBox Items="{Binding FooList}" />

This is a bit more wordy than the earlier examples, of course (although some of that can be removed if you’re using named templates, which is the best option provided you don’t need to also select templates by type). But because you’re going through the style and binding engine, WPF will be watching the ReadOnly property specifically, and will automatically switch templates if it changes (assuming that the property is observable, of course — but anything you bind to the UI should be made observable). With this sort of responsiveness, you can do all kinds of cool things with your UI. (The ReadOnlyTemplate is the default, since it appears outside of any triggers.)

Hopefully that all made sense to people. Any questions?

1 comment to Templates Galore


  • Catchable fatal error: Object of class WP_Comment could not be converted to string in /home/uecasm/public_html/lambert/wpdata/wp-content/themes/atahualpa/functions/bfa_custom_comments.php on line 16