Dec
16
2006

ControlTemplates in XAML

XAML has this great concept of look-less controls. When we see a normal Win32 control like say a checkbox, we always assume that it will be a square box along with some text written to the left of it. Taking an example from our old friend WordPad, we would always expect a checkbox to look like

Pretty boring, if you would ask a kid (or even a teenager!). Even though it makes the control much more usable (which is a very important thing BTW), it doesn’t look cool. So how does WPF help us here?

For starters, when you define a control, WPF doesn’t force to you to define the look of the control along with the functionality. Here comes the concept of a look-less control; a control whose behaviour is defined but whose look is not defined. For instance, we might define the behavior of the check box (it has a bool or a bool? property called IsChecked, it has a check mark to the left of the content, etc…;) but leave it to the author to define the look for it. So we might have something like

Checked Checkbox

Unchecked Checkbox

How to go about doing this? This is all done by redefining the ControlTemplate for the checkbox. ControlTemplate is basically what it says, it is a Template for the Control and decides how the control will look like. To achieve such a look we define the Style of the Checkbox as

<Style x:Key="{x:Type CheckBox}" TargetType="{x:Type CheckBox}">
    <Setter Property="SnapsToDevicePixels" Value="true"/>
    <Setter Property="OverridesDefaultStyle" Value="true"/>
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="{x:Type CheckBox}">
          <WrapPanel Orientation="Horizontal">
            <Border x:Name="Border"
              CornerRadius="0>
              <Image Name="CheckMark"
                      Height="{Binding ElementName=MyContent, Path=ActualHeight}" Source="happy.gif">
                <Image.BitmapEffect>
                  <OuterGlowBitmapEffect GlowColor="Blue" GlowSize="2Noise=".1Opacity="0.8"/>
                </Image.BitmapEffect>
              </Image>
            </Border>
            <ContentPresenter Margin="4,0,0,0" Name="MyContent"
              VerticalAlignment="Center"
              HorizontalAlignment="Left"
              RecognizesAccessKey="True"/>
          </WrapPanel>
          <ControlTemplate.Triggers>
            <Trigger Property="IsChecked" Value="{x:Null}">
              <Setter TargetName="CheckMark" Property="Source" Value="unsure.gif" />
              <Setter Property="BitmapEffect" TargetName="CheckMark">
                <Setter.Value>
                  <OuterGlowBitmapEffect GlowColor="Black" GlowSize="2" Noise=".1"
                  Opacity="0.8" />
                </Setter.Value>
              </Setter>
            </Trigger>
            <Trigger Property="IsChecked" Value="false">
              <Setter TargetName="CheckMark" Property="Source" Value="sad.gif" />
              <Setter Property="BitmapEffect" TargetName="CheckMark" Value="{x:Null}"/>
            </Trigger>
            <Trigger Property="IsMouseOver" Value="true">
              <Setter Property="BitmapEffect" TargetName="CheckMark">
                <Setter.Value>
                  <OuterGlowBitmapEffect GlowColor="Red" GlowSize="2" Noise=".1"
                  Opacity="0.8" />
                </Setter.Value>
              </Setter>
            </Trigger>
            <Trigger Property="IsPressed" Value="true">
              <Setter Property="BitmapEffect" TargetName="CheckMark">
                <Setter.Value>
                  <OuterGlowBitmapEffect GlowColor="Yellow" GlowSize="2" Noise=".1"
                  Opacity="0.8" />
                </Setter.Value>
              </Setter>
            </Trigger>
            <Trigger Property="IsEnabled" Value="false">
              <Setter TargetName="Border" Property="Background" Value="{StaticResource DisabledBackgroundBrush}" />
              <Setter TargetName="Border" Property="BorderBrush" Value="{StaticResource DisabledBorderBrush}" />
              <Setter Property="Foreground" Value="{StaticResource DisabledForegroundBrush}"/>
            </Trigger>
          </ControlTemplate.Triggers>
        </ControlTemplate>
      </Setter.Value>
    </Setter>
  </Style>

Let us dissect this monster to see how it works :)

The first line

<Style x:Key="{x:Type CheckBox}" TargetType="{x:Type CheckBox}">

means that this is a Style being defined for Type CheckBox in the namespace referred to by x which is http://schemas.microsoft.com/winfx/2006/xaml in my case.

Moving on, the core part of the Style is in defining the ControlTemplarte.

 <ControlTemplate TargetType="{x:Type CheckBox}">

Note that I could have defined the ControlTemplate for the CheckBox using CheckBox.ControlTemplate but have chosen to implement it using a Style as it is quite common to do so.

It is in this ControlTemplate that I define the look of the CheckBox. Here I say that it should be a WrapPanel which should contain a Border which has an Image and a ContentPresenter in it. You can read more about the ContentPresenter in MSDN, it is just a UIElement which can contain and display any other object. The Image has a binding to the Height of the ContentPresenter so that it resizes automatically to the Height of the content, else you might have a case when you have text with FontSize 36 points but you checkmark is just 10 pixels high. That’s it! That’s the definition of our customized CheckBox; could it be any easier?

I added some Triggers to change the Image in the CheckBox depending on its state. I have added an image when IsChecked is null to account for a Tri State CheckBox. I also added some BitmapEffects for a Glow (just to make it more pretty!)

Go through the code, it isn’t very hard to understand or comprehend. Now whenever you wish your CheckBox to use this style, simply use it as

<CheckBox Margin="8Style="myCheckBoxStyle">
    <TextBlock FontSize="20">Happy or Sad?</TextBlock>
</CheckBox>

If you find this article useful, do comment and let me know

kick it on DotNetKicks.com

6 Responses so far

  1. Ankit December 17, 2006 3:40 PM

    Incredibly nice article i have to say and no i am not just saying it i mean it !!!

  2. Aaron January 27, 2007 7:39 AM

    Nice article, but I have to say thats quite a bit of code/markup just to make an image box that changes appearance when you click on it :)

  3. Rohit January 27, 2007 9:28 AM

    Unfortunately that is the part of the game. WPF does not allow (yet!) one to edit specific parts of the ControlTemplate; so even for a minor change one has to redefine the complete ControlTemplate. This might make some sense as when you are mdifying only a part of the ControlTemplate, you might change something that is closely coupled with the rest of the template; that’s my guess why this hasn’t been done.

    On the other hand, things are much easier if these properties are exposed as Styles. We could just extend from the Style and overwrite only those properties that interest us. Though this severely limits us to controlling only the properties that are exposed as Dependency Properties on the control.

  4. Dimitri July 2, 2008 9:16 AM

    Just what I was looking for, thanks!

  5. chinese_zmm August 12, 2009 3:06 AM

    Nice article, and also nice comment

  6. wonder.mice February 12, 2010 4:29 PM

    Nice explanation, nice example – thanks!

Leave a Comment

Name (required)

Email (required)

:) :( :'( 8-) :^) (h) :o |-) :| :p *-) ;) :s :$ (y) (n) more »

Comments

What is 9 + 9 ?
Please leave these two fields as-is:
IMPORTANT! To be able to proceed, you need to solve the following simple math (so we know that you are a human) :-)

2006 (c) Rohit's Blog, Using the ReviewSaurus Theme : Powered by WordPress