WPF: Control Templating


One of the truly best things in WPF is the ability to change the look of any control, and still keeping its original functionality. In my previous blog post, I talked about how create Data Templates. This post, we are going to explore the world of Control Templating. Most WPF controls derive from Control class, and there’s a property called Template. This property allows us to set a whole new template and that will override the default look of the control. Typically we use a ControlTemplate to do this. In this post, we will look at how to re-template a Slider.

SliderTemplate

When writing a Control Template, you can do it within the WPF control you want to template, like so,

<Slider>
 <Slider.Template>
 <ControlTemplate>
   <!-- insert your new control template here -->
 </ControlTemplate>
 </Slider.Template>
</Slider>

Or I rather put the template in a style and that in a ResourceDictionary where we can then reference and reuse. Before that, how you define a Control Template is no different from how you create normal stuff using just plain old controls from the toolbox. For example, a button’s default template might be decomposed into various simple components, like a Border, a Rectangle, ContentPresenter, etc. Of course this might result in different layers of controls with varying colors and gradients to create a stylish look, and redefining those property values using triggers to react to events like IsMouseOver and IsPressed to create effects of being moused over and pressed.

In this example, a Sliders basic structure is made up of a Track, a Thumb, RepeatButtons and a TickBar. To recreate a new template for the Slider, you must have a Track and a Thumb as a bare minimum to mimic the slider’s behaviour, and anything else you want to do is up to your imagination.

To keep it short, my slider template consists of a rounded Border and a Track, and they are set within the same Grid row and column. I also added Control Template Triggers to reconfigure my layout when the Slider’s orientation is set to Vertical. I also created a ControlTemplate for round buttons, to simulate the ‘-‘ and ‘+’ for the slider. A very convenient way to increase/decrease the slider using the RepeatButtons (if you have them) and my round  +/- buttons is via the Commands provided by the Slider class, namely Slider.IncreaseLarge, Slider.DecreaseLarge, Slider.IncreaseSmall and Slider.IncreaseLarge.

I created a couple of Brushes resource and Styles for my Thumb, RepeatButton and the Slider itself.

This is the control template for the Slider, within a style.

<Style TargetType="Slider">
 <Setter Property="OverridesDefaultStyle" Value="true"/>
 <Setter Property="Template">
 <Setter.Value>
   <ControlTemplate TargetType="Slider">
     <Grid>
       <Grid.RowDefinitions>
         <RowDefinition Height="auto" />
         <RowDefinition Height="auto" Name="row" />
         <RowDefinition Height="auto" />
       </Grid.RowDefinitions>
       <Grid.ColumnDefinitions>
         <ColumnDefinition Width="auto" />
         <ColumnDefinition Width="*" Name="column" />
         <ColumnDefinition Width="auto" />
       </Grid.ColumnDefinitions>

       <RepeatButton Name="PART_DecreaseRepeatButton"
                     Grid.Row="1" Grid.Column="0"
                     Style="{StaticResource RoundButtonStyle}"
                     Content="-" Command="Slider.DecreaseSmall" />
       <RepeatButton Name="PART_IncreaseRepeatButton"
                     Grid.Row="1" Grid.Column="2"
                     Style="{StaticResource RoundButtonStyle}"
                     Content="+" Command="Slider.IncreaseSmall" />
       <Border Name="PART_Border"
               BorderBrush="Black" BorderThickness="1"
               Padding="2"
               CornerRadius="5"
               Grid.Row="1" Grid.Column="1"
               Width="{TemplateBinding Width}"
               Height="{TemplateBinding Height}"
               Background="{StaticResource HorizontalBrush}"
               HorizontalAlignment="Stretch"
               VerticalAlignment="Center" />

       <Track Name="PART_Track"
              HorizontalAlignment="Stretch"
              VerticalAlignment="Center"
              Grid.Row="1" Grid.Column="1"
              Width="{TemplateBinding Width}"
              Height="{TemplateBinding Height}">
        <Track.DecreaseRepeatButton>
          <RepeatButton Command="Slider.DecreaseLarge"
                        Style="{StaticResource SliderButtonStyle}" />
        </Track.DecreaseRepeatButton>
        <Track.Thumb>
          <Thumb Style="{StaticResource SliderThumbStyle}" />
        </Track.Thumb>
        <Track.IncreaseRepeatButton>
          <RepeatButton Command="Slider.IncreaseLarge"
                        Style="{StaticResource SliderButtonStyle}" />
        </Track.IncreaseRepeatButton>
      </Track>
    </Grid>
    <ControlTemplate.Triggers>
      <Trigger Property="Orientation" Value="Vertical">
        <Setter TargetName="PART_Border" Property="HorizontalAlignment" Value="Center" />
        <Setter TargetName="PART_Border" Property="VerticalAlignment" Value="Stretch" />
        <Setter TargetName="PART_Border" Property="Background" Value="{StaticResource VerticalBrush}" />
        <Setter TargetName="PART_Track" Property="HorizontalAlignment" Value="Center" />
        <Setter TargetName="PART_Track" Property="VerticalAlignment" Value="Stretch" />
        <Setter TargetName="row" Property="Height" Value="*" />
        <Setter TargetName="column" Property="Width" Value="auto" />
        <Setter TargetName="PART_DecreaseRepeatButton" Property="Grid.Row" Value="2" />
        <Setter TargetName="PART_DecreaseRepeatButton" Property="Grid.Column" Value="1" />
        <Setter TargetName="PART_IncreaseRepeatButton" Property="Grid.Row" Value="0" />
        <Setter TargetName="PART_IncreaseRepeatButton" Property="Grid.Column" Value="1" />
      </Trigger>
   </ControlTemplate.Triggers>
 </ControlTemplate>
 </Setter.Value>
 </Setter>
</Style>

It might be quite daunting to write or look at so much Xaml, but I strongly believe that in order to become good at WPF, one must be comfortable writing most Xaml by hand. In cases where you require some complex animations or would like to visualize the effects of styling with gradients and such, it would make sense to use Expression Blend to churn out the Xaml. Anyways if you break up the Xaml above, the template consists of a Border and a Track. The border provides the background for the track. The track consist of an IncreaseRepeatButton and DecreaseRepeatButton which are styled to have a transparent background and a thumb which is styled into a circle. Do download the code sample to have a look at all the styles used in this template.

We also specifed a Trigger for the ControlTemplate to re-layout the slider when the Slider’s orientation is set to vertical. You can set Names for your controls within your template, and that will allow you to redefine their properties in the trigger, by using the TargetName property. Also you can set a control’s property within the template to be binded to the property of the actual control you are templating, by specifying  like so Width={TemplateBinding Width}. Last of all, when defining a style, you must set a TargetType and a x:Key optional. When you don’t specify a key, that style will automatically be applied to all control types you specified in your TargetType. If you specified a x:Key, the style will only be active when your control references that style like so Style={StaticResource SomeStyle}.

Besides just re-templating WPF controls in the toolbox, another use of control templating is when you create custom controls (not user controls). The difference is that you create a class that inherits from a control in the WPF library,  for example inheriting from a ItemsControl, or a Panel, a Control, etc. When you create a custom control, it is look-less at that point so you will need to define a style with your control template to give it a default look. The WPF control inheritance hierarchy in WPF is pretty deep, therefore choosing the right base class to inherit requires careful consideration and understanding.

Hope you liked this post and happy coding.

Download code sample here.

<Setter Property=”OverridesDefaultStyle” Value=”true”/>
<Setter Property=”Template”>
<Setter.Value>
<ControlTemplate TargetType=”Slider”>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height=”auto” />
<RowDefinition Height=”auto” Name=”row” />
<RowDefinition Height=”auto” />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width=”auto” />
<ColumnDefinition Width=”*” Name=”column” />
<ColumnDefinition Width=”auto” />
</Grid.ColumnDefinitions><RepeatButton Name=”PART_DecreaseRepeatButton”
Grid.Row=”1″ Grid.Column=”0″
Style=”{StaticResource RoundButtonStyle}”
Content=”-” Command=”Slider.DecreaseSmall” /><RepeatButton Name=”PART_IncreaseRepeatButton”
Grid.Row=”1″ Grid.Column=”2″
Style=”{StaticResource RoundButtonStyle}”
Content=”+” Command=”Slider.IncreaseSmall” /><Border Name=”PART_Border”
BorderBrush=”Black” BorderThickness=”1″
Padding=”2″
CornerRadius=”5″
Grid.Row=”1″ Grid.Column=”1″
Width=”{TemplateBinding Width}”
Height=”{TemplateBinding Height}”
Background=”{StaticResource HorizontalBrush}”
HorizontalAlignment=”Stretch”
VerticalAlignment=”Center” />

<Track Name=”PART_Track”
HorizontalAlignment=”Stretch”
VerticalAlignment=”Center”
Grid.Row=”1″ Grid.Column=”1″
Width=”{TemplateBinding Width}”
Height=”{TemplateBinding Height}”>
<Track.DecreaseRepeatButton>
<RepeatButton Command=”Slider.DecreaseLarge”
Style=”{StaticResource SliderButtonStyle}” />
</Track.DecreaseRepeatButton>
<Track.Thumb>
<Thumb Style=”{StaticResource SliderThumbStyle}” />
</Track.Thumb>
<Track.IncreaseRepeatButton>
<RepeatButton Command=”Slider.IncreaseLarge”
Style=”{StaticResource SliderButtonStyle}” />
</Track.IncreaseRepeatButton>
</Track>
</Grid>

<ControlTemplate.Triggers>
<Trigger Property=”Orientation” Value=”Vertical”>
<Setter TargetName=”PART_Border” Property=”HorizontalAlignment” Value=”Center” />
<Setter TargetName=”PART_Border” Property=”VerticalAlignment” Value=”Stretch” />
<Setter TargetName=”PART_Border” Property=”Background” Value=”{StaticResource VerticalBrush}” />
<Setter TargetName=”PART_Track” Property=”HorizontalAlignment” Value=”Center” />
<Setter TargetName=”PART_Track” Property=”VerticalAlignment” Value=”Stretch” />
<Setter TargetName=”row” Property=”Height” Value=”*” />
<Setter TargetName=”column” Property=”Width” Value=”auto” />
<Setter TargetName=”PART_DecreaseRepeatButton” Property=”Grid.Row” Value=”2″ />
<Setter TargetName=”PART_DecreaseRepeatButton” Property=”Grid.Column” Value=”1″ />
<Setter TargetName=”PART_IncreaseRepeatButton” Property=”Grid.Row” Value=”0″ />
<Setter TargetName=”PART_IncreaseRepeatButton” Property=”Grid.Column” Value=”1″ />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

Share this post :
Advertisements
Posted in WPF. 2 Comments »

2 Responses to “WPF: Control Templating”

  1. agiletalk's me2DAY Says:

    작은아이의 생각…

    WPF: Control Templating (via. Code Blitz)…

  2. Ian Says:

    Really useful thanks!


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: