RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


WPF Wonders: Building Control Templates : Page 3

WPF's properties and styles let you change a control's appearance. Templates let you modify a control at a much more fundamental level, changing the components that make up the control and the way it acts. This article shows how you can use templates to change the fundamental structure of WPF controls.


Using Template Bindings

The temOutlinedLabel template is useful, albeit just barely. It displays a thick red outline around some text. Obviously, this template is useful only if you want a thick red border, but what if you wanted a thin green outline instead? You could try setting the Label's BorderBrush property to Green and the BorderThickness property to 1 manually—but because the brush and thickness are hard coded in the template's Border control, the template would ignore the new values.

The solution to this dilemma is similar to the one used to bring the text content into the template. Just as the ContentPresenter pulls the value assigned to the control (the Label's content in this case) into the template, template bindings can bring other values into the template.

To use a template binding, set a template property equal to a TemplateBinding object, specifying the name of the property that you want to bring in. The property should be one provided by the control using the template.

For example, to use the Label control's BorderBrush property, you might set the Border control's BorderBrush property to {TemplateBinding BorderBrush}.

Here's an improved version of the template that obeys the control's BorderBrush and BorderThickness properties.

<ControlTemplate x:Key="temOutlinedLabel" TargetType="Label">
     BorderBrush="{TemplateBinding BorderBrush}"
     BorderThickness="{TemplateBinding BorderThickness}">

The following code shows how the OutlinedLabel2 example program uses this template to produce a result similar to the one shown in Figure 2. Using this template, however, when you change the Label control's BorderBrush and BorderThickness properties, the template changes its Border control to match.

<Label Margin="10" Content="Outline Me!"
 BorderBrush="Red" BorderThickness="5"
 Template="{StaticResource temOutlinedLabel}"/>

You may be asking yourself, "What's the point?" After all, the Label control itself has BorderBrush and BorderThickness properties, so why not just use it without the template?"

The answer is that if you simply wanted to reproduce the features of a Label, it would be silly to create a template.

Here's a more interesting template. It starts with a Border that uses the Label control's BorderBrush and BorderThickness properties. That control also contains a second Border separated from the first by a margin the width of the Label's BorderThickness value. This inner Border contains the ContentPresenter. Notice that the ContentPresenter's HorizontalAlignment and VerticalAlignment properties center the content inside the Border.

<ControlTemplate x:Key="temOutlinedLabel" TargetType="Label">
     BorderBrush="{TemplateBinding BorderBrush}"
     BorderThickness="{TemplateBinding BorderThickness}">
        <Border Margin="{TemplateBinding BorderThickness}"
         BorderBrush="{TemplateBinding BorderBrush}"
         BorderThickness="{TemplateBinding BorderThickness}">
Figure 3. Refined Line: The DoubleOutlinedLabel program uses two Border controls to draw a Label with a double outline.

The DoubleOutlinedLabel example program uses this template in the following code. Figure 3 shows the result.

<Label Margin="10"
 Content="Outline Me!"
 BorderBrush="Green" BorderThickness="2"
 Template="{StaticResource temOutlinedLabel}"

Note that this template produces a result that you can't produce by simply manipulating the Label control's properties.

The following code shows an even more interesting Label template.

<ControlTemplate x:Key="temOutlinedLabel" TargetType="Label">
         Background="{TemplateBinding Background}"
         BorderBrush="{TemplateBinding BorderBrush}"
         BorderThickness="{TemplateBinding BorderThickness}">
            <Border Margin="{TemplateBinding BorderThickness}"
             BorderBrush="{TemplateBinding BorderBrush}"
             BorderThickness="{TemplateBinding BorderThickness}">
                <TextBlock TextWrapping="Wrap"
                 Text="{TemplateBinding ContentPresenter.Content}"
                 Margin="{TemplateBinding BorderThickness}"
        <Canvas Name="canDisabled" Opacity="0">
                 StartPoint="0,0" EndPoint="10,10"
                    <GradientStop Color="LightGray" Offset="0"/>
                    <GradientStop Color="Black" Offset="1"/>
        <Trigger Property="IsEnabled" Value="False">
            <Setter TargetName="canDisabled"
             Property="Opacity" Value="0.5"/>

This template uses a Grid to hold a Border and a Canvas that sits on top of the Border.

The first Border contains another Border as in the previous example, but this time the inner Border holds a TextBlock that provides features for wrapping and trimming text (ending text that won't fit with an ellipsis). Without such a template, a normal Label doesn't wrap text, and truncates any text that won't fit.

The Canvas is filled with a LinearGradientBrush whose Opacity is initially set to 0, so it is invisible.

Figure 4. Disabled Label: The BetterLabel example program uses the temOutlinedLabel template.

The template's <Triggers> section holds a single trigger. When the Label control's IsEnabled property is False, the trigger changes the Canvas's Opacity value to 0.5, so it becomes semi-transparent and partially obscures the Border.

Figure 4 shows two Labels that use this template, one enabled and one disabled. Both contain text that is too long to fit, so the template's TextBlock ends them with ellipses.

By now you should be able to see some of the possibilities. With a template you can make a Label do things that it would not be able to do otherwise.

Note that you could do roughly the same work to make a single Label in a XAML file act in the same way without the template. After all, the template just contains controls and triggers, and you could put them directly in your user interface. But using a template makes the code easier to manage and read, because it wraps all its controls in a neat little package. The template is also easy to reuse. If you have several Labels that should all have the same appearance and behavior, they can use the same template. If you want to use the template in another application, you can copy and paste it without having to reproduce all of the template's controls every time you want to use them.

Close Icon
Thanks for your registration, follow us on our social networks to keep up-to-date