Download the tutorial in PDF formatBy Jesse Liberty
Download the code
Styling Controls
Silverlight Controls are designed to look great
right out of the box. That said, you inevitably find yourself making at
least small adjustments to fit your particular design.
In this tutorial we will look at two ways to
change the look of Silverlight controls. First, we will look at Styles
– the ability to modify characteristics of the look of controls. Later,
we will look at Templates, which allow any control to start out
“lookless” (with no specific appearance) and to have its appearance
entirely dictated by a design you supply.
Even simple programs use some style attributes
For example, in the previous tutorial, we used a
number of in-line styles to change the default appearance of the
controls so that they would align, have the height and width we desired
and so forth.
To get
started working with styles, make a new program, called Styler, and
carefully turn it into a clone of BookProperties from the tutorial on
Data Binding (or, if you like, just download BookProperties from
Silverlight.net).
In Styler, create a Book.cs file and copy the Book class from the original program.
Next,
replace the new Grid with the Grid from the original program, and
replace the contents of the Page class in Page.xaml.cs with the
contents of the same class in the original program.
Run the new program to make sure you have a working copy. You’re ready to go.
An in-line style is placed as an attribute “in
line” with the declaration of the control. We’ll look at an
alternative, the Style object, in just a moment
To really see in-line styles at work, let’s add font styles to the first two TextBlocks (for the Title and Author prompts.

Here we’ve set two of the Prompt TextBlocks (I’ve
cut out the elements in between) to use the Comic Sans MS font at size
16, medium bold color of Navy blue. The effect is unmistakable,

The two prompts are noticeably different (if not nicer – I never said I was a designer).
NB: During Beta 1 the Font families supported by Silverlight are:

Inline Styles Don’t Scale
There is much to be admired about having all the
prompts look alike, but by setting these independently in each text
box, you run two risks:
1. There will be little or no uniformity in your layout and (worse)
2. If
you decide to change a value (e.g., all data entry text boxes should
now have a height of 40) you must make that change everywhere;
inevitably missing a few. This is hard to maintain and gets harder as
your program gets larger.
Thus, there is a real need for a way to set the
style for different controls in a single location and then applying
those styles in the control, and that is what the Style object is for.
Creating and Using Styles
As a simple start, you can define a Resource
section for your grid, and define the styles you want for the various
controls. Each style consists of a Style element with attributes for
· The target type (the element type you’ll apply the style to)
· A Key (the name for the style that you’ll use to refer to the style
· Zero or more Setters
A setter represents an style attribute. Each
setter consists of a setter element with property /value pairs, where
the property is the style property you are setting and the value is the
value to be set for that property for that style against that target.
These are easy to set if you’ve started out with inline properties.
Thus, if you have this code:
You can translate it directly into a style:

You can then apply this style back to the Text
box (the TargetType) using the Key and eliminating all the in-line
styles that you’ve moved to the Style object (HorizontalAlignment,
Width and Height).

Note that the xKey value in the Style
declaration and the ResourceName in the UIElement must agree. This is
so that you can create more than one style for the same type of
element. For example, we use TextBlocks for label prompts and also for
values. We might decide to use different styles in the two situations,

Here we’ve declared two styles, both for
TextBlocks. They are differentiated by having different Keys. Notice
that the prompt uses a medium weight blue font, and the value is a
normal black font.
You apply the two styles based on their usage,

Here the first TextBlock is being used as a
label, the second to display a value retrieved from the data object.
The result of using the differing styles is that the text is
differentiated. The advantage of the styles is that we can do this
repeatedly with consistent results, and if we decide to change black to
purple, that change is made in one place (the style) rather than in
every TextBlock,

Having assigned Styles to the TextBlock and the
TextBox, we can go on to create Styles for the other controls as well,
as shown in this cut down of the entire Styles section,

You can see by the missing line numbers that
I’ve cut out the body of the styles already examined. The new styles
(ListBox, CheckBox and Button), however, are not terribly different.
You can, of course, add other Style values.
Two good ways to find what properties you might add to a style are
1. Go to the control and use Intellisense to reveal the properties of the control,

2. Even better, consult the properties listing of the on-line or local help files,

Overriding Styles
Once you’ve set styles for your controls, you
are still free to override any attribute of any Style for any
individual control. Note that overriding one attribute does not
override the entire style. That is, if the TextBlockPrompt style sets
six attributes,

You can freely override one or more of these on
an individual control, and the properties not overridden will still be
in effect. To see this at work, let’s set the prompt for Quantity on
hand to Red and Bold,
The Declaration of the text block uses the Style
but overrides the two properties (or attributes) that we care about.
The result is just what we want: application of the rest of the style
but these two properties are overridden for this control,

Application Resources
Until now we’ve put the static resources in the Grid,

While this is convenient, it scopes the style
definitions to this single Grid. You can, however, make these styles
available to the entire application by moving them to App.xaml.

You will find that Visual Studio 2008 has
created an empty Applicaton.Resources element within this file. Just
drop your styles in, and they are globally available,

What is particularly nice is that you do not have to change your invocation of the styles at all; you use them just as you had,

The call to the StaticResource will resolve to the declaration in App.xaml with no additional effort.
Content
Some controls have a Text property and others, like Button, have a Content
property, and yet, until now they’ve seemed to do the same thing. That
has been an illusion fostered by the convention of filling Content with
text; but while you can only fill the Text property of a Text box with
(you guessed it!) Text; you can fill the Content property of a button
with… just about anything
Button
inherits from ButtonBase which in turn inherits from ContentControl.
ContentControls are objects that have a content property (or, to be
precise, have a ContentControl dependency property).
Let’s start simple and create a button that has an image and text and when you click on it takes an action.
In a new project named ContentButton, I’ve added
a button with the usual attributes, except for content, which I’ve
filled with a stack panel. The stack panel holds two controls: an image
and a TextBlock (I only realized I stole this from Scott’s tutorial after I was done, but it is a natural first step in adding content to a button).

On line 5 the Grid is created as usual. On line
7 the Button is created and given the normal attributes (note that it
is placed at Grid Row and Column 0 on line 10.

The Content property for the button begins on
line 12 and does not end until line 24. It consists of a stack panel
which runs from line 13 through 23. Within the stack are two controls,
an image, created on lien 17, and a TextBlock created on lines 18-22.
The image has a Source property, and the URL used points to the ClientBin directory, as can be made visible by clicking on the Show All Files button in Solution Explorer. You can, of course, obtain the image from the web, by providing the URI of any image.
Below the Button (and thus outside the
StackPanel and the Button.Content, and all by itself in the grid) is a
second image, declared on line 27 that holds a separate image to be
displayed only if the user clicks the button. This image has its height
and width set based on its actual size so as to render at 100%, and has
its visibility set to collapsed as to be initially invisible. Finally,
its Grid.Row and Grid.Column are also set to 0,0 so that when it is
made visible it will overlay the button.
Finally, notice that on line 11 I declared the
event handler in line this time; a lazy trick rather than declaring it
where it belongs (in the code behind) which I did to illustrate that it
could be done. Visual Studio noticed and created a template for the
event handler, which I replaced with code to make the second image
visible,
When you run the program, the button is rendered
with its content consisting of the image and the text you’ve provided,
and the “out of the box” button behavior (including the subtle mouse
over animation) continue to work,
Clicking on the button fires the event handler
and the second image is made visible and it covers the button, (Note,
I’ve moved the button to the upper left hand corner to save space)

A Quick Note On Design Expectations
You can make a button any size and shape you
want, but in my experience users expect a button to be rectangular
(possibly with rounded corners.) If you are going to depart from that
you may want to do two things to help avoid confusing your users:
a. Make it obvious that these are buttons; don’t make your user thnk (see Don’t Make Me Think by Steve Krug and the even more imperative Why Software Sucks by the ever brilliant
David S. Platt
b. You’ll know you’ve failed if you need text to tell your user that what they’re looking at is a button (see The Design Of Everyday Things by Donald Norman – required reading for anyone who ever touches the User Interface of any program).
c. In
my opinion, bigger departures are more successful than small
departures; thus I suspect round buttons work better than square. Viz:

Interestingly, you can create the first and
third from the existing button class by modifying just the height and
the width attributes, but to create the middle button you must modify
the button Template: our next topic.
Templates
There is a limit to how much of the appearance
of your control you can modify through changing style attributes. If
you totally want to change the “skin” of your control (without
modifying the controls methods and events) you can do so by changing
the control’s Template.
Every control has a default template, which
gives it the appearance you are used to. When you create a new template
you are not imposing a template on top of the skin of the control, you
are just replacing the default template with your own.
Starting Easy – Creating A Reusable Skin
Let’s start easy by using a ControlTemplate
object inside the Setter.Value of a Style object that we’ll make
universally available in App.xaml.
We saw something similar earlier, when we put
the composite button (with the image and the text box) in App.xaml.
This is different in an important way, however, as this time the first
Setter in the style is Property=Template which signals that we are changing the entire skin

Each property takes a value, but this time the value will be spread out to contain the entire definition of the template,

The value begins on line 8 and runs through line 27.
On line 9 Button is set as the element tree to
be applied as the control template. The template look runs from line 9
through 25. A grid is chosen as the easiest layout element to work
with; the parts will simply fall into position.
Two controls are placed in the new RoundButton
skin. The first is an Ellipse. Since the Width and Height are set to
the same value on line 11, this will be a circle. The Ellipse is filled
with a RadialGradientBrush, with the center from which the colors
radiate offset slightly.
Shapes
and Brushes are unchanged in these aspects from Silverlight 1.0 and
will be the topic of a full tutorial in coming weeks. In the interim,
you may find this blog article a good starting point as well as this video
Using Your New Button
With the definition of your new template in
place, you can now use that button just as you used your composite
button previously. You set the new button skin as a “style” in
Page.xaml,

This time, let’s wire up the event handler in the code behind file; it makes me feel more like a real programmer.

When you run the application, your new button
comes up, and clicking on it provides the same behavior as clicking on
the old button!
Variable Content
As designed, the button always says “Music,” and
it is always the same size. It would be great if, like a normal button,
we could reuse it and set the content, height and width each time we
do.
The solution is to change the template to use
TemplateBinding objects for any property we want to set when we
instantiate a RoundButton. Returning to App.xaml, we can remove the
hard coded values for the height and width of the ellipse, as these are
values the user of our RoundedButton would expect to set,

Similarly we’ll use TemplateBinding for the
TextBox’s FontSize and FontWeight and even its Content, but to do that,
we need to change the type from TextBox to ContentPresenter.

This has tremendous advantages, because now
rather than just being able to put text into the Rounded Button we can
put anything that can go into Content – any control we like.
Let’s jazz up this application by using Data
Binding as described in Tutorial #2 on Data Binding. We’ll create a
very simple business object to represent what I still insist on calling
an “Album” because I’m very old.

The class is marked to implement the
INotifyPropertyChanged interface on line 5 and as explained in the
second tutorial on Data Binding. In effect this means two things:
1. The class will declare an event of type PropertyChangedEventHandler (and named PropertyChanged by convention)
2. Each
public property will call use this event to notify any interested
“listener” (e.g., the control that binds to this business object) when
the property is set (e.g., see line 50)
I’ve stripped the Album class down to its
essentials: which the name of the album, the name of the group, the
genre (as if you could pick a single genre) and the performers on the
album.
Creating the Template
The template is created in App.xaml, using the
techniques discussed above. The Rounded Button is set as an
application Resource, and created as a style whose TargetType is
Button. The Setter Property is Template (signaling that this is a
template and not a normal style) and the value is set on lines 7
through 28. The entire value is set within a ControlTemplate, and built
inside a grid. It consists of an Ellipse (for the shape of the rounded
button) and a ContentPresenter (for the text or other contents within
the buton).
The TemplateBinding construct allows us to defer
setting some properties (such as height, width, etc.) until the
template is used in, e.g., page.xaml so that one template may be used
in various ways.

We put the template to work in Page.xaml

Four RoundButtons are created (lines 7, 10, 13
and 16). The pattern is to name the RoundButton object, set its width
and height (if these values are equal the button will be round,
otherwise it will be more obviously elliptical) and then bind its data
and set its style to the Template created in App.xaml.
The Content is set using the Binding object;
which binds the control to a property of the Album (Group, AlbumName,
Genre and Performers respectively). No specific album is bound until
the DataContext is set at run time in the code behind.
On line 27 a TextBlock is declared, but no text is assigned, so it is not visible for now.
Implementing the Event Handlers
The code-behind file is intentionally kept
simple. The first thing to note is that nowhere in the code-behind need
there be any awareness that the buttons have had their skins changed;
templates affect appearance, not behavior.

The first thing done in Page_Loaded is that all four buttons have their Click event handled by the same event handler.

As explained in the 2nd Tutorial,
with routed events you cannot assume that Sender is the actual object
that sent the event, so we cast it not to type Button but rather to
type FrameworkElement, and get that object’s name an display it in the
formerly invisible TextBlock “Msg.” This is put in as a place holder
to demonstrate that the buttons continue to work as buttons.
Also in Page_Loaded we need to create a
DataSource to bind all the Binding objects to, which we accomplish by
creating a new album and assigning values to its properties,

On line 29 an instance of Album is created. On
lines 30-33 the properties of the new Album are populated, and on line
42 that new Album is assigned as the dataContext for the StackPanel
that encompasses the controls that will bind to that album.

In the end, we’ve created a new skin (or template) for the button that behaves the same, but looks entirely different.