This article is part 1 of (planned) 4 in a series attempting to
cover everything I think is worth knowing about styles in
Silverlight 4.
What are
Styles?
Styles in Silverlight are a powerful mechanism that allow
controlling the visual representation of elements in a unified way
for all user interface elements. The concept is similar to using
CSS to control the look and feel of HTML.
Style properties differ by element type. For example on a TextBlock (a text element) one can style the
FontSize, FontFamily, Foreground and lots of other properties. On
an Ellipse however none of these properties can
be set, on the other hand it does provide for example the Fill
property, that the TextBlock does not. So creating a blue "Hello,
World" text and a red ellipse could be done the following way:
<stackPanel Orientation="Vertical">
<textBlock FontSize="16" FontFamily="Comic Sans MS" Foreground="Blue">Hello, World!</textBlock>
<ellipse Fill="Red" Width="30" Height="30"/>
</stackPanel>
However, to do the same thing, we could simply define Styles.
Styles are list of property-value pairs. Creating the same blue
text and red ellipse with styles would be done the following
way:
<userControl.Resources>
<style x:Key="CustomTextBlockStyle" TargetType="TextBlock">
<setter Property="FontSize" Value="16"/>
<setter Property="FontFamily" Value="Comic Sans MS"/>
<setter Property="Foreground" Value="Blue"/>
</style>
<style x:Key="CustomEllipseStyle" TargetType="Ellipse">
<setter Property="Width" Value="30"/>
<setter Property="Height" Value="30"/>
<setter Property="Fill" Value="Red"/>
</style>
</userControl.Resources>
<stackPanel Orientation="Vertical">
<textBlock Style="{StaticResource CustomTextBlockStyle}">Hello, World!</textBlock>
<ellipse Style="{StaticResource CustomEllipseStyle}"/>
</stackPanel>
As you can see properties that we've specified as attributes on
the elements, we've listed in the styles and then applied this
style to the element. So instead of defining FontSize="16" of the
TextBlock, we've added a Setter that had FontSize set as it's
Property and 16 as it's Value. (Download the source of this example
here: What Are
Styles.zip)
Having seen a simple example of using styles, let's look a
little bit deeper in to what they actually are.
Styles: List
of Setters for a Given Type
Styles are objects that have two important properties: the type
that they are applied to (TargetType) and the list of their
Setters.
TargetType
Whenever declaring a Style it is mandatory to set it's
TargetType, that is which type of visual element it should be
applied to. The reason for this is simple: in Silverlight all
visual elements have different properties that can be styled.
Looking at the previous example, the Style for TextBlock sets
different properties than the one for Ellipse.
Because the TargetType has to be set for each style, this means
that if you have e.g. a TextBox, an Ellipse and a Grid on the same
page, you want all of them to have a blue background or fill, you
have to declare three different styles for this:
<userControl.Resources>
<style x:Key="BlueEllipseStyle" TargetType="Ellipse">
<setter Property="Fill" Value="Blue"/>
</style>
<style x:Key="BlueTextBoxStyle" TargetType="TextBox">
<setter Property="Background" Value="Blue"/>
</style>
<style x:Key="BlueGridStyle" TargetType="Grid">
<setter Property="Background" Value="Blue"/>
</style>
</userControl.Resources>
Download the source for this example here: Styles -
TargetType.zip.
(There are times when you can get away with using the same style
for different types, I'll go deeper in this in a later part of the
series).
Setters
Setters are the list of property-value pairs. For each type, the
Property values of the Setters within the Style must match the
properties on the type itself. So for example when styling a
TextBlock, you can declare a Setter with the FontSize property, but
not the Fill, because TextBlock does not have a Fill property. So
the following example would work fine:
<style x:Key="ValidTextBlockStyleExample" TargetType="TextBlock">
<setter Property="FontSize" Value="16"/>
</style>
However the following declaration would throw a
XamlParseException in runtime with the message "The property
'Fill' was not found in type 'System.Windows.Controls.TextBlock'"
as there's no FIll property on the TextBlock":
<style x:Key="InValidTextBlockStyleExample" TargetType="TextBlock">
<setter Property="Fill" Value="Blue"/>
</style>
Unfortunately at the moment Visual Studio 2010 doesn't have
Intellisense support when declaring Setters for styles: so you only
get warnings for invalid Setters in runtime. Therefore it's worth
double checking that correct Setter properties have been
declared.
Defining Styles In XAML
In all of the examples I've defined styles in XAML: declaring
them in XAML is the easiest way to write and read them (the other
option is to declare them in the code behind, I'll be covering this
in a later part of the series). When declaring styles in XAML, one
can do it in two different ways: either within Resources section of
the XAML or inline.
Defining Styles in the
Resources Section
Defining styles in the Resources section is the most common way of
doing. For a UserControl(such as the MainPage created as the
start page for a new Silverlight application) this means the
UserControl.Resources section, that should be declared as child of
the UserControl element:
<userControl x:Class="IntroductionToStyles.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
<userControl.Resources>
<style x:Key="CustomTextBlockStyle" TargetType="TextBlock">
<setter Property="FontSize" Value="16"/>
<setter Property="FontFamily" Value="Comic Sans MS"/>
<setter Property="Foreground" Value="Blue"/>
</style>
</userControl.Resources>
<!-- Declare the visual layout after the UserControl.Resources section -->
<grid x:Name="LayoutRoot"></grid>
When declaring Styles in the Resources section, the x:Key value
is required to be set on all Styles (except when applying implicit
Styles, which I'll be covering in the next part of the series). The
x:Key value defines the key of the style that it can be later
referenced as a StaticResource:
<textBlock Style="{StaticResource CustomTextBlockStyle}">Hello, World!</textBlock>
The x:Key values have to be unique within a resource, otherwise
a XamlParseException will be thrown with the message "The
dictionary key 'xxx' is already used. Key attributes are used as
keys when inserting objects into a dictionary and must be
unique."
Defining Styles Inline
Defining styles in the Resources section is recommended because
that way they can easily be reused within the document by
referencing them as StaticResources. However Styles can also be
declared as the property of the visual elements. For example,
declaring the original example (the blue text and red ellipse)
inline would look the following:
<stackPanel Orientation="Vertical">
<textBlock>
<textBlock.Style>
<style TargetType="TextBlock">
<setter Property="FontSize" Value="16"/>
<setter Property="FontFamily" Value="Comic Sans MS"/>
<setter Property="Foreground" Value="Blue"/>
</style>
</textBlock.Style>
Hello, World!
</textBlock>
<ellipse>
<ellipse.Style>
<style TargetType="Ellipse">
<setter Property="Width" Value="30"/>
<setter Property="Height" Value="30"/>
<setter Property="Fill" Value="Red"/>
</style>
</ellipse.Style>
</ellipse>
</stackPanel>
Defining styles inline has no real practical usage: it's much
more longer to write out than just declaring the properties on the
elements and the styles can't even be reused as they're not defined
as resources. (Download the source for this example here: Inline Styles.zip)
Referencing Styles
Defined in XAML from Code Behind
Styles defined within the Resources section of XAML can be
referenced by their x:Key values from the Resources array. So for
example referencing the following style defined in XAML:
<userControl.Resources>
<style x:Key="CustomTextBlockStyle" TargetType="TextBlock">
<setter Property="FontSize" Value="16"/>
<setter Property="FontFamily" Value="Comic Sans MS"/>
<setter Property="Foreground" Value="Blue"/>
</style>
</userControl.Resources>
Can be done the following way in the code behind:
Style customTextBlockStyle = Resources["CustomTextBlockStyle"] as Style;
(Styles can be assigned to objects and manipulated in code
behind as well. I'll go into details on this in a later part of the
series).
Styling Controls Can't
Always Be Done With Styles: Templates and Themes
When working with built-in Silverlight controls, one quickly
realizes that styling them doesn't quite work just by using styles.
Let's take a Button for example. The default Button implementation
has a white color with black text on it. Let's say we want to
change it to black background and red text. The Button has both a
Background property and a Foreground, so let's set these:
<userControl.Resources>
<style x:Key="CustomButtonStyle" TargetType="Button">
<setter Property="Foreground" Value="Red"/>
<setter Property="Background" Value="Black"/>
</userControl.Resources>
<button Style="{StaticResource CustomButtonStyle}" Content="Click me!" Width="150" Height="30"/>
The result:
The button's text color did indeed change to red as we've
expected, however the background is still white. This is because
the Button control has a much more complex layout than what could
be manipulated using simply the Background property. It has
gradient fills, different states on mouseover state and when
disabled and the designers of this control didn't bother specifying
porperties to set each of these. Instead if one wants to customize
the look and feel of a Button control (or any other built in
Silverlight control), one must either customize it using Control
Templates or by using Silverlight Toolkit Themes.
This series focuses only on working with Styles, therefore these
topics are out of scope at this point. For further reading on this
topic I'd recommend the following articles: Control Templates: a tutorial by Scott Guthrie
and Using Silverlight Toolkit Themes by Deborah
Kurata.
Conclusion
In this part I've given an overview of what styles are in
Silverlight and how they can be used. Main points covered were:
- Styles are collections of property setters
(Setters) to be applied to a visual element. It is
mandatory to specify the TargetType for each
Style: the type of element it will be applied to.
- Setters within a Style can be different for
styles with different TargetTypes. Setters can only contain
propeties that the type to be applied to has as well, otherwise an
exception is thrown.
- Defining styles within XAMLcan be done two
ways: either within the Resources section or inline.
- Defining styles within the Resources section
is recommended. Styles defined here need to specify an x:Key
attribute and can be reused within the document with
StaticResources. Keys need to be unique within a resource
section.
- Inline declaration does not have real
advantages: it's lengthy and the Style declared can't be re-used
from the XAML.
- Styles defined in XAML (within the Resources section) can be
referenced from code behind
- The visual look of controls can't always be altered just by
setting properties of the control via styles. A more powerful way
of customizing control look and feel is via Control
Templates and Themes.
I hope this part has been a useful introduction. In the second
part of the series I'll be looking into more advanced Style usage
scenarios, stay tuned!