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 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:
<Setter Property="X" Value="Y" />
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 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:
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.
Now, 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:
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">
<ContentPresenter TextBlock.Foreground="White" />
We replaced the TextBlock with a ContentPresenter. We use an attached property to make any text inside the ContentPresenter white.
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 ...
06-27-2008 1:54 AM