Images in this post missing? We recently lost them in a site migration. We're working to restore these as you read this. Should you need an image in an emergency, please contact us at imagehelp@codebetter.com
Contrasting Control Templates & Styles in WPF

We've had a few projects that involved custom styling for many of the standard controls. At first, I found myself swimming in a sea of XAML and rather double-minded about where to put things. Now, an approach has begun to coalesce for me.

When it comes to customizing the look of controls, there are two concerns: styles and control templates. Knowing when to use one or the other can be confusing, as there is really a lot of overlap in what they each do.

Styles

Styles are frequently compared to cascading styles sheets (css) in the html world. WPF styles are like css in that they can be used to globally define the looks of certain elements, they can be paired to specific element by way of a key, or they can even be declared inline directly on an element.

I've come to view styles as a way to set default values on the public properties of WPF elements. (Really, it's dependency properties, but most public properties on WPF controls are dependency properties anyway .) When you associate a style with an element you are essentially saying "here's the set of properties and corresponding values that I'd like to set." This distinction is important for two reasons:

  • Styles can only affect the public dependency properties.
  • Styles can affect non-visual properties.

This concept of setting properties is inherent in the syntax of styles:

<Style>
    <Setter Property="X" Value="Y" />
</Style>

This also means that styles are limited in what they can do by the public API of the element that they are applied to.

Control Templates

Control templates are used for manipulating the composition of a control.  They are a means of modifying the structure of a control.

First, you need to recognize that every control in WPF is composed out of other WPF elements. If you were to examine a Button control, you would discovered that internally it's composed of something like this:

<ms:ClassicBorderDecorator x:Name="ContentContainer"
                           SnapsToDevicePixels="True"
                           Background="{TemplateBinding Background}"
                           BorderBrush="{TemplateBinding BorderBrush}"
                           BorderStyle="Raised"
                           BorderThickness="3,3,3,3">
    <ContentPresenter />
</ms:ClassicBorderDecorator>

ClassicBorderDecorator is an element Microsoft built for emulating the look of the existing Windows UI (that's pre-WPF elements). It's very much like a Border, and tucked away in the theme-specific assembly PresentationFramework.Classic.dll. (Ok, maybe that's a lame example).

There are certain internal elements that the control looks for in its template.  In the case of Button, it look for a ContentPresenter, though it might be something else depending on the control.

button exampleNow, let's say you want a button that has rounded corners, with a black background, and gray outline.  You can use a style to set the background and outline using the Background and BorderBrush properties, and you might hope for a CornerRadius property similar to that of a Border, but there isn't one. In short, you can't make a button have rounded corners using a style.

I'm sure someone out there is thinking that you can use a style and set Background to a DrawingBrush that looks like that, and well, yes, you could, but um, that's a different matter and I need a simple example at the moment.

We could achieve this with the following control template however:

<Button>
    <Button.Template>
        <ControlTemplate TargetType="Button">
            <Border CornerRadius="4" 
                    Background="Black"
                    BorderBrush="Gray"
                    BorderThickness="1"
                    Padding="10 6">
                <TextBlock Foreground="White"
                           Text="Click Me"/>
            </Border>
        </ControlTemplate>
    </Button.Template>
</Button>

You are afforded much more liberty with control templates than you are with styles, but the control templates are also trickier to use. In the template above, we completely bypassed the Content property on the button, and we manually created the "content" in the template.  That makes the template not so reusable and the above example is not a good template. Here's one that's a bit more generic:

<Button Content="Click Me">
    <Button.Template>
        <ControlTemplate TargetType="Button">
            <Border CornerRadius="4" 
                    Background="Black"
                    BorderBrush="Gray"
                    BorderThickness="1"
                    Padding="10 6">
                <ContentPresenter TextBlock.Foreground="White" />
            </Border>
        </ControlTemplate>
    </Button.Template>
</Button>

We replaced the TextBlock with a ContentPresenter. We use an attached property to make any text inside the ContentPresenter white.

Why Both?

So we've redefined the look of a Button, and we didn't touch the style.  Why do we need styles?

There are a few good reasons:

  • Styles are easier to use. You don't need to be concerned about the internal composition of the control or making sure that you've included all the necessary pieces.
  • Styles can affect properties on the control that don't have anything to due with the composition.  That means they can affect things that control templates cannot.
  • If you want to globally replace a template for a control in your application, you have to do it with a style.
  • It's not uncommon to "break" your controls when you start creating custom templates. You don't need to worry about this so much with styles.

More to come ...


Posted 06-27-2008 1:54 AM by Christopher Bennage

[Advertisement]

About The CodeBetter.Com Blog Network
CodeBetter.Com FAQ

Our Mission

Advertisers should contact Brendan

Subscribe
Google Reader or Homepage

del.icio.us CodeBetter.com Latest Items
Add to My Yahoo!
Subscribe with Bloglines
Subscribe in NewsGator Online
Subscribe with myFeedster
Add to My AOL
Furl CodeBetter.com Latest Items
Subscribe in Rojo

Member Projects
DimeCasts.Net - Derik Whittaker

Friends of Devlicio.us
Red-Gate Tools For SQL and .NET

NDepend

SlickEdit
 
SmartInspect .NET Logging
NGEDIT: ViEmu and Codekana
LiteAccounting.Com
DevExpress
Fixx
NHibernate Profiler
Unfuddle
Balsamiq Mockups
Scrumy
JetBrains - ReSharper
Umbraco
NServiceBus
RavenDb
Web Sequence Diagrams
Ducksboard<-- NEW Friend!

 



Site Copyright © 2007 CodeBetter.Com
Content Copyright Individual Bloggers

 

Community Server (Commercial Edition)