1. Problem
You want to create a
consistent UI without having to replicate styles, colors, templates, and
so forth on individual elements, much in the same way that CSS
resources are shared in a web application.
2. Solution
Take advantage of ResourceDictionary objects to store resources that can be accessed using the StaticResource markup extension. The Resources member introduced in the FrameworkElement class is of type ResourceDictionary, which is a Dictionary collection accessible via name/value pairs. The Resources member can be used to organize common styles, brushes, and colors for use across an application.
3. How It Works
A markup
extension provides additional evaluation for a value set on an attribute
in XAML. For example, a value can be configured for Background equal to the string "Green", which is evaluated by a TypeConverter that takes the string value and converts it to the Colors.Green enumerations value. You can also set Background equal to the hexadecimal value, such as #FF008000, which also equals the color Green.
Type converters are great for single string values converted to a particular type. A markup extension, such as StaticResource,
allows more complex string values that consist of multiple types to be
evaluated or substituted for the placeholder value of an attribute. A StaticResource
value can be configured for any XAML property attribute except for
event attributes. All markup extensions have the following syntax:
<element attribute="{MarkupExtensionName Value}" />
When you first see this
syntax, it looks a bit confusing, but once you understand it, you see
the power that markup extensions provide. For the StaticResource markup extension, Value represents an x:key name for a resource located in a Resources collection in the application. Usually resources are located at the Application or UserControl (page) level, but they can be located on any element that inherits from FrameworkElement, such as Grid or StackPanel objects.
Silverlight 4 added the ability
to have a merged resource dictionary, which means that you can place
the contents of a resource dictionary in a separate file but have the
resources treated as a logical part of the main XAML file. Resources
stored in a merged resource dictionary are accesses only after all
resources in the main XAML code file are checked for a match. The MergedDictionaries is a collection the UIElement.ResourceDictionary object. Here is an example:
<ResourceDictionary>
<SolidColorBrush Color="#FFFFFFFF" x:Key="darkBrush"/>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/GradientsResourceDictionary.xaml">
<ResourceDictionary Source="/StylesResourceDictionary.xaml">
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
The separate resource dictionary files contain a <ResourceDictionary> declaration as the root element with the resources identified as if part of the MainPage.ResourceDictonary directly.
4. The Code
The sample application for this recipe includes a number of resources defined in the MainPage class. Here is an example resource defined at the <UserControl> level:
<UserControl.Resources>
<Color x:Key="Pumpkin">#FFD5901F</Color>
<Color x:Key="Lime">#FF75E564</Color>
<LinearGradientBrush x:Key="PumpkinLimeBrush"
EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="{StaticResource Lime}"/>
<GradientStop Color="{StaticResource Pumpkin}" Offset="1"/>
</LinearGradientBrush>
</UserControl.Resources>
Three resources are
defined with two color resources and a brush resource. The brush is a
LinearGradientBrush that references the color resources for the
GradientStop Color value using the syntax discussed earlier:
{StaticResource Lime}
NOTE
For performance
reasons, if a resource consists of other resources, define the resources
in order of dependency as shown in the preceding example so that
forward references can be avoided.
Notice that every resource has a name defined by the x:Key attribute, which is different than the x:Name
attribute used to name XAML elements. This is the value used to
reference a resource with the XAML on the page. For example, you add a StackPanel to Grid.Row="0" and Grid.Column="0" and configure the Background attribute to this value:
Background="{StaticResource PumpkinLimeBrush}"
Figure 1 shows the result.
Expression Blend 4 provides
great support to create and manage resources. There is a Resources tab
next to the Project and Properties tabs in the UI. You can expand the
tree in the Resources tab to view resources created as part of the
available objects, such as the Application, MainPage, and StackPanel levels, as shown in Figure 2.
Expression Blend 4 provides a drop-down editor for modifying resources right on the Resources tab, as shown in Figure 3.
Resources can be defined deeper in the XAML tree, such as on a Grid or StackPanel control, or even on a Rectangle directly. Any object that inherits from FrameworkElement has the Resources
collection. Click the Advanced Properties Option button next to
Background, and select Convert to New Resource in the pop-up menu in
Expression Blend 4, as shown in Figure 4.
The Create Brush Resource dialog displays the two available options to
store a resource; either at the application or page level, as shown in
the Define In section of Figure 4.
If you want to define a resource at a different level, you can use Expression Blend 4 to create a resource at the document or UserControl level and then copy it to the location where you want it. As an example, define a new brush called FallBrush at the UserControl level and then move it to a new location, in this case a StackPanel, using the following code:
<StackPanel Grid.Column="0" Grid.Row="1" Margin="2,2,2,2">
<StackPanel.Resources>
<LinearGradientBrush x:Key="FallBrush" EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FF000000"/>
<GradientStop Color="#FFFFA500" Offset="1"/>
</LinearGradientBrush>
</StackPanel.Resources><Rectangle Margin="2,2,2,2" Stroke="#FF000000"
Fill="{StaticResource FallBrush}" Height="193"/>
</StackPanel>
You've moved the FallBrush resource from the UserControl.Resources to StackPanel.Resources and applied it to a Rectangle, resulting in the UI as shown in Figure 1 above. This limits use of the resource within the StackPanel only. For resources that need to be shared across the application, locate the resource at the page or application level.
For this recipe, all of the modifications are in the XAML, shown in Listing 1.
Listing 1. Recipe 9's MainPage.xaml File
<UserControl x:Class="Ch02_ProgrammingModel.Recipe2_9.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignHeight="300"
d:DesignWidth="400"> <UserControl.Resources>
<Color x:Key="Pumpkin">#FFD5901F</Color>
<Color x:Key="Lime">#FF75E564</Color>
<LinearGradientBrush x:Key="PumpkinLimeBrush"
EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="{StaticResource Lime}"/>
<GradientStop Color="{StaticResource Pumpkin}" Offset="1"/>
</LinearGradientBrush>
</UserControl.Resources>
<Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition Height="0.5*"/>
<RowDefinition Height="0.5*"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" Background=
"{StaticResource PumpkinLimeBrush}" Margin="2,2,2,2">
</StackPanel>
<StackPanel Grid.Row="1" Margin="2,2,2,2">
<StackPanel.Resources>
<LinearGradientBrush x:Key="FallBrush" EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FF000000"/>
<GradientStop Color="#FFFFA500" Offset="1"/>
</LinearGradientBrush>
</StackPanel.Resources>
<Rectangle Margin="2,2,2,2" Stroke="#FF000000" Fill=
"{StaticResource FallBrush}" Height="193"/>
</StackPanel>
</Grid>
</UserControl>