Introduction
This is an examination of many aspects of writing Styles and Control Templates in code.
The accompanying project contains all the code for recreating various controls, and a few solutions to common problems.
I also list some of the common errors encountered and how to get round the limitations.

Building the Sample
Just download, unblock, unzip, open and run!
Description
A ControlTemplate is like a schematic, showing the relationships of the elements. Each element within has it's own blueprint. These are not actual controls.
A control template is a design, waiting for you to print a hard copy
When you see how the AppendChild method works, you realise difference between HTML and XAML markup formats. HTML places the item in the cell and uses a DOM to map items.
Here is an example of a simple ControlTemplate in XAML.
XAML
Edit|Remove
xaml
<Window ...snip ...>
<Window.Resources>
<ControlTemplate x:Key="LabelAsGroupBox" TargetType="ContentControl">
<GroupBox Header="{TemplateBinding Content}"/>
</ControlTemplate>
</Window.Resources>
<Grid>
<Label Content="Test this" Template="{StaticResource LabelAsGroupBox}"/>
</Grid>
</Window>
<Window ...snip ...>
<Window.Resources>
<ControlTemplate x:Key="LabelAsGroupBox" TargetType="ContentControl">
<GroupBox Header="{TemplateBinding Content}"/>
</ControlTemplate>
</Window.Resources>
<Grid>
<Label Content="Test this" Template="{StaticResource LabelAsGroupBox}"/>
</Grid>
</Window>
Below is a real and very pervasive Style and ControlTemplate:
XAML
Edit|Remove
xaml
<Style x:Key="ButtonFocusVisual">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Rectangle Margin="2" SnapsToDevicePixels="true" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" StrokeThickness="1" StrokeDashArray="1 2"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="ButtonFocusVisual">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Rectangle Margin="2" SnapsToDevicePixels="true" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" StrokeThickness="1" StrokeDashArray="1 2"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Notice it has no TargetType, as it is not changing any specialised properties, or control template properties.
Below is the coded equivalent:
C#
Edit|Remove
csharp
Style MakeButtonFocusVisualStyle()
{
ControlTemplate template = new ControlTemplate(); //Notice no TargetType
var rec = new FrameworkElementFactory(typeof(Rectangle));
rec.SetValue(FrameworkElement.MarginProperty, new Thickness(2));
rec.SetValue(Shape.StrokeProperty, new DynamicResourceExtension(SystemColors.ControlTextBrushKey));
rec.SetValue(Shape.StrokeThicknessProperty, 1.0D);
rec.SetValue(Shape.StrokeDashArrayProperty, new DoubleCollection { 1.0D, 2.0D }); //this makes the focus visual dashes
template.VisualTree = rec;
var style = new Style();
style.Setters.Add(new Setter(Control.TemplateProperty, template));
return style;
}
Style MakeButtonFocusVisualStyle()
{
ControlTemplate template = new ControlTemplate();
var rec = new FrameworkElementFactory(typeof(Rectangle));
rec.SetValue(FrameworkElement.MarginProperty, new Thickness(2));
rec.SetValue(Shape.StrokeProperty, new DynamicResourceExtension(SystemColors.ControlTextBrushKey));
rec.SetValue(Shape.StrokeThicknessProperty, 1.0D);
rec.SetValue(Shape.StrokeDashArrayProperty, new DoubleCollection { 1.0D, 2.0D });
template.VisualTree = rec;
var style = new Style();
style.Setters.Add(new Setter(Control.TemplateProperty, template));
return style;
}
Removing VisualFocusStyle
The standard VisualFocusStyle is defined in the Theme and applied to all controls by default. I only show it in my example to show how you can change it, how to apply it to resources and how to reference it from other controls. Microsoft recommends only
changing this at Theme level, for giving the user a consistent effect to all controls, but if you have a heavily re-styled control, or are just sick of it, you can to strip it off, here are some examples of how to remove it.
C#
Edit|Remove
csharp
//Direct access
myControl.VisualFocusStyle = null;
//As used in templates (and direct)
myControl.SetValue(myControl.SetValue(Control.VisualFocusStyle, null);
myControl.VisualFocusStyle = null;
myControl.SetValue(myControl.SetValue(Control.VisualFocusStyle, null);
XAML
Edit|Remove
xaml
<!-- In a template -->
<Setter Property="VisualFocusStyle" Value="{x:Null}" />
<!-- in the control's markup -->
<Border VisualFocusStyle="{x:Null}" />
<!-- In a template -->
<Setter Property="VisualFocusStyle" Value="{x:Null}" />
<!-- in the control's markup -->
<Border VisualFocusStyle="{x:Null}" />
Style Setters
Styles consist entirely of a bunch of
Setters. These simply set the value of properties on an object. Setters do not only change colours and other basic control properties. Template is also a property of the control, so you can set the ControlTemplate from within a style, as shown above.
FrameworkElementFactory
This template has only one
FrameworkElementFactory (FEF), which is like a blueprint, for a Rectangle. As the FEF is a generic control, it does not itself have the actual properties, so you can't set them directly. We use the FEF's
SetValue method to "line up" the settings, for when the control is actually rendered. Like saying "
This factory produces a Rectangle and when it does, here are it's properties". To build up the
VisualTree of the control, we use
AppendChild to add children factories to parent factories. The VisualTree is different from the
LogicalTree. The VisualTree is interesting to understand because events travel along the VisualTree.
Base or Derived Properties
You will notice throughout this example project that I have taken properties right back to their base properties. For example, in the style above I use FrameworkElement.MarginProperty instead of derived Rectangle.MarginProperty. This makes no difference,
which is interesting to know, as it makes it easier to cut and paste chunks of code when creating more controls, because for example, both a Rectangle and an Ellipse derive from FrameworkElement.
DynamicResourceExtension
The template above uses a
DynamicResourceExtension object, which is a blueprint for the actual
DynamicResource Markup Extension shown within the curly brackets, in the XAML version above. Dynamic resources are references to resources which the compiler defers until run-time. For example,
system colours are user specific, so cannot be hard coded into the application. This kind of object uses the x:Key to find the resource.
StaticResourceExtension
C#
Edit|Remove
csharp
style.Setters.Add(new Setter(Button.FocusVisualStyleProperty, new StaticResourceExtension("ButtonFocusVisual")));
style.Setters.Add(new Setter(Button.FocusVisualStyleProperty, new StaticResourceExtension("ButtonFocusVisual")));
...you would get a top level exception:
'The invocation of the constructor on type 'PEJL_WPF_Examples.MainWindow' that matches the specified binding constraints threw an exception.' Line number '4' and line position '55'.
..with an inner exception of...
'StaticResourceExtension' is not valid for Setter.Value. The only supported MarkupExtension types are DynamicResourceExtension
and BindingBase or derived types.
TemplateBindingExtension
Another common object you will use to build and bind controls is the
TemplateBindingExtension, which creates the markup for binding to properties of the parent (templated) control, in this case a ButtonChrome.
C#
Edit|Remove
csharp
butt.SetValue(Control.BorderBrushProperty, new TemplateBindingExtension(Control.BorderBrushProperty));
butt.SetValue(Control.BorderBrushProperty, new TemplateBindingExtension(Control.BorderBrushProperty));
This is the equivalant to this XAML:
XAML
Edit|Remove
xaml
<Microsoft_Windows_Themes:ButtonChrome ... BorderBrush="{TemplateBinding BorderBrush}" ... />
<Microsoft_Windows_Themes:ButtonChrome ... BorderBrush="{TemplateBinding BorderBrush}" ... />
SetValue Mistakes & Errors
If StrokeProperty in the code above is a static resource, why can't we just change to this?
rec.SetValue(Shape.StrokeProperty, SystemColors.ControlTextBrush); //This is a static reference
This is hard coding the colour, as it is on application start-up, instead of referring and monitoring it during runtime. If you were to change your Windows Theme, or the other system colours through Control Panel, then your application colour would not
change. It is no longer a binding, and so it is not receiving PropertyChanged notifications from changes to Windows SystemColors.
SetValue is like very loose coupling, and a mistake will not show in the designer, or when you build. Only when the application actually creates the control will the error occur.
For example, if I were to change one line above to:
C#
Edit|Remove
csharp
rec.SetValue(Shape.StrokeProperty, 42);
rec.SetValue(Shape.StrokeProperty, 42);
Then you get the following error:
'The invocation of the constructor on type 'PEJL_WPF_Examples.MainWindow' that matches the specified binding constraints threw an exception.' Line number '4' and line position '55'.
A rather useless error, but one of the most common errors, when making control templates.
Whenever you get an error in WPF, if the exception message does not help, look for the
inner exception by clicking "
View Detail..."
'42' is not a valid value for property 'Stroke'.
When dealing with WPF, especially XAML related problems, the inner exception is the most common place to find the actual error.
Most WPF controls have a visual element. Themes are a set of styles that define the default appearance of a control. Themes only apply to the client area within a window. The window itself and many properties within your application are controlled by a
different Win32 theme service.
For example,
SystemColors.WindowTextBrushKey is Win32 (changed through ControlPanel / Appearance and Personalisation / Change Window
Colors & Metrics (Titled: "Window Colour & Appearence").
Microsoft.Windows.Themes are the base set of visuals that define the default look of your controls. You will notice from that link that there are not many. This is because
they are not the control itself, just components used with the controls. So for example, a standard button uses a
ButtonChrome as it's visual. From that link you will see that it does not inherit from Control or ButtonBase, but it does from FrameworkElement, like Control, so it has many similar properties, events and event handlers. The templated parent Button handles
the actual Button events, and the ButtonChrome changes visually, in response to this. In other words, a button is the actual control, but how it looks and changes when interacted with is down to the ButtonChrome, or whatever button style you use.
Event Handers
Another useful method of the FrameworkElementFactory (and directly on UIElements) is the
AddHandler method, for adding
RoutedEvent handlers. This is one aspect of control coding that is more powerful than XAML. The XAML alternative would be to add and consume
Commands instead. The example below shows how adding event handlers in code allows much more flexible authoring.
Tweak 1: DataGrid - DataGridColumnHeaderStyle
Now that we have covered the basic features to control authoring, here are some examples of how control authoring in code can be more powerful than XAML markup.
If you wanted a control template to be usable throughout your application, you could place it in a shared resources file like App.xaml or Generic.xaml.
This works fine until you want to add a specific EventHandler. If the template is defined in Application.Resources, then it's THAT same application level where it expects to find the event handler, not on the page that uses the template. A common way around
this is to use an
EventToCommand, like used in the
EventTrigger in Expression Blend's
Interactivity library. The problem with this is it does not easily pass the EventArgs. There are
hacks for it, and
MVVMLite's implementation has an attribute for it (PassEventArgsToCommand="True").
However if you do not want to add Blend dlls or MVVMLite framework, then coding the template gives you all the flexibility you need.
In the demo project that accompanies this article, there is a
DataGridFactory class, that has a
Make_ColumnHeaderAware_DataGrid method. In this example, I have passed in the IndicatorsViewModel, which the event handlers work on. You could of course pass in anything in, similar to
Dependency Injection, to "allow the players to use the stage", so to speak.
C#
Edit|Remove
csharp
class DataGridFactory
{
IndicatorsViewModel indicatorsViewModel;
public DataGrid Make_ColumnHeaderAware_DataGrid(IndicatorsViewModel ivm)
{
indicatorsViewModel = ivm;
var dg = new DataGrid();
dg.ColumnHeaderStyle = MakeDataGridColumnHeaderStyle();
return dg;
}
...
class DataGridFactory
{
IndicatorsViewModel indicatorsViewModel;
public DataGrid Make_ColumnHeaderAware_DataGrid(IndicatorsViewModel ivm)
{
indicatorsViewModel = ivm;
var dg = new DataGrid();
dg.ColumnHeaderStyle = MakeDataGridColumnHeaderStyle();
return dg;
}
...
In this example, the IndicatorsViewModel is also shared with the page, where it displays which column header the mouse is over. So a MouseOver event in an obscure control template, deep within a DataGrid, has now updated a completely
unrelated parent visual.
Below is a trimmed down version of the MakeDataGridColumnHeaderStyle method in the demo project, which creates a Style and ControlTemplate with custom event handlers.
C#
Edit|Remove
csharp
Style MakeDataGridColumnHeaderStyle()
{
ControlTemplate template = new ControlTemplate{TargetType = typeof(DataGridColumnHeader),};
var grid = new FrameworkElementFactory(typeof(Grid));
var border = new FrameworkElementFactory(typeof(DataGridHeaderBorder));
border.SetValue(DataGridHeaderBorder.BorderBrushProperty, new TemplateBindingExtension(Control.BorderBrushProperty));
...etc
var contentPresenter = new FrameworkElementFactory(typeof(ContentPresenter));
contentPresenter.SetValue(FrameworkElement.HorizontalAlignmentProperty, new TemplateBindingExtension(Control.HorizontalContentAlignmentProperty));
...etc
contentPresenter.AddHandler(UIElement.MouseEnterEvent, new MouseEventHandler(ColumnHeader_MouseEnter));
contentPresenter.AddHandler(UIElement.MouseLeaveEvent, new MouseEventHandler(ColumnHeader_MouseLeave));
border.AppendChild(contentPresenter);
var thumb1 = new FrameworkElementFactory(typeof(Thumb));
...etc
var thumb2 = new FrameworkElementFactory(typeof(Thumb));
...etc
grid.AppendChild(border);
grid.AppendChild(thumb1);
grid.AppendChild(thumb2);
template.VisualTree = grid;
var style = new Style() { TargetType = typeof(DataGridColumnHeader) };
style.Setters.Add(new Setter(Control.TemplateProperty, template));
... etc
return style;
}
Style MakeDataGridColumnHeaderStyle()
{
ControlTemplate template = new ControlTemplate{TargetType = typeof(DataGridColumnHeader),};
var grid = new FrameworkElementFactory(typeof(Grid));
var border = new FrameworkElementFactory(typeof(DataGridHeaderBorder));
border.SetValue(DataGridHeaderBorder.BorderBrushProperty, new TemplateBindingExtension(Control.BorderBrushProperty));
...etc
var contentPresenter = new FrameworkElementFactory(typeof(ContentPresenter));
contentPresenter.SetValue(FrameworkElement.HorizontalAlignmentProperty, new TemplateBindingExtension(Control.HorizontalContentAlignmentProperty));
...etc
contentPresenter.AddHandler(UIElement.MouseEnterEvent, new MouseEventHandler(ColumnHeader_MouseEnter));
contentPresenter.AddHandler(UIElement.MouseLeaveEvent, new MouseEventHandler(ColumnHeader_MouseLeave));
border.AppendChild(contentPresenter);
var thumb1 = new FrameworkElementFactory(typeof(Thumb));
...etc
var thumb2 = new FrameworkElementFactory(typeof(Thumb));
...etc
grid.AppendChild(border);
grid.AppendChild(thumb1);
grid.AppendChild(thumb2);
template.VisualTree = grid;
var style = new Style() { TargetType = typeof(DataGridColumnHeader) };
style.Setters.Add(new Setter(Control.TemplateProperty, template));
... etc
return style;
}
-
First we create the ControlTemplate with TargetType set to the Control type (in this case, it's DataGridColumnHeader).
-
Then we start building the visuals. In this case it is a Border that contains a ContentPresenter (added with border.AppendChild).
-
We also define a couple of EventTrigger handlers with contentPresenter.AddHandler.
-
Next we define a couple of Thumbs, and finally add all three to the grid with grid.AppendChild.
-
The Grid is the root control. This is added as the Template's VisualTree (template.VisualTree = grid).
-
The Template is then used by a new Style (also TargetType=DataGridColumnHeader).
-
The style sets the Template property of whatever control it is applied to.
Tweak 2: Buttons - DataTrigger, AttachedProperty & Animation
To include other aspects of control authoring, the attached project has a
ButtonFactory which creates a standard button. It also creates a "flashy button", having an
AttachedProperty, which is used by a Button's
DataTrigger, to run an
Animation. If it is to be a FlashyButton, the attached property is wired in as follows:
C#
Edit|Remove
csharp
butt.SetBinding(AttachedProperties.IsHighlightedProperty, new Binding { Source=ivm, Path=new PropertyPath("IsConfirmed") });
butt.SetBinding(AttachedProperties.IsHighlightedProperty, new Binding { Source=ivm, Path=new PropertyPath("IsConfirmed") });
This in effect CREATES the attached property on the Button, and binds it to an "IsConfirmed" public property of the button's DataContext, which in this case is the Window itself.
The DataTrigger and it's
Binding are then added to the Button Style with this:
C#
Edit|Remove
csharp
var apBinding = new Binding("(local:AttachedProperties.IsHighlighted)") { RelativeSource = RelativeSource.TemplatedParent };
var apDataTrigger = new DataTrigger { Binding = apBinding, Value = true };
var apBinding = new Binding("(local:AttachedProperties.IsHighlighted)") { RelativeSource = RelativeSource.TemplatedParent };
var apDataTrigger = new DataTrigger { Binding = apBinding, Value = true };
This is the listener for the AttachedProperty, and it is looking for it on the
RelativeSource.TemplatedParent (the actual Button Control where we put the AttachedProperty, not it's visual components). This DataTrigger
will fire when the AttachedProperty changes to
True.
C#
Edit|Remove
csharp
var sb = new Storyboard();
var colAnim = new ColorAnimation(Colors.Transparent, Colors.Red, TimeSpan.FromMilliseconds(250));
colAnim.AutoReverse = true;
colAnim.RepeatBehavior = RepeatBehavior.Forever;
Storyboard.SetTargetProperty(colAnim, new PropertyPath("(Background).(SolidColorBrush.Color)"));
colAnim.EasingFunction = new CubicEase { EasingMode = EasingMode.EaseOut };
sb.Children.Add(colAnim);
var sb = new Storyboard();
var colAnim = new ColorAnimation(Colors.Transparent, Colors.Red, TimeSpan.FromMilliseconds(250));
colAnim.AutoReverse = true;
colAnim.RepeatBehavior = RepeatBehavior.Forever;
Storyboard.SetTargetProperty(colAnim, new PropertyPath("(Background).(SolidColorBrush.Color)"));
colAnim.EasingFunction = new CubicEase { EasingMode = EasingMode.EaseOut };
sb.Children.Add(colAnim);
We then create the
EnterActions and
ExitActions for the DataTrigger, which in this case are
BeginStoryboard and
StopStoryboard
C#
Edit|Remove
csharp
apDataTrigger.EnterActions.Add(new BeginStoryboard { Name = "HighlightedAnimation", Storyboard = sb }); = sb });
apDataTrigger.ExitActions.Add(new StopStoryboard { BeginStoryboardName = "HighlightedAnimation" });
apDataTrigger.EnterActions.Add(new BeginStoryboard { Name = "HighlightedAnimation", Storyboard = sb }); = sb });
apDataTrigger.ExitActions.Add(new StopStoryboard { BeginStoryboardName = "HighlightedAnimation" });
Then add the DataTrigger to the Style
C#
Edit|Remove
csharp
style.Triggers.Add(apDataTrigger);
style.Triggers.Add(apDataTrigger);
But there is one final gotcha!
Despite that all looking perfect, the Storyboard will not be found in your Style, causing the following error:
The solution is to register it's name in the style itself with the
RegisterName method:
C#
Edit|Remove
csharp
style.RegisterName("HighlightedAnimation", bsb);
style.RegisterName("HighlightedAnimation", bsb);
Tweak 3a: CheckBox & RadioButton Bullet Problem
If you spend much time trying to restyle your controls, you will inevitably come across a common issue with CheckBox and RadioButons..
As you build up a Control Template, each visual is the child of another, added with AppendChild. The root element of the CheckBox VisualTree is a BulletDecorator. However, the actual Bullet itself is a property for the bullet visual, that shows as
checked or unchecked. The Checkbox defers all responsibility for the visual representation of it's IsChecked state over to the Bullet. The Bullet property is therefore of type UIElement, as the Bullet is a fully fledged element that does stuff.
Firstly, the Bullet property is a simple CLI object, not a DependancyProperty. So when you try to find it as below, it isn't listed:
"not all of the template functionality is available when you create a template using this class. The recommended way to programmatically create a template is to load XAML from a string or a memory stream using the
Load method of the
XamlReader class."
You can easily code up the BulletChrome FrameworkElementFactory as follows:
But we can't simply attach with AppendChild, as it is not a child, but a property.
So we can't use the standard BulletDecorator. We will need to make a new control that inherits from it, but adds a new DependancyProperty.
C#
Edit|Remove
csharp
public object ActualBullet
{
get { return (object)GetValue(ActualBulletProperty); }
set { SetValue(ActualBulletProperty, value); }
}
public static readonly DependencyProperty ActualBulletProperty =
DependencyProperty.Register("ActualBullet", typeof(object), typeof(BulletDecoratorByCode), new UIPropertyMetadata(null, ActualBulletChanged));
public object ActualBullet
{
get { return (object)GetValue(ActualBulletProperty); }
set { SetValue(ActualBulletProperty, value); }
}
public static readonly DependencyProperty ActualBulletProperty =
DependencyProperty.Register("ActualBullet", typeof(object), typeof(BulletDecoratorByCode), new UIPropertyMetadata(null, ActualBulletChanged));
Now we can pass stuff in and set the Bullet property directly.
However, when we try to pass the Bullet control in with SetValue, we find a fundamental limitation of FrameWorkElementFactory:
As this is all code, the solution is to defer the bullet creation to our new "BulletDecoratorByCode" class.
You can just pass a dummy value if you only need one BulletDecorator, then code the Bullet within.
In this demo, I show several Bullet examples, so I use the ActualBullet DependancyProperty to pass in a simple string value of which Bullet type I want to use.
C#
Edit|Remove
csharp
static void ActualBulletChanged(object obj, DependencyPropertyChangedEventArgs e)
{
var bdec = obj as BulletDecoratorByCode;
if ((string)e.NewValue == "Microsoft.Windows.Themes.BulletChrome")
bdec.Bullet = BulletFactory.MakeBulletChrome(obj);
else
bdec.Bullet = BulletFactory.MakeBulletImage(obj, (string)e.NewValue);
}
static void ActualBulletChanged(object obj, DependencyPropertyChangedEventArgs e)
{
var bdec = obj as BulletDecoratorByCode;
if ((string)e.NewValue == "Microsoft.Windows.Themes.BulletChrome")
bdec.Bullet = BulletFactory.MakeBulletChrome(obj);
else
bdec.Bullet = BulletFactory.MakeBulletImage(obj, (string)e.NewValue);
}
