ResourceDictionary and StaticResource references (Windows Store apps using C#/VB/C++ and XAML)

2 out of 4 rated this helpful - Rate this topic

In addition to defining the UI for your app, XAML also has a metaphor for defining blocks of XAML information that can be reused as a resource throughout an app or a XAML page. This metaphor is based on using a XAML ResourceDictionary element to define the resources, and then referencing the defined resources by their keys with a StaticResource markup extension usage. The XAML elements you might want to declare most often as XAML resources include Style, ControlTemplate, animation components, and Brush subclasses. This topic explains how to define a ResourceDictionary and keyed resources, and how XAML resources relate to other resources that you define as part of your app or app package. It also explains concepts such as MergedDictionaries and ThemeDictionaries.

Prerequisites

This topic assumes that you understand XAML markup and have read the XAML overview.

XAML resources must be shareable

For an object to be defined in and accessed from a ResourceDictionary, the object must be shareable.

Being shareable is required because, when the object tree of an app is constructed and used at run time, objects cannot exist at multiple locations in the tree.

ResourceDictionary supports these objects for shareable usage:

  • Styles and templates.
  • Brushes and colors.
  • Animation types including Storyboard.
  • Transforms.
  • Matrix and Matrix3D.
  • Point.
  • Certain other UI-related structures such as Thickness and CornerRadius.
  • Custom types that are defined in your backing code and then instantiated in XAML as a resource. Examples are object data sources and IValueConverter implementations for data binding. Custom types must have a default constructor, and cannot have the UIElement class in their inheritance.
  • XAML intrinsic data types.

Keys for resources

The items within a ResourceDictionary must each have a key defined. A ResourceDictionary really is a dictionary or map in a programming sense. The x:Key becomes the item's key, and the remainder of the XAML defines an object that is the item's value. In XAML, you assign the key by providing a value for the x:Key attribute on the object element that is added as ResourceDictionary content.

Resources must use strings for their key names. If you include an item in a ResourceDictionary and the item does not have a usable key, a XAML parse error occurs when the app attempts to look up the resource item. If you duplicate a key, a XAML parse error occurs when the ResourceDictionary as a whole is first loaded. Microsoft Visual Studio and its XAML design surface can often provide design-time feedback if resource-definition or resource-reference issues exist in your XAML. However, if the XAML designer cannot provide a warning, XAML resource definition issues may instead be reported as errors or exceptions when the app attempts to load the XAML at run time.

Keys for control templates and styles

The Template property for a Control element and a Style element with a TargetType property are special cases for which the key for the resource as it exists in a ResourceDictionary is implicit. That implicit key is based on a string form of the TargetType value applied to the template or style, and that key is used at run time for implicit lookup of which style or template to apply to an object.

Immediate and app resources

FrameworkElement.Resources and Application.Resources are two existing Windows Runtime properties that take values of type ResourceDictionary.

FrameworkElement.Resources provides immediate resources. Immediate resources are also sometimes called page resources. In XAML, you can use the immediate reference technique to reference the keyed resources from FrameworkElement.Resources from any object that is connected to the same object tree as the resource dictionary where the resource is defined. Typically you define the FrameworkElement.Resources value on the root element of a XAML page.

Application.Resources provides app-wide resources. The resources that are defined by Application.Resources are available no matter what page or other UI is loaded as the current Window.Content of the app. Specifying resources at the app level can be useful if you are loading different pages into the Window.Content and want a way to avoid duplicating the same resources in each page's immediate resource scope. Also, if you are writing values into a ResourceDictionary at run time, the app scope provides a location where those resources can persist for the app's lifetime.

A third location where resources can exist is as part of the default style of a control, packaged along with the control. This location is only for lookup of the resource that is keyed by the DefaultStyleKey value of the control.

Note   Do not confuse the concepts related to ResourceDictionary with the Resource build action, resource (.resw) files, or other "resources" that are discussed in the context of structuring the code project that produces an app package. Although the concept of resources for build actions and app packaging structure can overlap with ResourceDictionary usages, think of the ResourceDictionary generally as providing a self-contained resource-sharing system that uses XAML as its definition format. For more info about app-resource concepts overall, see Defining app resources and How to prepare for localization.

Referencing resources from XAML

In XAML, you reference an existing resource from a ResourceDictionary by using the StaticResource markup extension. To use the markup extension, you always reference the property you are setting by means of an attribute usage. For example, to set the value of the Background property of a Button to be a resource you define, use this XAML.


<ResourceDictionary>
...
  <LinearGradientBrush x:Key="fadeBrush">
    <GradientStop Color="Red" Offset="0"/>
  <  GradientStop Color="Gray" Offset="1"/>
  </LinearGradientBrush>
</ResourceDictionary>
...
 <!--XAML within a UserControl or some other root container tag that defines app UI-->
<Button Background="{StaticResource fadeBrush}" .../>


In the preceding example, the two pieces of XAML might not even be in the same XAML file. The ResourceDictionary might be defined in Application.Resources, in a theme dictionary, or in a merged dictionary. You must use XAML attribute syntax to make a resource reference even if the property you are setting typically requires a property element usage in XAML.

For example, here is equivalent property element usage, if the LinearGradientBrush is defined inline rather than referencing a ResourceDictionary resource.


<Button>
  <Button.Background>
    <LinearGradientBrush>
      <GradientStop Color="Red" Offset="0"/>
      <GradientStop Color="Gray" Offset="1"/>
    </LinearGradientBrush>
  </Button.Background>
</Button>

Lookup behavior for StaticResource

The lookup behavior for StaticResource starts with the object where the actual usage is applied and its own Resources property. If a ResourceDictionary exists there, that ResourceDictionary is checked for an item that has the requested key. This first level of lookup is rarely relevant because you usually do not define and then reference a resource on the same object. In fact a Resources property often doesn't exist here. You can make StaticResource references nearly anywhere in XAML; you aren't limited to FrameworkElement subclasses.

The lookup sequence then checks the next parent object in the runtime object tree of the app. If a FrameworkElement.Resources exists and holds a ResourceDictionary, the dictionary item with the specified key string is requested. If the resource is found, the lookup sequence stops and the object is provided to the location where the reference was made. Otherwise, the lookup behavior advances to the next parent level towards the object tree root. This sequence continues until the root element of the XAML is reached, exhausting the search of all possible immediate resource locations.

It is a common practice to define all the immediate resources at the root level of a page, both to take advantage of this resource-lookup behavior and also as a matter of markup style.

If the requested resource is not found in the immediate resources, the next lookup step is to check the Application.Resources property. Application.Resources is the best place to put any app-specific resources that are referenced by multiple pages in your app's navigation structure.

Control templates have another possible location in the reference lookup: theme dictionaries. A theme dictionary is a single XAML file that has a ResourceDictionary element as its root. The theme dictionary might be a merged dictionary from Application.Resources; this is the case in many of the default templates that reference Common/StandardStyles.xaml. The theme dictionary might also be the control-specific theme dictionary for a templated custom control that is being included in your UI.

Finally, there is a resource lookup against platform resources. Platform resources include the control templates that are defined for each of the Windows 8 themes, and which define the default appearance of all the controls that you use for UI in a Windows Store app built for Windows using C++, C#, or Visual Basic. Platform resources also include a set of named resources that relate to system-wide appearance and themes. These resources are technically a MergedDictionaries item, and thus are available for code lookup once the app has loaded. For example, these resources include a resource named "SystemColorWindowTextColor" that provides a Color definition to match app text color to a system window's text color. Other XAML styles for your ap can refer to this style, or your code can get a resource lookup value (and cast it toColor in the example case).

If the requested key is still not found in any of these locations, a XAML parse exception occurs. In certain circumstances, the XAML parse exception may be a run-time exception that is not detected either by a XAML markup compile or a XAML design environment.

Based on the lookup behavior for resource dictionaries, you can deliberately define multiple resource items that each have the same string value as the key, as long as each resource is defined at a different level. In other words, keys must be unique within any given ResourceDictionary, but the uniqueness requirement does not extend to the lookup sequence as a whole. Only the first such object retrieved is used for the StaticResource. You could use this behavior to retrieve the same StaticResource key at various levels of the object tree but obtain different results, depending on the scope from which the StaticResource reference was made.

Merged resource dictionaries

A merged resource dictionary enables you to declare the contents of a resource dictionary by referencing an external file, and to use the externally defined resources to augment the resources found in an existing Resources property location. Merged resource dictionary usages modify two of the default characteristics of resource dictionaries: the lookup sequence, and key-uniqueness requirements within a scope.

To declare a merged resource dictionary, you add a property element for the MergedDictionaries property to an existing ResourceDictionary element. You can add MergedDictionaries for either FrameworkElement.Resources or Application.Resources properties, but merging into Application.Resources is more typical.

You must explicitly declare ResourceDictionary as an object element in order to use a property element within it. The existing ResourceDictionary can have other keyed resources in addition to the MergedDictionaries property element. The content of a MergedDictionaries XAML property element includes one or more ResourceDictionary items declared as XAML object elements. The ResourceDictionary elements representing merged dictionaries cannot have additional keyed resources as content. Instead, these ResourceDictionary elements must declare only one attribute: Source. The Source value is how you reference the resource dictionary's external location. For example, the following XAML defines a ResourceDictionary with one keyed resource, as well as a MergedDictionaries collection that references two different resource dictionaries by Uniform Resource Identifier (URI).


<Application.Resources>
    <ResourceDictionary>
      <SolidColorBrush Color="#d0157820" x:Key="muddyBrush"/>
      <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="rd1.xaml" />
        <ResourceDictionary Source="rd2.xaml" />
      </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
  </Application.Resources>

In the resource-lookup sequence, a MergedDictionaries dictionary is checked only after a check of all the keyed resources of the ResourceDictionary that declared MergedDictionaries. You can specify more than one ResourceDictionary within MergedDictionaries. After the lookup behavior exhausts the search for keys in the main dictionary and reaches the merged dictionaries, each item in MergedDictionaries is checked. If multiple merged dictionaries exist, these dictionaries are checked in the inverse of the order in which they are declared in the MergedDictionaries property. In the preceding example, if both "rd2.xaml" and "rd1.xaml" declare the same key, the key from "rd2.xaml" is used.

Within the scope of the ResourceDictionary, the dictionaries are checked for key uniqueness. However, that scope does not extend across different items in MergedDictionaries.

You can use the combination of the lookup sequence and lack of unique key enforcement across merged-dictionary scopes to create a precedence sequence for fallback values of ResourceDictionary resources. For example, you might store user preferences for a particular brush color in the last merged resource dictionary in the sequence. However, if no user preferences exist yet, you can define that same key string for a ResourceDictionary resource in the previous MergedDictionaries item's XAML source, and it can serve as the fallback value. Remember that any value you provide in a primary resource dictionary is always looked up before the merged dictionaries are checked, so if you want to use the fallback technique, don't define the resource in a primary resource dictionary.

ThemeDictionaries

A theme dictionary is a special type of merged dictionary that is intended to hold the resources that vary depending on which theme a user is currently using on his or her PC. For example, the "light" theme might use a white color brush whereas the default theme might use a dark color brush. The brush changes, but otherwise the composition of a control that uses the brush as a resource could be the same, just by referencing a theme resource. Instead of using MergedDictionaries as the property to merge these items into the main dictionaries, use the ThemeDictionaries property. As with MergedDictionaries, you set this property by using property element syntax, and the value of the property is one or more ResourceDictionary object elements.

Each ResourceDictionary element here must have an x:Key value. That value is a string that names the relevant theme—for example, "Default" or "HighContrast".

The contained ResourceDictionary elements can use one of two possible models:

  • The ResourceDictionary has only one attribute, Source, and no other contents. The Source refers to a separate XAML file that contains only a ResourceDictionary root. This dictionary then defines keyed items that are specific to the theme that is named by the x:Key value on the ResourceDictionary element that specified Source. The Source value typically references a XAML file in your project structure and app package.
  • The ResourceDictionary has contents, which are keyed items that are specific to the theme that is named by the x:Key value on the parent ResourceDictionary element.

Also like merged dictionaries, it is legal for the same key to be defined multiple times as long as there is uniqueness within each theme-dictionary unit. In fact this is the intended design: each theme dictionary should have an identical set of keys. Otherwise, any theme that is missing one of the keys is likely to cause a problem when that theme is loaded. Unlike merged dictionaries, the order of definition of each theme doesn't matter. For theme dictionaries, the active dictionary to be used for resource lookup is always determined at run time. The lookup logic in general is based on mapping the active theme to the x:Key of a specific theme dictionary.

It can be useful to examine how the theme dictionaries are structured in the default XAML design resources. Use a text editor to open the XAML files in \(Program Files)\windows kits\8.0\Include\winrt\xaml\design. Note how the theme dictionaries are defined first in generic.xaml, and how each theme dictionary defines the same keys. Each such key is then referenced by elements of composition in the various keyed elements that are outside the theme dictionaries and defined later in the XAML.

Forward references within a ResourceDictionary

Static resource references within a particular resource dictionary must reference a resource that has already been defined with a key, and that resource must appear lexically before the resource reference. Forward references cannot be resolved by a static resource reference. For this reason, if you use static resource references from within another resource, you must design your resource dictionary structure so that the resources that you intend for additional by-resource use are defined at, or near the start of, each respective resource dictionary.

Resources defined at the app level cannot make references to immediate resources. This is equivalent to attempting a forward reference, because the app resources are actually processed first (before any navigation-page content is loaded). However, any immediate resource can make a reference to an app resource, and this can be a useful technique for avoiding forward-reference situations.

UserControl usage scope

A UserControl element has a special situation for resource-lookup behavior because it has the inherent concepts of a definition scope and a usage scope. A UserControl that makes a resource reference from its definition scope must be able to support the lookup of that resource within its own definition-scope lookup sequence—that is, it cannot access app resources. From a UserControl usage scope, a resource reference is treated as being within the lookup sequence towards its usage page root (just like any other resource reference made from an object in a loaded object tree) and can access app resources.

ResourceDictionary and XamlReader.Load

You can use a ResourceDictionary as either the root or a part of the XAML input for the XamlReader.Load method. You can also include StaticResource references in that XAML if all such references are completely self-contained in the XAML. XamlReader.Load parses the XAML in a context that is not aware of any other ResourceDictionary objects, not even Application.Resources.

Using a ResourceDictionary from code

In C# or Microsoft Visual Basic code, you can reference a resource in a ResourceDictionary by using the indexer (Item). A ResourceDictionary is a string-keyed dictionary, so the indexer uses the string key instead of an integer index. In C++ code, use Lookup.

These code calls are made on a ResourceDictionary instance, so you must first retrieve one—either an immediate ResourceDictionary somewhere in the object tree by getting FrameworkElement.Resources, or Application.Current.Resources.

When referencing resources by using code, the lookup behavior does not traverse from immediate resources to app resources; the lookup is self-contained to the ResourceDictionary instance that you are using at the time. Also, if you request a key that does not exist in the ResourceDictionary, there may not be an error; the return value may simply be provided as null. You may still get an error, though, if the specific property you were trying to set cannot accept null as a value. Note that this behavior contrasts with a StaticResource usage from XAML; a failure to resolve the provided key for StaticResource results in a XAML parse error, even in cases where the property could have accepted null. Merged resource dictionaries are included into the index scope of the primary resource dictionary that references the merged dictionary at run time. In other words, you can use Item or Lookup of the primary dictionary to find any objects that were actually defined in the merged dictionary. The logic mimics the general XAML lookup behavior: if there are multiple objects in merged dictionaries that each have the same key, the object from the last-added dictionary is returned.

You are permitted to add items to an existing ResourceDictionary by calling Add (C# or Visual Basic) or Insert (C++). You could add to either immediate resources or app resources. Either of these API calls requires a key, which satisfies the requirement that each item in a ResourceDictionary have a key. However, items that you add to a ResourceDictionary at run time are not relevant to StaticResource usages at all. The necessary lookup for StaticResource happens when XAML is being parsed as the app is loaded, and collections that are modified at run time are not available then.

You also can remove items from a ResourceDictionary at run time, make copies of some or all items, or other operations. The members listing for ResourceDictionary indicates which APIs are available. Note that because ResourceDictionary has a projected API, your API options differ depending on whether you are using C# or Visual Basic versus C++.

ResourceDictionary and localization

A XAML ResourceDictionary might initially contain strings that are to be localized. If so, store these strings as project resources instead of in a ResourceDictionary. Take the strings out of the XAML, and instead give the owning element an x:Uid value. Then define a resource in a resources file. Provide a resource name in the form XUIDValue.PropertyName and a resource value of the string that should be localized. For more info, see Quickstart: Translating UI resources.

Custom resource lookup

For advanced scenarios, you can implement a class that can have different behavior than the StaticResource lookup behavior described in this topic. To do this, you implement the class CustomXamlResourceLoader, and then you can access that behavior by using the CustomResource markup extension for resource references rather than using StaticResource. For more info, see CustomXamlResourceLoader.

Related topics

ResourceDictionary
StaticResource markup extension
Quickstart: Translating UI resources
Application resources and localization sample

 

 

Build date: 11/28/2012

Did you find this helpful?
(1500 characters remaining)
© 2013 Microsoft. All rights reserved.