Поделиться через


ResourceDictionary and XAML resource references

[This article is for Windows 8.x and Windows Phone 8.x developers writing Windows Runtime apps. If you’re developing for Windows 10, see the latest documentation]

XAML defines the UI for your app, and XAML also has a way to define resources in XAML. Resources are typically definitions of some object that you expect to use more than once. You specify a key for a XAML resource that acts like its name for future references. You can reference a resource throughout an app or from any XAML page within it. Windows Runtime XAML has a ResourceDictionary element where you define your resources. Then you can reference your resources by using a StaticResource markup extension or ThemeResource markup extension.

The XAML elements you might want to declare most often as XAML resources include Style, ControlTemplate, animation components, and Brush subclasses. Here we explain 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.We also explain resource dictionary advanced features such as MergedDictionaries and ThemeDictionaries.

Prerequisites

We assume that you understand XAML markup and have read the XAML overview.

XAML resources must be shareable

For an object to exist in a ResourceDictionary, that 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. Internally, the resource system creates copies of resource values to use in the object graph of your app when each XAML resource is requested.

A ResourceDictionary and Windows Runtime XAML in general supports these objects for shareable usage:

You can also use custom types as a shareable resource if you follow the necessary implementation patterns. You define such classes in your backing code (or in runtime components that you include) and then instantiate those classes in XAML as a resource. Examples are object data sources and IValueConverter implementations for data binding.

Custom types must have a default constructor, because that's what a XAML parser uses to instantiate a class. Custom types used as resources can't have the UIElement class in their inheritance, because a UIElement can never be shareable (it's always intended to represent exactly one UI element that exists at one position in the object graph of your runtime app).

Keys for resources

The items within a ResourceDictionary must each have a key defined. A ResourceDictionary really is a dictionary in a programming sense. You typically define keys for XAML resources using the x:Key attribute. There are all kinds of objects that can possibly be a XAML resource and these objects don't share any common Windows Runtime properties. So to make it possible to put this wide range of possible resources within a resource dictionary, the x:Key attribute is globally defined by the XAML language, and can be legally applied to any XAML object element that's found in a dictionary.

The x:Key becomes the dictionary item's key, and the other values in XAML define an object that is the dictionary item's value.

Resources for Windows Runtime XAML must use strings for their key names. Specifically, the string must follow the rules of XamlName grammar; see the "XamlName grammar" section of the x:Key attribute reference page.

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. 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.

Implicit keys for control templates and styles

Control templates and Style elements with a TargetType property are special cases where an x:Key attribute value isn't required for it to exist as a XAML resource. For these types, the key for the resource is implicit. The value of the implicit key is based on a string form of the TargetType value that's declared in the template or style, and that key is used by XAML control logic at run time as the implicit lookup for the style or template to apply to a particular control. For example, a Style that has TargetType="Button" can be the implicit style of all Button controls in your app, without requiring a key for the resource's definition or a StaticResource reference from each and every Button. For more info on implicit styles and how they work, see Quickstart: Control templates.

Storyboard resources

A Storyboard resource is another special case where a Storyboard can exist in a ResourceDictionary without an x:Key value, so long as it does have an x:Name value. The intention of putting a Storyboard in a ResourceDictionary isn't usually to be reusable, because the animations within are already targeting specific properties. Instead, a ResourceDictionary is just a convenient XAML container to put the various Storyboard elements within. Eventually, you'll refer to the Storyboard instances by name, and call their Begin methods. This is usually done from code-behind in response to events like Loaded or tied to user-initiated events. For more info, see Storyboarded animations.

Immediate and app resources

There are two properties for typical apps that contain the ResourceDictionary nodes where you will define XAML resources: FrameworkElement.Resources and Application.Resources.

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 XAML resources in FrameworkElement.Resources from any object that is connected to the same object tree. Basically that means from the same XAML page, because typically you define the FrameworkElement.Resources value on the root element of a XAML page, where all possible XAML elements within the page can find it.

Application.Resources provides app-level 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 to support navigation, and want to avoid duplicating the same resources in each page. Also, if you are adding key/value pairs (resources) to a ResourceDictionary at run time so that pages loaded later can find them, 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 your app package. 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 XAML resource from a ResourceDictionary by using either the StaticResource markup extension or the ThemeResource markup extension. In this documentation, these are referred to as XAML resource references. To use a XAML resource reference with the markup extensions, you always reference the property you are setting with an attribute usage.

Here's some example XAML. To set the value of the Background property of a Button to use a static resource named fadeBrush, you first have to declare the resource with a key, and then you refer to it by key.

<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 file, or in a merged dictionary file.

You use XAML attribute syntax to make a XAML resource reference, even if the property you are setting typically requires a property element usage in XAML. For example, here is an equivalent property element usage, which shows the LinearGradientBrush defined inline rather than as a ResourceDictionary resource.

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

Lookup behavior for XAML resource references

Lookup behavior is the term for how the XAML resources system tries to find a XAML resource. The lookup happens for a key that's referenced as a XAML resource reference from somewhere in the app's XAML. The resources system has predictable behavior for where it will check for existence of a resource based on scope. If a resource isn't found in the initial scope, the scope expands. The lookup behavior continues on throughout the locations and scopes that a XAML resource could possibly be defined by an app or by the system. If all possible resource lookup attempts fail, an error often results. It's usually possible to eliminate these errors during the development process.

The lookup behavior for XAML resource references 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 XAML resource references from nearly anywhere in XAML; you aren't limited to properties of 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. The search continues recursively upwards until the root element of the XAML is reached, exhausting the search of all possible immediate resource locations.

Note  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 convention of XAML 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. The theme dictionary might also be the control-specific theme dictionary for a templated custom control.

Finally, there is a resource lookup against platform resources. Platform resources include the control templates that are defined for each of the system UI themes, and which define the default appearance of all the controls that you use for UI in a Windows Runtime app. 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 lookup from XAML or code once the app has loaded. For example, the system theme resources include a resource named "SystemColorWindowTextColor" that provides a Color definition to match app text color to a system window's text color that comes from the operating system and user preferences. Other XAML styles for your app can refer to this style, or your code can get a resource lookup value (and cast it to Color in the example case).

For more info and for a list of the theme-specific and system resources that are available to a Windows Store app that uses XAML, see XAML theme resources reference.

If the requested key is still not found in any of these locations, a XAML parsing error/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 action, or by a XAML design environment.

Because of the tiered 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, although keys must be unique within any given ResourceDictionary, the uniqueness requirement does not extend to the lookup behavior sequence as a whole. During lookup, only the first such object that's successfully retrieved is used for the XAML resource reference, and then the lookup stops. You could use this behavior to request the same XAML resource by key at various positions within your app's XAML but get different resources back, depending on the scope from which the XAML resource reference was made and how that particular lookup behaves.

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 a Resources property. Merged resource dictionary usages change two characteristics of resource dictionaries: the lookup sequence, and the 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 (merging into Application.Resources is more typical).

You must explicitly declare ResourceDictionary as an object element in order to use the ResourceDictionary.MergedDictionary 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 dictionary XAML files.

<Application.Resources>
    <ResourceDictionary>
      <!--other resources can be here-->
      <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="rd1.xaml" />
        <ResourceDictionary Source="rd2.xaml" />
      </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
  </Application.Resources>

You can specify more than one ResourceDictionary within MergedDictionaries. In the resource-lookup sequence, a MergedDictionaries dictionary is checked only after a check of all the other keyed resources of that ResourceDictionary. After, searching that level, the lookup reaches the merged dictionaries, and 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" declared the same key, the key from "rd2.xaml" is used first because it's last in the MergedDictionaries set.

Within the scope of any one ResourceDictionary, the dictionary is checked for key uniqueness. However, that scope does not extend across different items in different MergedDictionaries files.

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

Theme dictionaries

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 the resource that it resolves to, but otherwise the composition of a control that uses the brush as a resource could be the same. To reproduce the theme-switching behavior in your own templates and styles, instead of using MergedDictionaries as the property to merge 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 within ThemeDictionaries must have an x:Key value. The value is a string that names the relevant theme—for example, "Default", "Light" 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.

Similar to merged dictionaries, it is legal for the same key to be defined multiple times across a set of theme dictionaries in the same ThemeDictionaries collection, as long as there is uniqueness within each ResourceDictionary 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 UI 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 changes dynamically, whenever ThemeResource markup extension is used to make the reference and the system detects a theme change. The lookup behavior done by the system 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, which parallel the templates that the Windows Runtime uses by default for its controls. Use a text editor or similar editors in your IDE to open the XAML files in \(Program Files)\Windows Kits\<version>\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. There's also a separate themeresources.xaml file for design that contains only the theme resources and extra templates, not the default control templates. The theme areas are duplicates of what you'd see in generic.xaml.

When you use XAML design tools to edit copies of styles and templates, the design tools extract sections from the XAML design resource dictionaries and place them as local copies of XAML dictionary elements that are part of your app and project.

For more info and for a list of the theme-specific and system resources that are available to a Windows Store app that uses XAML, see XAML theme resources reference.

Windows 8 behavior

Windows 8 did not support the ThemeResource markup extension, it is available starting with Windows 8.1. Also, Windows 8 did not support dynamically switching the theme-related resources for a Windows Runtime app. The app had to be restarted in order to pick up the theme change for the XAML templates and styles. This isn't a good user experience, so apps are strongly encouraged to recompile and target Windows 8.1 so that they can use styles with ThemeResource usages and can dynamically switch themes when the user does. Apps that were compiled for Windows 8 but running on Windows 8.1 continue to use the Windows 8 behavior.

Forward references within a ResourceDictionary

XAML 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 XAML resource reference. For this reason, if you use XAML resource references from within another resource, you must design your resource dictionary structure so that the resources that are used by other resources are defined first in a 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 (when the app first starts, and 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 XAML 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 XAML resource references in that XAML if all such references are completely self-contained in the XAML submitted for loading. XamlReader.Load parses the XAML in a context that is not aware of any other ResourceDictionary objects, not even Application.Resources. Also, don't use {ThemeResource} from within XAML submitted to XamlReader.Load.

Using a ResourceDictionary from code

Most of the scenarios for a ResourceDictionary are handled exclusively in XAML. You declare the ResourceDictionary container and the resources within as a XAML file or set of XAML nodes in a UI definition file. And then you use XAML resource references to request those resources from other parts of XAML. Still, there are certain scenarios where your app might want to adjust the contents of a ResourceDictionary using code that executes while the app is running, or at least to query the contents of a ResourceDictionary to see if a resource is already defined. 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.

In C# or Microsoft Visual Basic code, you can reference a resource in a given 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 Visual C++ component extensions (C++/CX) code, use Lookup.

When using code to examine or change a ResourceDictionary, the behavior for APIs like Lookup or Item does not traverse from immediate resources to app resources, that's a XAML parser behavior that only happens as XAML pages are loaded. At run time, scope for keys is self-contained to the ResourceDictionary instance that you are using at the time. However, that scope does extend into MergedDictionaries.

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 you try to use the returned null as a value. The error would come from the property's setter, not your ResourceDictionary call. The only way you'd avoid an error is if the property accepted null as a valid value. Note how this behavior contrasts with XAML lookup behavior at XAML parse time; a failure to resolve the provided key from XAML at parse time 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. In this case the lookup behavior does resemble the parse-time 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++/CX). 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 XAML resource references. The necessary lookup for XAML resource references happens when that XAML is first parsed as the app is loaded (or a theme change is detected). Resources added to collections at run time weren't available then, and altering the ResourceDictionary doesn't invalidate an already retrieved resource from it even if you change the value of that resource.

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 to support its underlying collection interfaces, your API options differ depending on whether you are using C# or Visual Basic versus C++/CX.

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.

Resource loading optimization in Windows 8.1

Starting with Windows 8.1, there's a resource loading optimization that's enabled by the app model and the Windows Runtime XAML parser. For Windows 8, the XAML parser loaded resources from app.xaml and created each of them as objects as part of startup. That wasn't very efficient if there were big dictionaries there. Also, those resources included the items as defined for three themes, and only the current theme is really needed. Starting with Windows 8.1, the XAML parser only creates the resources when they're specifically requested by XAML resource references. Those might come from other resources or from page-level XAML as each page is loaded. The optimized XAML parser behavior minimizes the time it takes to read the app-level dictionary at startup time, and enables the first page for your app to load faster in most cases. Resources needed by inactive themes are only loaded if that theme is activated by the user. Switching themes while apps are running isn't something that users commonly do. But, if that happens, any resource where the ThemeResource markup extension was used for the request is recalculated based on the newly active theme.

Windows 8 behavior

Windows 8 didn't have the optimizations described above. Because of this you might see some differences in timing when you retarget your app for Windows 8.1. The app should be loading faster, however it may not be possible to isolate this improvement versus other changes you've made to your app code as part of retargeting. Some of the places where you might see evidence of timing changes due to optimized resource loading include when the constructors are called by the parser, for objects like Application objects, converters, or other custom classes. Apps that were compiled for Windows 8 but running on Windows 8.1 continue to use the Windows 8 behavior.

Custom resource lookup

For advanced scenarios, you can implement a class that can have different behavior than the XAML resource reference 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 or ThemeResource. Most apps won't have scenarios that require this. For more info, see CustomXamlResourceLoader.

ResourceDictionary

XAML overview

StaticResource markup extension

ThemeResource markup extension

XAML theme resources reference

Quickstart: Translating UI resources

Quickstart: Styling controls

Application resources and localization sample

x:Key attribute