When writing code, it’s customary to take
common pieces of code and reuse them in a number of ways, including
creating base classes, creating static classes, or even creating
reusable libraries. XAML has the same need for creating reuse in the
design. This reusability, though, is more about creating a consistent
look for the application without having to copy the same code over and
over. Consider this common XAML:
<TextBox x:Name="nameBox"
FontSize="36"
FontFamily="Segoe WP"
FontWeight="Black"
BorderBrush="Blue"
Foreground="White"
HorizontalAlignment="Stretch" />
<TextBox x:Name="emailBox"
FontSize="36"
FontFamily="Segoe WP"
FontWeight="Black"
BorderBrush="Blue"
Foreground="White"
HorizontalAlignment="Stretch" />
In this XAML many of the properties are copied from one of the TextBox
es
to the other. If we change any of the properties of one, we will have
to copy the change to the other to provide consistency of the UI. In
addition, the Foreground
and BorderBrush
are
using colors that could or should be part of an overall look and feel.
It’s likely that we would want the brushes used there to be consistent
not only from TextBox
to TextBox
but also across the entire application. That’s where styling and resources come in.
Understanding Resources
The first level of consistency has to do with sharing common
resources. When creating an application, you often will want to use
common colors or brushes across the application. XAML allows you to
create objects to be used in more than one area by specifying them in a Resources
section and identifying the object with an x:Key
attribute. For example, you could define a SolidColorBrush
in a Resources
section like so:
<Grid x:Name="LayoutRoot">
<Grid.Resources>
<SolidColorBrush x:Key="mainBrush"
Color="Blue" />
</Grid.Resources>
...
</Grid>
Every class that derives from FrameworkElement
(which means most XAML elements) supports a collection of Resources
. We can refer to these named elements using the StaticResource
markup extension, like so:
<Grid x:Name="LayoutRoot">
<Grid.Resources>
<SolidColorBrush x:Key="mainBrush"
Color="Blue" />
</Grid.Resources>
<TextBlock Foreground="{StaticResource mainBrush}"
Text="Hello World" />
...
</Grid>
The StaticResource
markup extension tells the XAML
parser to replace the foreground with the main brush. You can use the
resource in several places, which isolates it from changes so that
later, when you change the main brush to a LinearGradientBrush
, it cascades down to wherever the StaticResource
was used.
The StaticResource
markup extension looks up through the
XAML document to find the resource with the correct name (through the
hierarchy), and will continue beyond the beginning of the XAML document.
Above the XAML document is the App.xaml file in the phone application
project. Normally this is where you would place any application-wide
resources. For example, if the App.xaml file looked like this:
<Application x:Class="PhoneControls.App"
xmlns="..."
xmlns:x="..."
xmlns:phone="..."
xmlns:shell="...">
<!--Application Resources-->
<Application.Resources>
<SolidColorBrush x:Key="mainBrush"
Color="Blue" />
</Application.Resources>
...
</Application>
the mainBrush
would then be defined at the application level so that any XAML document that wanted to use the brush could do so, like so:
<Grid x:Name="LayoutRoot">
<TextBlock Foreground="{StaticResource mainBrush}"
Text="Hello World" />
...
</Grid>
Although this example shows a brush (which is a very commonly shared
resource), it is not limited to only brushes. Any creatable object can
be used in this way. In addition, when you want to share these resources
across projects you can accomplish this with ResourceDictionary
objects. Resource dictionaries
are XAML files that contain shared resources that can be imported into
App.xaml using merged dictionaries. These dictionaries can be flat XAML
files or can be contained in separate assemblies that are referenced to
your phone application. For more information on merged dictionaries, see
the documentation.
Understanding Styles
While sharing resources can help you to define a common look and
feel, the styling stack extends this idea by allowing you to specify the
default properties for controls in a common place. The Style
object allows you to create these default properties:
<Style TargetType="TextBox"
x:Key="mainTextBox">
<Setter Property="FontSize"
Value="18" />
<Setter Property="FontFamily"
Value="Segoe WP Bold" />
</Style>
The Style
object takes the type of object it can be applied to and then a set of Setter
objects that define default values for properties. In this example, the FontSize
and FontFamily
for a TextBox
are supplied. To apply this style to an object, you can map it to the Style
property on an element via the StaticResource
markup extension, like so:
<TextBox Style="{StaticResource mainTextBox}" />
By setting this TextBox
’s Style
property using the StaticResource
markup extension, the default property values of the TextBox
will be set using the Style
. Style
s
are just named resources, so you would typically place them in the
App.xaml file along with other resources. In addition, you can use
resources inside your styles, like so:
<Application.Resources>
<SolidColorBrush x:Key="mainBrush"
Color="Blue" />
<Style TargetType="TextBox"
x:Key="mainTextBox">
<Setter Property="FontSize"
Value="18" />
<Setter Property="FontFamily"
Value="Segoe WP Bold" />
<Setter Property="Foreground"
Value="{StaticResource mainBrush}" />
</Style>
</Application.Resources>
In this way, the shared resources can cascade down into the styling stack. In addition, the Style
s themselves can be cascaded by using the BasedOn
property:
<Application.Resources>
<SolidColorBrush x:Key="mainBrush"
Color="Blue" />
<Style TargetType="TextBox"
x:Name="baseTextBox">
<Setter Property="FontSize"
Value="18" />
<Setter Property="FontFamily"
Value="Segoe WP Bold" />
</Style>
<Style TargetType="TextBox"
x:Key="mainTextBox"
BasedOn="{StaticResource baseTextBox}">
<Setter Property="Foreground"
Value="{StaticResource mainBrush}" />
</Style>
</Application.Resources>
Finally, styles can be polymorphic. In other words, the TargetType
may apply to a base class and be applied to all objects of that type. For example:
<Application.Resources>
<SolidColorBrush x:Key="mainBrush"
Color="Blue" />
<Style TargetType="Control"
x:Name="baseControl">
<Setter Property="BorderBrush"
Value="Black" />
</Style>
<Style TargetType="TextBox"
x:Key="mainTextBox"
BasedOn="{StaticResource baseControl}">
<Setter Property="FontSize"
Value="18" />
<Setter Property="FontFamily"
Value="Segoe WP Bold" />
<Setter Property="Foreground"
Value="{StaticResource mainBrush}" />
</Style>
</Application.Resources>
Since the TargetType
of the base style was Control
, it could be used as the BasedOn
for any controls (or even the Style
for any control that derived from the Control
class).
Implicit Styles
While you will often use named styles (and the StaticResource
markup extension) to tie a Style
to a XAML element (as shown in the preceding section), you can also
create styles that apply to elements by default. These are called implicit styles. To create an implicit style, your style must not include a key. For example:
<Application.Resources>
<Style TargetType="TextBox">
<Setter Property="FontSize"
Value="18" />
<Setter Property="FontFamily"
Value="Segoe WP Bold" />
</Style>
</Application.Resources>
By eliminating the x:Key
on the Style
, the style will apply to every element of the TargetType
(e.g., TextBox
).
If an element is specifically styled with an explicit (e.g., named)
style, the implicit style is completely replaced. Therefore, you can
have either an implicit or an explicit style applied to a specific XAML
element, not both. In most cases you will have an implicit style for the
main style of a control, then specific explicit styles to handle
specific use cases for controls.
One big difference in implicit styles is that the TargetType
is not polymorphic, so it only applies to the specific type, not
derived types. For example, if you create an implicit style of type
“Control”, it will only apply to XAML elements of the Control
class specifically; TextBox
and Button
elements (which derive from Control
) will not use the style at all. The other rules for styles (e.g., using base resources, cascading styles with BasedOn
) all apply.