Styling Tips for Silverlight and WPF Controls
In this article I’ve collected together some information and advice that you’ll find useful when creating controls templates. These topics apply generally, no matter what control you’re creating a template for. Here you’ll learn how to bind the look of your template to properties on the control instance, about some of the smart make-into-control workflows, and you’ll hear some advice about using visual states on both custom controls and UserControls.
Controls have properties such as Background and Foreground (Brushes), Padding, and many more. One way these properties can be manifested is by binding them to an object in the template. This is known as template binding. Say you have a shape (Rectangle, Ellipse, Path) in a Button template and you want to paint it with the Background Brush of any Button it gets applied to. While you’re in the template, select the shape, go to the property inspector and find its Fill property, click the property marker, click Template Binding, then click Background.
Some properties, such as OpacityMask, Font, FontSize, LayoutTransform and RenderTransform, do not need to be template bound. They will affect the objects in the template automatically.
Content and Header Properties
Several controls have a Content property, some notable examples being Button, CheckBox and RadioButton. Very similar to Content is the Header property. TabItem and MenuItem are examples of controls that have a Header property. The value of properties such as Content and Header is that you can use content of any type; it doesn’t have to be just text.
If you need this style to support any kind of content
You’ll need a ContentPresenter in your template, with its own Content property template bound to the control’s Content (or Header, etc) property. If you use Make Into Control to make artwork into a template then Blend has already added a ContentPresenter to the template. If you were using a TextBlock in the artwork to represent the content then delete that TextBlock from the template. When you set the Content (or Header, etc) property of a control with this style applied, the ContentPresenter will display that content.
If this style only ever has text content
Instead of a ContentPresenter you can put a TextBlock in the template and template bind the TextBlock’s Text property to the Button’s Content (or Header, etc) property. If you use Make Into Control to make artwork into a template and you were using a TextBlock in the artwork to represent the content then you can use that TextBlock. Delete the ContentPresenter that Blend added to your template. When you put some text in the Content (or Header, etc) property of a control with this style applied, the TextBlock will display that text.
Smart Make Into ContentControl
When you use Make Into Control to turn artwork containing a TextBlock object into a template for a ContentControl (buttons and so on), Blend does the following for you:
- Put a ContentPresenter into the template in place of the TextBlock.
- Copy layout properties from the TextBlock to the ContentPresenter.
- Copy Typography properties from the TextBlock to the control Style.
- Copy the Text property from the TextBlock to the control instance’s Content property.
Smart Make Into TextBox
When you use Make Into Control to turn artwork containing a TextBlock object into a template for a TextBox, Blend does the following for you:
- Put a ScrollViewer representing the ContentElement part into the template in place of the TextBlock.
- Copy layout properties from the TextBlock to the ContentElement part.
- Copy Typography properties from the TextBlock to the TextBox Style.
- Copy the Text property from the TextBlock to the TextBox instance.
Visual states are grouped such that the states in a state group are mutually exclusive, but the states in any group are independent of the states in any other. This means that any one state from each group can be applied at the same time without conflict. An example is a check box where the checked states are independent of, and orthogonal to, the mouse states. Changing an object’s property in more than one state in the same group is common practice. For example, you might change a Rectangle’s Fill to different colors in MouseOver, Pressed and Disabled. This works because only one state from the CommonStates state group is ever applied at a time. But changing an object’s property in more than one state group breaks the independent nature of the state groups and leads to conflicts where more than one state is trying to set the same object’s property at the same time. Blend will display a warning icon (with a tooltip containing details) beside any affected state group whenever this conflict situation is detected.
Remember that each state name must be unique within a template, even across state groups.
The ‘normal’ visual state
Each state group should have a state that represents the default state for that group. CommonStates has ‘Normal’, CheckedStates has ‘Unchecked’, and so on. It is a good (and efficient) practice to set objects’ properties in Base such that no changes have to be made in any ‘normal’ state. So, for example, you would hide a check box’s check glyph and focus rectangle in Base and show them in Checked and Focused respectively.
Remember that Base is not a state, therefore you cannot define nor control transitions to and from Base. Base simply represents the template with its local (or ‘base’) property values set, with no states applied.
When you create your own templated controls, or UserControls, you should define a ‘normal’ state in each state group and have the control go to those ‘normal’ states when it loads. Unless you do this, when you for example first mouse over a Button, the Button will not be in the Normal state so the Normal -> MouseOver transition will not run. And remember you cannot define Base -> MouseOver because Base is not a state. Only states should figure in your state transition designs.
These tips are relevant to control templates in general. When you’re ready to create a template for a specific control be sure to check out the following control-specific articles: