Templates for Uncommon Controls

Charles Petzold

Code download available at:Foundations2008_01.exe(784 KB)


Elements and Controls
Default Templates and DLLs
Control Derivation Basics
A Hierarchy of Elements and Controls
Code and XAML
Generating Commands
Replacing the Template

For programmers who enjoy turning common controls into uncommon visual objects, Windows® Presentation Foundation (WPF) offers an exciting feature known as the template. Traditionally, the functionality of a control and its visual appearance were bound together deep within the control's code. In WPF, the functionality of a control is still implemented in code, but the visuals are decoupled from that code and take the form of a template defined in XAML. By creating a new template—most often in XAML without a lick of code—programmers and designers can completely revise the visual appearance of the control without having to modify the control's code.

In my inaugural column one year ago, I showed how to design templates for ScrollBar, ProgressBar, and Slider controls. But the templating feature has a flip side: when designing a new custom control, you should provide a default template for the control's visual appearance and also allow that template to be replaced by programmers using your control. You aren't strictly required to structure your control like this—indeed, none of the custom controls in my book Applications = Code + Markup (Microsoft Press®, 2006) define replaceable templates—but people who need to use that control (including you) will be a lot happier if you do.

The purpose of this column is not to create fully functional and picturesque controls but to establish the mechanics for defining default replaceable templates for controls distributed in dynamic link libraries. Many of the templating techniques I discuss here I learned by examining templates on existing WPF controls. If you want to do the same, the DumpControlTemplate program in chapter 25 of Applications = Code + Markup lets you extract the default templates from all the standard WPF controls in convenient XAML form.

Elements and Controls

Programmers with experience in previous Windows client programming environments soon discover a curiosity in the WPF class hierarchy. In the native Windows API, for example, everything that has an on-screen visual appearance is categorized as a "window," while in Windows Forms, everything is a "control." But in WPF, the Control class derives from FrameworkElement, as do many other visual objects—most notably TextBlock, Image, Decorator, and Panel. What exactly, then, is the difference between an element and a control?

First, the Control class adds a collection of very handy properties to the FrameworkElement class, including Foreground, Background, and five font-related properties. Control doesn't use these properties directly—they're for the convenience of classes that derive from Control.

Second, the Control class adds IsTabStop and TabIndex properties, implying that controls often need to be stops in the tab-key navigation chain, while elements do not. In short, elements are for looking and controls are for interaction (but elements can still obtain focus and respond to keyboard, mouse, and stylus input).

Third, the Control class defines the Template property of type ControlTemplate. This template is mostly a visual tree of elements and other controls that make up the control's visual appearance and also often contains triggers that change this visual appearance based on property changes and events.

This third feature means that classes that derive from Control have a customizable visual appearance, while other classes that derive from FrameworkElement do not. Certainly TextBlock and Image have a visual appearance, but customizing these visuals doesn't make sense because these elements don't add anything to the formatted text or the bitmap they display. A ScrollBar, on the other hand, can have a variety of appearances and still be functionally the same. That's what the template is for.

For a programmer, here's perhaps the biggest difference between elements and controls: if you derive from FrameworkElement, you'll very likely need to override MeasureOverride, ArrangeOverride, and OnRender to render the element's visual elements and its children on the screen. If you derive from Control, you do not usually need to override these methods because the control's visuals are defined by the visual tree in the Template property's ControlTemplate object.

WPF includes a class named UserControl that derives from Control by way of ContentControl. This UserControl is often recommended as a base class for simple custom controls, and it serves fine for many purposes. The DatePicker control in Chapter 25 of my book derives from UserControl, for example. But keep in mind this important distinction between Control and UserControl: when deriving from UserControl, you can define a visual tree in XAML, but this visual tree is a child of UserControl's Content property. UserControl has its own simple default template that you probably won't replace because it simply nests a ContentPresenter inside a Border.

The visual tree of a class that derives from UserControl is not meant to be replaced, so the code of that class and the visual tree can be more tightly coupled. Conversely, when you intend to derive from Control and supply a replaceable default template, the interaction between the code and visual tree should instead be both simple and well-documented.

Default Templates and DLLs

For this column, I decided to take another crack at calendar controls. The source code for this column is a single Visual Studio® solution named CalendarTemplateDemo. It contains a library project that creates a DLL named CalendarControls and four demonstration programs that use these controls.

The CalendarControls library contains code and default templates for three controls in the CalendarControls namespace named CalendarMonth, CalendarDay, and CalendarDayNotes. Each of these classes is defined in its own C# file. The first two classes derive from Control, and CalendarDayNotes derives from CalendarDay.

There are very strict rules for defining default templates in a DLL so they can be replaced by applications using the controls. If you don't follow these rules, you're likely to create a control that doesn't have a default template or one that has a template that can't be replaced by an application. The DLL project must have a directory named Themes containing a file named generic.xaml with a root element of ResourceDictionary. The resources are Style elements targeting the controls in your DLL. The generic.xaml file in my CalendarControls library might look something like Figure 1.

Figure 1 Generic.xaml for CalendarControls

<ResourceDictionary xmlns="" xmlns:x="" xmlns:cc="clr-namespace:CalendarControls"> <Style TargetType="cc:CalendarMonth" /> ... </Style> <Style TargetType="cc:CalendarDay" /> ... </Style> <Style TargetType="cc:CalendarDayNotes" /> ... </Style> </ResourceDictionary>

Each of the Style elements in this file has at least one Setter element for setting the control's default template:

<Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="cc:CalendarMonth"> ... </ControlTemplate> </Setter.Value> </Setter>

The ControlTemplate contains the visual tree for the control. Optionally, it contains a Resources section for defining some resources used within the template. Very often, there will be a Triggers section that defines how the template reacts to changes in other properties or events.

Alternatively, you can separate these default templates into separate files, which is what I did. The Themes directory of the CalendarControls project contains three files named CalendarMonthStyle.xaml, CalendarDayStyle.xaml, and CalendarDayNotesStyle.xaml. Each of these files has a root element of ResourceDictionary and a single child of type Style targeting the particular control. The generic.xaml file references these three resource files, as shown in Figure 2.

Figure 2 Referencing Themes from Generic.xaml

<ResourceDictionary xmlns="" xmlns:x="" xmlns:cc="clr-namespace:CalendarControls"> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="CalendarControls;Component/themes/ CalendarMonthStyle.xaml" /> <ResourceDictionary Source="CalendarControls;Component/themes/ CalendarDayStyle.xaml" /> <ResourceDictionary Source="CalendarControls;Component/themes/CalendarDayNotesStyle.xaml" /> </ResourceDictionary.MergedDictionaries> </ResourceDictionary>

To ensure that these styles are successfully united with objects of type CalendarMonth, CalendarDay, and CalendarDayNotes, these classes must return a correct value from their DefaultStyleKey properties. For this reason, the static constructors of these classes alter the default value of that property to return the type of the class. This is accomplished by overriding the metadata of the DefaultStyleKey dependency property:

static CalendarMonth() { DefaultStyleKeyProperty.OverrideMetadata(typeof(CalendarMonth), new FrameworkPropertyMetadata(typeof(CalendarMonth))); ... }

The first argument to OverrideMetadata is the type of the class modifying the metadata; the argument to the FrameworkPropertyMetadata constructor indicates the new default value of the DefaultStyleKey property, which is also the type of the class.

The assembly information file for the DLL (a file usually named AssemblyInfo.cs) is a very good place to put the following assembly attribute:

[assembly: ThemeInfo(ResourceDictionaryLocation.None, ResourceDictionaryLocation.SourceAssembly)]

This means that there is no theme-specific set of templates, but generic templates for the controls are located in the same assembly (the DLL) as the controls themselves.

Control Derivation Basics

When deriving from Control, you'll likely need to define a few new public properties. In most cases, you should back these properties with dependency properties so they can be targets of data bindings and animations. Even if they are get-only properties and can't be binding targets, the use of dependency properties provides a notification mechanism so that other objects can be informed when these properties change.

If you need to define a property that is already defined by some other class but that you do not inherit from Control, don't entirely redefine the property. Instead, call AddOwner on the existing dependency property. Save the return value from AddOwner as a public static read-only field and use that value in the CLR property.

If you need to change the default value of an inherited property, you have a couple of options. Simply setting the property's value in the class's constructor is not a good idea because this so-called local setting has a very high precedence and can't be overridden by a style or property. The low-precedence approach is calling OverrideMetadata on the dependency property, as I showed earlier with the DefaultStyleKey property. The Control class itself sets a default true value of IsTabStop in this manner.

Another low-precedence approach is setting a property with a Setter in the same Style element as the control's default template. Many of the WPF controls use this technique to set properties that relate to the visual appearance of the control, such as SnapsToDevicePixels, MinWidth, or MinHeight. If your class needs to be notified whenever an inherited property's value changes, you can use OverrideMetadata to install an additional callback method.

One of the biggest challenges in deriving from Control is defining the interactions between the code and the template in a way that provides for all of your needs but won't baffle someone trying to write an alternative template. Generally, your control code accommodates the template in several ways:

  • The code defines properties that can be accessed by the template through the TemplateBinding markup extension.
  • The code defines properties and events that the template can use as triggers.
  • The code defines RoutedCommand properties or fields that buttons within the template can fire.
  • The code assumes that certain helper elements are present in the template; sometimes it references these elements through pre-defined names.

I'll have examples of all these techniques.

In some cases, your Control derivative class will need to be aware of when the template is replaced so that it can access the new template and establish links to it. The Control class defines a virtual OnTemplateChanged method that you can override to be notified when the Template property has changed. However, it is my experience that the OnTemplateChanged method is not really a very good place to perform any required linking because the template has not yet been applied (to use the WPF jargon). A much better strategy to employ is overriding the OnApplyTemplate method that, oddly enough, is defined by FrameworkElement but has no default implementation.

A Hierarchy of Elements and Controls

The MonthCalendar control in the CalendarControls library displays a single month of the year. The TwoCalendars program displays two instances of MonthCalendar, one in English and the other in French, as shown in Figure 3.

Figure 3 Two Instances of MonthCalendar

Figure 3** Two Instances of MonthCalendar **(Click the image for a larger view)

It is in keeping with the WPF design philosophy that default templates look just like their Win32® counterparts—which are usually somewhat bland, and the calendars you see here certainly are. But one of my goals from the beginning of this project was to correctly implement calendars that don't necessarily begin with Sunday (or dimanche, in French). However, I didn't venture beyond the Gregorian calendar.

In order to keep these examples relatively simple, I decided that I would not implement any concept of selecting dates. As you'll see, however, there is a property named IsToday that causes a particular day to be highlighted.

One of the big lessons I've learned with WPF is that complex controls can be built from simpler controls and elements. None of what you see in Figure 3 is defined by the code part of CalendarMonth. It's all part of the default XAML template: a border surrounds the whole thing and provides a background. The buttons on top are RepeatButton controls; they navigate backward and forward by one month or one year. Between the buttons is a TextBlock. The days of the week are StatusBarItem objects on a StatusBar using a UniformGrid as an ItemsPanel. The days of the month are also displayed in a UniformGrid.

For reasons that will become apparent, I decided that the individual days should be a separate control that I called CalendarDay. CalendarDay also derives from Control; the default template is a Border containing a TextBlock. The CalendarMonth control generates 28, 29, 30, or 31 CalendarDay objects when it displays a month. The TwoCalendars.xaml file that displays these two calendars is shown in Figure 4.

Figure 4 TwoCalendars.xaml

<!-- TwoCalendars.xaml by Charles Petzold, Sept. 2007 --> <Window xmlns="" xmlns:x="" xmlns:cc="clr-namespace:CalendarControls;assembly=CalendarControls" x:Class="Petzold.TwoCalendars.TwoCalendars" Title="Two Calendars Demonstration"> <Window.Resources></Window.Resources> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" /> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <cc:CalendarMonth Grid.Column="0" Margin="24" /> <cc:CalendarMonth Grid.Column="1" Margin="24" Culture="fr-FR" /> </Grid> </Window>

Code and XAML

Because CalendarDay is much simpler than CalendarMonth and short enough to show in its entirety, that's the part of this project I'd like to focus on first. Figure 5 shows CalendarDay.cs. Notice that this is not a partial class. The default template is not part of the CalendarDay class; instead, it is automatically applied to objects of type CalendarDay.

Figure 5 CalendarDay.cs

// CalendarDay.cs by Charles Petzold, Sept. 2007 using System; using System.Windows; using System.Windows.Controls; namespace CalendarControls { public class CalendarDay : Control { // Static constructor: Change defaults. static CalendarDay() { DefaultStyleKeyProperty.OverrideMetadata(typeof(CalendarDay), new FrameworkPropertyMetadata(typeof(CalendarDay))); IsTabStopProperty.OverrideMetadata(typeof(CalendarDay), new FrameworkPropertyMetadata(false)); } // Date dependency property and property. public static readonly DependencyProperty DateProperty = DependencyProperty.Register("Date", typeof(DateTime), typeof(CalendarDay), new PropertyMetadata(DateChangedCallback)); public DateTime Date { set { SetValue(DateProperty, value); } get { return (DateTime)GetValue(DateProperty); } } // IsToday dependency property and property. static readonly DependencyProperty IsTodayProperty = DependencyProperty.Register("IsToday", typeof(bool), typeof(CalendarDay), new PropertyMetadata(false)); public bool IsToday { set { SetValue(IsTodayProperty, value); } get { return (bool)GetValue(IsTodayProperty); } } // Day read-only dependency property and property. static readonly DependencyPropertyKey DayKey = DependencyProperty.RegisterReadOnly("Day", typeof(string), typeof(CalendarDay), new PropertyMetadata()); public static readonly DependencyProperty DayProperty = DayKey.DependencyProperty; public string Day { protected set { SetValue(DayKey, value); } get { return (string)GetValue(DayProperty); } } // DateChangedCallback method static void DateChangedCallback(DependencyObject obj, DependencyPropertyChangedEventArgs args) { CalendarDay calday = obj as CalendarDay; calday.Day = calday.Date.Day.ToString(); } } }

The class defines three new properties, all backed by dependency properties: Date of type DateTime, IsToday of type bool, and a get-only property named Day of type string. The Day property is entirely for the benefit of the template; whenever the Date property changes, the class converts the Day property of the DateTime object to a string and sets it to its own Day property.

The CalendarDayStyle.xaml file contains the template for the CalendarDay control. A template often begins with a Border element whose properties are set through TemplateBinding extensions to the same-named properties defined by the Control class. If these properties aren't explicitly referenced in the template, they have no effect on the visual appearance of the control!

The Control properties you do not need to reference in the template are those that are inherited through the visual tree. These inherited properties are Foreground and the five font-related properties. Any change to those properties will be automatically inherited by the TextBlock, which displays the Day property defined by the CalendarDay class. The HorizontalContentAlignment and VerticalContentAlignment properties determine how that text is aligned in the cell. The template concludes with a Triggers section, as shown below:

<Trigger Property="IsToday" Value="True"> <Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}" /> <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}" /> </Trigger>

When the IsToday property defined by CalendarDay is true, then the Background and Foreground properties are set to system brushes for a highlighted item. This Triggers section can also contain MultiTrigger elements and EventTrigger elements, and the EventTrigger elements can trigger animations.

Although CalendarDay contains a Border element, there is no visible border. The template for CalendarMonth is defined similarly, and there's no visible border around the entire month either. The border is not visible because Control defines the default value of BorderBrush to be null and the default value of BorderThickness to be zero, just like the Border element itself. You might feel very strongly that each day should be enclosed in a visible box. Why not just set the properties in the template to more sensible values?

<Border BorderThickness="1" BorderBrush="Black" ...

The problem with this approach is that any future programmer working with the control will have to alter the default template to change these values. The template should have a minimum of hardcoded property values. If you're the designer of the control and its default template, you can use the Style element in the resource file to set these properties to better defaults. If you're a consumer of the control, you can set these properties when using the control.

For example, in the TwoCalendars.xaml file, you can change the first calendar like this:

<cc:CalendarMonth BorderThickness="1" BorderBrush="Black" ...

That will put a visible border around the whole calendar. But how do you set the CalendarDay border? The CalendarDay element doesn't even show up in the TwoCalendars.xaml file because CalendarMonth is generating all the CalendarDay objects internally!

The solution is to use a style that targets CalendarDay. Try inserting this in the Resources section of TwoCalendars.xaml:

<Style TargetType="cc:CalendarDay"> <Setter Property="BorderThickness" Value="1" /> <Setter Property="BorderBrush" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" /> <Setter Property="HorizontalContentAlignment" Value="Center" /> <Setter Property="VerticalContentAlignment" Value="Center" /> <Setter Property="FontStyle" Value="Italic" /> </Style>

This style not only sets the border but also the content-alignment properties to center the dates in their cells, and it makes the numbers italic as well.

The default CalendarMonth template is contained in the file CalendarMonthStyle.xaml. It begins with a Border, then a Grid of three vertical cells for the buttons and month name, the days of the week, and the calendar grid itself. The top cell of the Grid is another Grid with five horizontal cells for the buttons and month name.

The CalendarMonth class makes available several properties that the template can use to display textual information, such as MonthName, AbbreviatedMonthName, and a formatted AbbreviatedYearMonth. (CalendarMonth obtains much of this information from DateTimeFormatInfo.) CalendarMonth also stores the names of the days of the week in two arrays available as the DayNames and AbbreviatedDayNames properties. These are a little different from the same-named properties in the DateTimeFormatInfo class because the arrays in DateTimeFormatInfo always begin with Sunday.

The FirstDayInWeek property defined by CalendarMonth is based on the day of the week of the first day in the month and the FirstDayOfWeek property from DateTimeFormatInfo.

The template found in CalendarMonthStyle.xaml contains its own Resources section. This is handy for setting styles within the template, particularly for element types that don't explicitly show up in the markup. For example, notice how the days of the week are displayed. The AbbreviatedDayNames property is assigned to a StatusBar that has a UniformGrid as its ItemsPanel. Internally, the strings in the AbbreviatedDaysNames array become the Content properties of StatusBarItem controls. StatusBarItem does not actually show up in this markup, but a Style can target StatusBarItem controls to center the content and insert some space between the names.

Accessing Named Elements

So far you've seen examples of how a class can define properties that the XAML file references through TemplateBinding extensions and triggers. But sometimes it is more convenient for a class to access a particular element in the template. In this example, the CalendarMonth class needs to generate all the CalendarDay objects for a particular month, and these objects must end up in some kind of panel. It seemed most convenient to assign this panel a name that the code references.

As you'll note, the second UniformGrid in the CalendarMonth template is given a name of PART_Panel. This name is similar to the names defined in the default templates for several WPF controls. The CalendarMonth class definition is preceded by an attribute indicating this name and the type of the element that the code expects it to be identifying:

[TemplatePart(Name = "PART_Panel", Type = typeof(Panel))]

This attribute is for the benefit of visual designers; it is not required. But notice that I've implied that this element in the template can be any derivative of Panel.

In response to a call to ApplyTemplate, this statement in CalendarMonth.cs obtains this panel and stores it as a field:

pnl = Template.FindName("PART_Panel", this) as Panel;

Notice that this is not the normal FindName method. It's the FindName method defined by FrameworkTemplate, from which ControlTemplate (and ItemsPanelTemplate and DataTemplate and HierarchicalDataTemplate) is derived, so it's accessed through the Template property of the CalendarMonth object.

What happens if this pnl object is null? It means the template does not include anything named PART_Panel, or perhaps an element named PART_Panel does not derive from Panel. If a class can't find a named element in its template, it should silently just continue on the best it can.

When associating names and types with helper elements in the template, try to be as general as possible. In this case, MonthCalendar needs an element with a Children property, so Panel seems reasonable. MonthCalendar does not require a certain type of panel, although it's obvious that a UniformGrid works quite well.

Generating Commands

The template for CalendarMonth must contain buttons to navigate forward and backward. How do you write code that responds to these button presses? The most convenient method is for the class to define public static read-only fields or get-only properties of type RoutedCommand. The existing WPF controls are inconsistent in defining these RoutedCommand objects as fields or properties. The ScrollBar class defines them as read-only fields and names them LineDownCommand, LineLeftCommand, and so forth. The Slider class defines them as get-only properties named DecreaseLarge, DecreaseSmall, IncreaseLarge, and IncreaseSmall. I chose the ScrollBar approach and defined my RoutedCommand objects as fields, like this:

public static readonly RoutedCommand NextMonthCommand = new RoutedCommand("NextMonth", typeof(CalendarMonth));

Notice that these are static! The template refers them with fully qualified names assigned to the Command property of the control:

<ToggleButton Command=" CalendarMonth.NextMonthCommand" ...

Not very many controls define Command properties that you can set to an object of type RoutedCommand. The only ones are ButtonBase, MenuItem, and Hyperlink.

The CalendarMonth constructor links these RoutedCommand objects with an execution method by adding the objects to its CommandBindings collection:

CommandBindings.Add(new CommandBinding(NextMonthCommand, NextMonthExecuted));

In addition, you can also specify a can-execute method that allows the code to set a Boolean indicating whether the control is valid. If not, it is automatically disabled.

The NextMonthExecuted method in CalendarMonth.cs looks like this:

void NextMonthExecuted(object sender, ExecutedRoutedEventArgs args) { Date = Date.AddMonths(1); }

One advantage of using RoutedCommand objects is that you can trigger them from code fairly easily by calling the Execute method. You might want to trigger the command from some keyboard input, for example. But in many cases you can accomplish this job without any explicit keyboard handling by associating objects of type InputGesture (an abstract class from which KeyGesture and MouseGesture derive) with the RoutedCommand. CalendarMonth's static constructor adds KeyGesture objects to all four RoutedCommand fields, like this:

NextMonthCommand.InputGestures.Add( new KeyGesture(Key.PageDown));

These gestures could have been added in the original field definition for the RoutedCommand objects, but it's a little clumsy because you need to provide an entire InputGestureCollection in the constructor.

Replacing the Template

The real test of this entire exercise comes when you write an application that attempts to replace the default template and make the calendar look completely different. The NewCalendar program defines a new template for CalendarMonth that displays the days of the month vertically. The program creates 12 of these monthly calendars and displays them side-by-side(see Figure 6).

Figure 6 The NewCalendar Display

Figure 6** The NewCalendar Display **

It is also desirable to enhance the calendar by deriving new classes from CalendarDay, but at first this doesn't seem possible: CalendarMonth is responsible for generating the 28, 29, 30, or 31 instances of CalendarDay. It seems that if you wish to derive from CalendarDay, you must also derive from CalendarMonth to change that logic. To avoid that, I defined yet another property of CalendarMonth called DayType of type Type. By default, this property is equal to typeof(CalendarDay), but it can also be set to the type of a class that derives from CalendarDay.

CalendarDayNotes derives from CalendarDay and expects its template to contain a control of type TextBox with a name of PART_TextBox. Whatever you type into those TextBox controls is saved by CalendarDayNotes in little files identified by a file name derived from the date. The next time you run the program, it loads them all in. CalendarDayNotes has a rather bland default template, but the ReminderCalendar program includes a slightly fancier template that displays the day of the month in an elongated shape and gradiated with color, as shown in Figure 7.

Figure 7 The ReminderCalendar

Figure 7** The ReminderCalendar **

It's not even necessary to derive from CalendarDay to make the days of the month a little fancier. The CalendarControls library also has a class named MoonDisk that derives from Viewport3D and has a DateTime property to orient lighting on a sphere of the moon. The MoonPhaseCalendar doesn't create the speediest calendar in the world—it needs to generate up to 31 Viewport3D objects for each month—but it does show a little more than the average calendar, as Figure 8 demonstrates.

Figure 8 The MoonPhaseCalendar Display

Figure 8** The MoonPhaseCalendar Display **

Send your questions and comments to

Charles Petzold is a Contributing Editor to MSDN Magazine. His most recent book is 3D Programming for Windows (Microsoft Press, 2007).