Control Templates
Although property-based styling is very powerful, it may not let you
change the look of the controls in dramatic ways. That is where control
templates come in. Every control has XAML that defines how a control is
drawn. For example, the humble button uses this XAML to draw itself:
<Grid Background="Transparent">
...
<Border x:Name="ButtonBackground"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}"
CornerRadius="0"
Margin="{StaticResource PhoneTouchTargetOverhang}">
<ContentControl x:Name="ContentContainer"
ContentTemplate="{TemplateBinding
ContentTemplate}"
Content="{TemplateBinding Content}"
Foreground="{TemplateBinding Foreground}"
HorizontalAlignment="{TemplateBinding
HorizontalContentAlignment}"
Padding="{TemplateBinding Padding}"
VerticalAlignment="{TemplateBinding
VerticalContentAlignment}" />
</Border>
</Grid>
Even though you think of controls as atomic elements, there is XAML
inside the control to define the look and feel of the control. Control
templates are used to redefine this XAML for any control.
Control templates are part of the style that is applied to a control. The ControlTemplate
is the value of the Template
property of any control. For example:
<Style x:Key="ButtonStyle1"
TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid Background="Transparent">
...
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The contents of the ControlTemplate
contain the XAML that the particular control should use (i.e., Button
in this case). As you define the XAML that makes up the look of a particular control, you can use a markup extension called a template binding to pull the value of a property into your XAML. For example:
<ControlTemplate TargetType="Button">
<Grid Background="Transparent">
<Border x:Name="ButtonBackground"
BorderBrush="{TemplateBinding BorderBrush}"
Background="{TemplateBinding Background}">
...
</Border>
</Grid>
</ControlTemplate>
By using template bindings, the natural value of the property will be
used as the value inside the control template. This value could come
from the default value in the control or from a style setter, or it
could specifically be set on an instance of the control. Template
bindings allow you to use whatever property value is supposed to be
shown in the control.
For some controls, the XAML must have certain elements to make sure the control still works. For example, the WebBrowser
control requires that part of the XAML be a Border
element named PresentationContainer
. That way, the control knows where to show the Web content. This contract between you and the control author is called a template part
(many controls do not have any template parts). The template parts that
are required are documented as attributes on the controls themselves
(as shown in Figure 6).
Figure 6. TemplatePart
attribute
The structure of the XAML in a control template represents the look
of the control, but in addition you can specify the feel of an
application. The feel of an application is the way the control can
interact with the users. For example, the Button
class
changes its appearance when pressed to give feedback to the user that
she has correctly pressed the button. This is how the feel of an
application works.
You can create the feel of an application using a structure called the visual state manager.
With the visual state manager you can define animations that represent a
state the control can be in. The control uses the visual state manager
to go into specific states as the user interacts with it. As the
application designer, you can define these states to change the way the
control interacts with the user. These states are broken up into groups
so that a single control can be in more than one state. For example, two
of the visual state manager groups that the TextBox
has are CommonStates
(which represent states such as Disabled
and ReadOnly
) and FocusStates
(which represent whether the control has focus or not). The groups
define a set of states where only one state can be active at a time. For
example, you can have your control be Disabled
and Focused
, but not Disabled
and ReadOnly
. The states in a group are mutually exclusive.
To define the groups and states for the visual state manager, the XAML can contain a VisualStateManager.VisualStateGroups
property (as an attached property):
<ControlTemplate TargetType="Button">
<Grid Background="Transparent">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="MouseOver" />
<VisualState x:Name="Pressed">
<Storyboard>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetProperty="Foreground"
Storyboard.TargetName="ContentContainer">
<DiscreteObjectKeyFrame KeyTime="0"
Value="{StaticResource
PhoneBackgroundBrush}" />
</ObjectAnimationUsingKeyFrames>
...
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled">
<Storyboard>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetProperty="Foreground"
Storyboard.TargetName="ContentContainer">
<DiscreteObjectKeyFrame KeyTime="0"
Value="{StaticResource
PhoneDisabledBrush}" />
</ObjectAnimationUsingKeyFrames>
...
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
...
</Grid>
</ControlTemplate>
As this example shows, the VisualStateManager
.VisualStateGroups
attached property contains one (or more) VisualStateGroup
objects. Inside the group is a list of VisualState
objects that represent a storyboard that shows how to go to a specific state. The empty VisualState
objects mean the state should look exactly like the natural state of the object.
The visual states and groups that a control supports are also specified as attributes on the control classes, as shown in Figure 7.
Figure 7. TemplateVisualState
attribute
When creating your own control templates you
will need to be aware of the template parts and template visual states,
as that is the contract between you and the control author. You must
implement these states and parts if you expect the controls to continue
to operate correctly.