Introduction to Silverlight > Using Templates > Control Templates |
Data Templates allow you to specify how to convert arbitrary data objects into UIElement objects that can be displayed to the user. But that's not the only use of templates in Silverlight and WPF. You can also use templates to modify the visual structure of existing UIElement objects such as controls.
Most controls have their visual appearance defined by a native XAML resource (typically contained within the assembly that defines the control). This resource specifies a Style which assigns values to most of the control's properties, including its Template property (which defines the control's internal "visual tree").
For example:
XAML |
Copy Code
|
---|---|
<Style TargetType="HyperlinkButton"> <Setter Property="IsEnabled" Value="true" /> <Setter Property="IsTabStop" Value="true" /> <Setter Property="Foreground" Value="#FF417DA5" /> <Setter Property="Cursor" Value="Hand" /> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="HyperlinkButton"> <Grid x:Name="RootElement" Cursor="{TemplateBinding Cursor}"> <!-- Focus indicator --> <Rectangle x:Name="FocusVisualElement" StrokeDashCap="Round" ...=""/> <!-- HyperlinkButton content --> <ContentPresenter x:Name="Normal" Background="{TemplateBinding Background}" Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}"...=""/> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style> |
This is a very simplified version of the XAML resource used to specify the HyperlinkButton control. It consists of a Style that begins by setting the default value of several simple properties, and then assigns a value of type ControlTemplate to the control's Template property.
The ControlTemplate in this case consists of a Grid (RootElement) that contains a Rectangle (FocusVisualElement) used to indicate the focused state and a ContentPresenter (Normal) that represents the content portion of the control (and itself contains another ContentTemplate property).
Note the TemplateBinding attributes in the XAML. These constructs are used to map properties exposed by the control to properties of the template elements. For example, the Background property of the hyperlink control is mapped to the Background property of the Normal element specified in the template.
Specifying controls this way has some advantages. The complete visual appearance is defined in XAML and can be modified by a professional designer using Expression Blend, without touching the code behind it. In practice, this is not as easy as it sounds, because there are logical relationships between the template and the control implementation.
Recognizing this problem, Silverlight introduced a TemplatePart attribute that allows control classes to specify the names and types it expects its templates to contain. In the future, this attribute will be added to WPF as well, and used by designer applications such as Blend to validate templates and ensure they are valid for the target control.
For example, the Microsoft Button control contains the following TemplatePart attributes:
XAML |
Copy Code
|
---|---|
/// <summary> /// Represents a button control, which reacts to the Click event. /// </summary> [TemplatePart(Name = Button.ElementRootName, Type = typeof(FrameworkElement))] [TemplatePart(Name = Button.ElementFocusVisualName, Type = typeof(UIElement))] [TemplatePart(Name = Button.StateNormalName, Type = typeof(Storyboard))] [TemplatePart(Name = Button.StateMouseOverName, Type = typeof(Storyboard))] [TemplatePart(Name = Button.StatePressedName, Type = typeof(Storyboard))] [TemplatePart(Name = Button.StateDisabledName, Type = typeof(Storyboard))] public partial class Button : ButtonBase |
These six template parts constitute a contract between the control implementation and the design specification. They tell the designer that the control implementation expects to find certain elements in the template (defined by their name and type).
Well-behaved controls should degrade gracefully, not crashing if some non-essential elements are missing from the template. For example, if the control can't find a Storyboard named Button.StateMouseOverName in the template, it should not do anything when the mouse hovers over it.
Well-implemented templates should fulfill the contract and provide all the elements that the control logic supports. Designer applications such as Blend can enforce the contract and warn designers if they try to apply invalid templates to controls.
For the time being, the easiest way to create new templates for existing controls is to start with the original XAML and customize it.
We will not show any actual examples of how to create and use custom control templates here. Instead, we suggest you download the examples developed by Corrina Barber:
http://blogs.msdn.com/corrinab/archive/2008/03/11/silverlight-2-control-skins.aspx
The link contains previews and downloads for three 'skins' (bubbly, red, and flat). Each skin consists of a set of Style specifications, similar to the one shown above, which are added to the application's global XAML file (App.xaml). The format is similar to this:
XAML |
Copy Code
|
---|---|
<Application xmlns="http://schemas.microsoft.com/client/2007" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="Styles_Red.App" <Application.Resources> <!-- Button --> <Style x:Key="buttonStyle" TargetType="Button"> <Setter Property="IsEnabled" Value="true" /> <Setter Property="IsTabStop" Value="true" /> <Setter Property="Foreground" Value="#FF1E2B33" /> <Setter Property="Cursor" Value="Hand" /> <Setter Property="TextAlignment" Value="Center" /> <!-- A lot more XAML follows… --> |
Once these styles are defined in the App.xaml file, they can be assigned to any controls in the application:
<Button Content="Button" Style="{StaticResource buttonStyle}"/>
If you are curious, this is what the Button control looks like after applying each of the skins defined in the reference above:
Default |
Bubbly |
Red |
Flat |
This mechanism is extremely powerful. You can change what the controls look like and even the parts used internally to build them.
Unlike data templates, however, control templates are not simple to create and modify. Creating or changing a control template requires not only design talent but also some understanding of how the control works.
It is also a labor-intensive proposition. In addition to their normal appearance, most controls have Storyboards that are applied to change their appearance when the mouse hovers over them, when they gain focus, get pressed, get disabled, and so on (see the C1ComboBox example above).
Furthermore, all controls in an application should appear consistent. You probably wouldn't want to mix bubbly buttons with regular scrollbars on the same page for example. So each 'skin' will contain styles for many controls.
Some controls are designed with custom templates in mind. For example, the C1ComboBox has an ItemsPanel property of type ItemsPanelTemplate. You can use this property to replace the default drop-down ListBox element with any other UIElement you like.
For examples of using the ItemsPanel property, check the ControlExplorer sample installed by default with Silverlight Edition.