Ioannis Panagopoulos blog

Tutorials on HTML5, Javascript, WinRT and .NET

Control Templates, Style Templates and Custom Elements in WPF

by Ioannis Panagopoulos

 

In the WinForms ages, when you needed a TextBox control with some special built-in additional functionality, the only option you had was to create a new control by inheriting from the TextBox control. Moreover, in cases where you needed to change the visual appearance of a control (for example, enlarge the scrollbar of a ListBox) sometimes you had to resort to the plain Win32 API to do your job.

WPF has a different way of dealing with such requirements and this way is related to the following three WPF constructs:

  • Style Templates
  • Control Templates
  • Custom Elements

In this post we will get a glimpse of the three.

First of all, we need to establish the difference between the logical and visual tree of a window. The XAML syntax enforces a hierarchy in the controls. If for example you put a TextBox and a Button Control in a StackPanel in a Window, then apparently you create the hierarchy below, which is also referred as the logical tree.

 

The parent-child relation in the tree should be interpreted as a loose aggregation in code. For example, in the Figure above, the stack panel "has a" list of Controls such as Button and TextBox.

The logical tree is a behavioral abstraction of the actual hierarchical tree formed for the Controls in a Window. And the term "behavioral" means that a programmer needs the logical tree in order to know which events he/she needs to catch in the code behind file. This implies that in WPF the Controls themselves as we know them carry only a specific behavior definition and not their actual appearance characteristics. For example, a button is a clickable content of some sort not a clickable gray box with a string text. Actually, for each control, there is a whole bunch of classes also belonging to the hierarchy that have to do with the appearance of the control in the window. If those additional "visualization" classes are included in the logical tree we will end up with the visual tree of the windows and its controls.

So, for this small introduction, let us forget about the default appearance of a button and try to create our own. The WPF mechanisms allow us to do such thing by specifying a Control Template for our Button Control. 

Go ahead and create a test WPF Project and in the default Window place a button. Go above the Grid markup and place the declaration of a new control template for the button:

<Window.Resources>

<ControlTemplate x:Key="ButtonDemoTemplate" TargetType="{x:Type Button}">

</ControlTemplate>

</Window.Resources>


Then go to the button's XAML declaration and define the template you want your button to use:

<Button Height="23" Name="button1" Width="75" Template="{StaticResource ButtonDemoTemplate}">Button</Button>


You will notice that the button has disappeared. That is because you have redefined the visual appearance of a button to nothing! Now place the following code inside the template:

<ControlTemplate x:Key="ButtonDemoTemplate" TargetType="{x:Type Button}">

<Canvas>

<Ellipse Canvas.Top="10" Canvas.Left="10" Width="50" Height="50" Fill="Blue" Stroke="Black" StrokeThickness="4" />

<Label Canvas.Top="23" Canvas.Left="10" FontSize="10" Foreground="Yellow" Content="Click Me!"/>

</Canvas>

</ControlTemplate>


Now the button has been dressed with a new visual style (the click me message is important otherwise nobody will realize that this is a button ;)):

This button now works as expected supporting the Click event and all the others. We have actually altered the visual appearance of the button and as a result the visual tree hierarchy is now as follows:

But note that the Content property of the button that is set to the string "Button" is not displayed. In order to display the value of the Content property, we need to define in the Template, a "Placeholder" for it. This placeholder is actually the definition of the ContentPresenter class in our template (we substitute here the label control with a Placeholder class):

<Window.Resources>

<ControlTemplate x:Key="ButtonDemoTemplate" TargetType="{x:Type Button}">

<Canvas>

<Ellipse Canvas.Top="10" Canvas.Left="10" Width="50" Height="50" Fill="Blue" Stroke="Black" StrokeThickness="4" />

<ContentPresenter Canvas.Top="27" Canvas.Left="15"/>

</Canvas>

</ControlTemplate>

</Window.Resources>


Now the Content property of the button can be set and displayed:

<Button Height="23" Name="button1" Width="75" Template="{StaticResource ButtonDemoTemplate}" Content="Click Me!" Foreground="Yellow" FontSize="9" />


Note also that if you need to set a property of your template, to a value of a property of the control tha uses it, you can establish a TemplateBinding. For example to change the Fill color of the Ellipse based on the value of the Background property of the Button Control you do the following:

<Ellipse Canvas.Top="10" Canvas.Left="10" Width="50" Height="50" Fill="{TemplateBinding Background}" Stroke="Black" StrokeThickness="4" />

And now you can set the Background property to change the color of the control:

<Button Height="23" Name="button1" Width="75" Template="{StaticResource ButtonDemoTemplate}" Background="Black" Content="Click Me!" Foreground="Yellow" FontSize="9" />

This type of binding is especially useful in more "important" properties such as the Padding property of a control, which in normal circumstances will be ignored if not bound to some properties of the Control Template. Even in our example, you could establish a TemplateBinding between the Content property of the control and the Text property of the Label and have similar functionality to the one using the ContentPresnter.

As you would have noticed by now, although the button you have just created is clickable, there is no visual indication of that to the user (eg change of color when the mouse is over the button etc). For this kind of behavior we need triggers. Triggers will not be covered here. I will give you just an example of a trigger that will change the fill color of the Ellipse when the mouse is over the button:


<ControlTemplate x:Key="ButtonDemoTemplate" TargetType="{x:Type Button}">

<Canvas>

<Ellipse x:Name="DemoEllipse" Canvas.Top="10" Canvas.Left="10" Width="50" Height="50" Fill="{TemplateBinding Background}" Stroke="Black" StrokeThickness="4" />

<ContentPresenter Canvas.Top="27" Canvas.Left="15"/>

</Canvas>

<ControlTemplate.Triggers>

<Trigger Property="IsMouseOver" Value="True">

<Setter TargetName="DemoEllipse" Property="Fill" Value="Yellow"/>

</Trigger>

</ControlTemplate.Triggers>

</ControlTemplate>


And this is the basic stuff to know for Control Templates in WPF. In few words, we use Control Templates to alter completely the visual appearance of a control (the visual tree of the control) and allow the programmer to set some properties of the visual appearance through TemplateBindings.

The difference with styles is that styles are only capable of setting automatically the properties that are allowed to be set programmatically by the Control and the Control Template.

To extend the previous example, suppose that we want a Style for our button that has the font size of the content to 12, has a red background and when the mouse is over the content changes to "Oh No!". Then, in the Window.Resources section of the window, we define the following style:


<Style x:Key="ButtonStyle">

<Style.Setters>

<Setter Property="Button.Background" Value="Red"/>

<Setter Property="Button.FontSize" Value="12"/>

</Style.Setters>

<Style.Triggers>

<Trigger Property="Button.IsMouseOver" Value="true">

<Setter Property="Button.Content" Value="Oh No!"/>

</Trigger>

</Style.Triggers>

</Style>


Note that there are two areas of interest nested in the style tags. The style Setters that set values of properties and the style triggers that change the properties based on conditions. Note that the properties to be set in this example are declared by including along with their name, the control they apply to, which means that you can use the same style and apply it to various controls (something like applying themes). You use the style as follows:

<Button Style="{StaticResource ButtonStyle}" Height="23" Name="button1" Width="75" Template="{StaticResource ButtonDemoTemplate}" Foreground="Black" />

If you define the same property in a style and also in the XAML declaration of the control the second overrides the first.

You can also use styles to set the same event handler for a number of controls. So for one more time, you use control Templates to modify the visual elements that create a control and styles to alter their properties on demand. If the control was an HTML page, the Control Template is the HTML Template and the Style Template is the css stylesheet that accompanies it.

So, when do we create custom elements? Well with the Style Template you can alter visual appearance to its detail, with Control Templates you can alter visual appearance to its core, and with a custom element you get the bonus of being able to alter functionality (behavior).

In our example:

  • We needed a button with and circular appearance and we created a new Control Template
  • We needed to define a family of such buttons that will have a Red background (and the other stuff described) and we have created a Style Template.
  • Now we need the button to display a message whenever it is clicked (and this functionality appears in a whole bunch of buttons in our application to justify the reason we implement a custom element for it).

We create a new class that inherits the Button and override the Click event as follows (we also add a boolean to allow the developper stop this behavior if he/she likes):

     public class MyButton:Button
    {
        private bool _showMessage=false;
        public bool ShowMessage
        {
            get { return _showMessage; }
            set { _showMessage = value; }
        }
       
        protected override void OnClick()
        {
            base.OnClick();
            if (ShowMessage) MessageBox.Show("I am always shown!");
        }
     }

Now alter the XAML for the button as follows:

<local:MyButton ShowMessage="True" Style="{StaticResource ButtonStyle}" ...

Of course you need to declare the "local" namespace in the Window tag as follows:

xmlns:local="clr-namespace:YourProjectNamespace"...

Implementing the custom element offered you this added functionality "out of the box" by controlling the ShowMessage boolean. Since the behavior is after the base.OnClick() method, it will be executed after any user-defined logic residing in a user-defined event handler of the Click event.

This concludes a very brief comparison of the three ways of altering a control in WPF.  For once more thank you for taking the time to read this.

kick it on DotNetKicks.com
blog comments powered by Disqus
hire me