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


WPF Wonders: Building Control Templates : Page 5

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.


Spectacular ScrollBars

While a Button provides a lot of states and interacts closely with the user, it's still a relatively simple control. It has only a single part, even if you built it out of a bunch of pieces.

In contrast, many controls contain several pieces that have distinct functions. For example, consider the ScrollBar shown in Figure 6. A ScrollBar contains two RepeatButtons on its ends and a Track in the middle. The Track contains two more RepeatButtons and a Thumb.

Figure 6. ScrollBar Parts: A ScrollBar consists of two RepeatButtons and a Track. The Track consists of two more RepeatButtons and a Thumb.

All these pieces must work together to make the control function properly. For example, when the user clicks and drags the Thumb, the control must adjust the sizes of the RepeatButtons on either side of it.

To make all of the pieces fit together properly, you need to add two techniques to those demonstrated in the Button example.

First, you need to give certain key controls specific names, so the ScrollBar can identify its parts. In this example, name the Track control PART_Track. You can learn what controls need special names by searching for "PART_" in the online documentation. For example, here's the ScrollBar's documentation.

The second thing required to make the pieces fit together is to provide Command attributes to the component controls to tell the ScrollBar what's happening. For example, when the user clicks the ScrollBar's leftmost RepeatButton, that control should invoke the ScrollBar's LineLeftCommand to tell it that it should adjust its Value by the SmallChangeAmount.

For a ScrollBar, the commands are:

  • ScrollBar.LineLeftCommand: The user clicked the small decrease button.
  • ScrollBar.PageLeftCommand: The user clicked the large decrease button.
  • ScrollBar.LineRightCommand: The user clicked the small increase button.
  • ScrollBar.PageRightCommand: The user clicked the large increase button.
  • ScrollBar.LineUpCommand: The user clicked the small decrease button in vertical orientation.
  • ScrollBar.PageUpCommand: The user clicked the large decrease button in vertical orientation.
  • ScrollBar.LineDownCommand: The user clicked the small increase button in vertical orientation.
  • ScrollBar.PageDownCommand: The user clicked the large increase button in vertical orientation.
Figure 7. Simple ScrollBar: The SimpleScrollBar example program demonstrates normal and templated ScrollBars.

The SimpleScrollBar example program is shown in Figure 7. The pale controls are standard ScrollBars and the shaded controls use a customized template. Unfortunately, the code is quite long and contains a lot of boring detail so I'll discuss only the highlights here. You can download the example program to see the template it all of its glory.

The template's structure begins with a Grid control that has three rows and three columns. Depending on the ScrollBar's orientation, the constituent controls are arranged vertically or horizontally so they span the Grid's width or height. For example, if the ScrollBar is oriented vertically, then its small Up button (with the red plus sign in Figure 7) gets Row = 0, Column = 0, ColumnSpan = 3 so the control spans the Grid's upper row.

The Grid contains two RepeatButtons with a Track control in between.

The following code shows the first RepeatButton's definition. The control's content is a Path that draws a thick red horizontal dash.

<RepeatButton Name="btnSmallDown" Grid.Column="0"
 Background="Black" MinHeight="17" Focusable="False">
    <Path HorizontalAlignment="Center" VerticalAlignment="Center"
     Data="M2,3 L10,3" Stroke="Red" StrokeThickness="3"
     StrokeStartLineCap="Round" StrokeEndLineCap="Round"/>

Notice that the control's Command attribute is set to ScrollBar.LineLeftCommand. When the RepeatButton fires, it calls the ScrollBar's LineLeftCommand so the control decreases its Value by the SmallChange amount.

The following code shows the Track control's definition. Notice that it's named PART_Track. That lets the ScrollBar know that this is its track, so it can arrange the Track's component controls appropriately.

<Track Grid.Column="1" Name="PART_Track" Margin="3">
        <RepeatButton Name="btnDecrease"
         Background="{StaticResource brLeft}" Focusable="False"/>
        <RepeatButton Name="btnIncrease"
         Background="{StaticResource brRight}" Focusable="False"/>
        <Thumb Margin="-3" Background="Red" />

The Track contains two more RepeatButtons (with appropriate Command attributes) and a Thumb.

The RepeatButtons have Background properties set to brushes defined in the template's Resources section. Those brushes give the RepeatButtons the shaded appearance you can see in Figure 7.

The template's triggers change properties depending on whether the control is oriented vertically or horizontally. The triggers position the controls in the proper Grid rows and columns, and set their Command attributes (for example, the small down button's command is either LineLeftCommand or LineUpCommand). They also set the Track's RepeatButtons' Background properties to horizontal or vertical gradient brushes.

The following code shows part of the template's Triggers section.

    <Trigger Property="Orientation" Value="Horizontal">
        <Setter TargetName="btnSmallDown"
         Property="Grid.Column" Value="0"/>
        <Setter TargetName="btnSmallDown"
         Property="Grid.Row" Value="0"/>
        <Setter TargetName="btnSmallDown"
         Property="Grid.RowSpan" Value="3"/>
        <Setter TargetName="btnSmallDown"
         Property="Command" Value="ScrollBar.LineLeftCommand"/>
        ... Position the other controls ...
        <Setter TargetName="btnDecrease"
         Property="Background" Value="{StaticResource brLeft}"/>
        <Setter TargetName="btnIncrease"
         Property="Background" Value="{StaticResource brRight}"/>
    <Trigger Property="Orientation" Value="Vertical">
        ... Similar code for vertical orientation ...

This template produces a distinctive look, but it's still not completely customized. The RepeatButtons it uses are still fairly generic. Although these controls display distinctive backgrounds, they have the normal RepeatButton behaviors. In particular, when the mouse floats over one, it changes its appearances to the mouse-over-button look. To fix that and any other remaining behaviors you don't like, you could make a separate template for the RepeatButtons.

Learning About Templates

Building a template for a complicated control such as a ScrollBar takes a lot of work. To get it right, you need to know what parts make up the control, what commands they should invoke, and what special names they need. So how do you learn all of this?

A good starting point is Microsoft's Control Styles and Templates Web page. That page provides links to other pages that describe different kinds of controls and the states they should handle. Those pages also show the controls' default templates (which are generally quite long).

Figure 8. Template Tattletale: The ShowTemplate example program makes a control display its template.

You can also interrogate a control to make it give up its secrets. The ShowTemplate example program shown in Figure 8 makes a control (a Slider in this case) display its template.

The following code shows how the ShowTemplate program works.

XmlWriterSettings writer_settings = new XmlWriterSettings();
writer_settings.Indent = true;
writer_settings.IndentChars = "    ";
writer_settings.NewLineOnAttributes = true;
StringBuilder sb = new StringBuilder();
XmlWriter xml_writer = XmlWriter.Create(sb, writer_settings);
XamlWriter.Save(Target.Template, xml_writer);
txtResult.Text = sb.ToString();

When you click its button, the program creates an XmlWriterSettings object and sets its properties to produce a nicely formatted output. It creates a StringBuilder that uses the settings and attaches it to an XmlWriter. The code uses the XamlWriter class to save the control's template into the XmlWriter and displays the result.

To display the templates for other controls, simply replace the Slider with another control, name it Target, and run the program.

Download the examples and give them a try. Then try making some changes to see what you can accomplish. Handling all of the myriad details needed to build a really robust template can be challenging but the results can be spectacular.

Rod Stephens is a consultant and author who has written more than a dozen books and two hundred magazine articles, mostly about Visual Basic. During his career he has worked on an eclectic assortment of applications for repair dispatch, fuel tax tracking, professional football training, wastewater treatment, geographic mapping, and ticket sales. His VB Helper web site receives more than 7 million hits per month and provides three newsletters and thousands of tips, tricks, and examples for Visual Basic programmers.
Email AuthorEmail Author
Close Icon
Thanks for your registration, follow us on our social networks to keep up-to-date