다음을 통해 공유


Resource Dictionaries

Microsoft Silverlight will reach end of support after October 2021. Learn more.

A resource dictionary is a concept that is supported by the ResourceDictionary class. A resource dictionary is a keyed dictionary of objects that can be used both in XAML and in code. XAML is the most common usage, particularly for initially defining the object resources in a resource dictionary. Resource dictionaries can exist at several positions in an application structure, including as immediate (page) resources, application resources (as part of the Application object) or as XAML files that are kept separate in the application project structure. Resource dictionaries typically support two major Silverlight scenarios: defining templates for controls (as part of theme styles that are packaged with controls, or styles that retemplate an existing control), and defining storyboards for animated properties. This topic explains various aspects of ResourceDictionary concepts in Silverlight, and how to use the ResourceDictionary API.

This topic contains the following sections.

  • Objects For ResourceDictionary Usage
  • Keys and Resources
  • Immediate and Application Resources
  • Referencing Resources from XAML
  • Merged Resource Dictionaries
  • Forward References Within a ResourceDictionary
  • UserControl Usage Scope
  • Resource Dictionaries and XamlReader.Load
  • StaticResource in Templates
  • Referencing Resources from Code
  • Run-Time Usages
  • Reusing Resources
  • generic.xaml
  • ResourceDictionary Implementation Differences from WPF
  • Resource Dictionaries and Localization
  • ResourceDictionary for JavaScript API
  • Related Topics

Objects For ResourceDictionary Usage

Not all types and objects are suitable for ResourceDictionary usage in Silverlight. In order for an object to be defined in and accessed from a ResourceDictionary, the object must be able to be shared. Being shareable is required because when the object tree of an application is constructed and used at run time, objects cannot exist at multiple locations in the tree. To be used as a resource, an object from a resource dictionary must be supported by the Silverlight resource sharing behavior, or by an an indirection mechanism such as an object reference. Both of these mechanisms require extra code support. Any object that is derived from the UIElement type is inherently not shareable, unless it is generated from a control template (the Silverlight control templating subsystem is a prominent example of an indirection mechanism).

Silverlight ResourceDictionary sharing behavior support the following classifications of objects for shareable usage:

  • Styles and templates.

  • Brushes and colors.

  • Animation types including storyboards.

  • Transforms.

  • Matrix, Matrix3D, and Point structure values.

  • Certain other structures that have settable and constructible properties, such as Thickness and CornerRadius.

  • Custom types that are defined in your backing code, then instantiated in XAML as a resource. Examples of this are converters for resources, CLR objects for data sources, or IValueConverter implementations for data binding.

  • Strings and basic numeric values such as double and int. Note that object element usage in XAML for these system types requires that you map the System namespace and mscorlib assembly where the backing types for each CLR primitive are defined. The syntax for this is typically xmlns:sys="clr-namespace:System;assembly=mscorlib". For more information about XAML namespace mapping, see Silverlight XAML Namespaces, and Mapping XAML Namespaces as Prefixes.

Keys and Resources

With just a few exceptions, the items within a ResourceDictionary must each have a key defined. 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. In code, the key is provided as a parameter for methods such as ResourceDictionary.Add.

NoteNote:

In the Silverlight implementation of the resource dictionary concept, a ResourceDictionary can have x:Name instead of, or in addition to, an x:Key. If x:Key is not specified, x:Name is used as the key. x:Name / x:Key substitution supports a legacy resource dictionary usage that some applications might still be using. Some tools or development environments might give warnings about XAML that contains a ResourceDictionary that has items with x:Name but no x:Key. However, the x:Name / x:Key substitution will be valid for XAML parsing by the Silverlight runtime.

If you intend to use keyed resources from XAML markup, you generally map the XAML namespace for the XAML language itself, through an xmlns declaration. This is usually necessary because x:Name and x:Key are defined by the XAML language XAML namespace.

NoteNote:

The Silverlight documentation assumes the typical mapping prefix of x: for the XAML language features. Therefore, references to "Key" in the documentation are typically in the form x:Key, with the assumed x prefix prepended. Also, it is typical to map both the default Silverlight namespace and the XAML language XAML namespace at the root element level.

Resources in a Silverlight XAML ResourceDictionary must use strings for their key names. See XamlName Grammar for string value restrictions on key names. The value that is used for key names in a Silverlight ResourceDictionary must comply with this grammar.

If you include an item in a resource dictionary and the item does not have a usable key, a XAML parse exception occurs when the dictionary attempts to load. If you duplicate a key, a XAML parse exception occurs. Visual Studio and its XAML design surface can often provide design-time feedback if problems with resource definition or resource reference issues exist in your XAML. However, if the XAML designer cannot provide a warning, resource definition issues might instead be reported as exceptions when Silverlight attempts to load the XAML at run time.

Immediate and Application Resources

FrameworkElement.Resources and Application.Resources are two Silverlight properties that take values of type ResourceDictionary. FrameworkElement.Resources provides immediate resources. Immediate resources are also sometimes referred to in documentation as 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. Generally, you define the FrameworkElement.Resources value on the root element of a XAML page, and generally for a user application the root element for UI pages in XAML is UserControl. Therefore the common usage is to define all immediate resources that might be used by a page as elements within a property element tag for UserControl.Resources.

Application.Resources provides application-scoped resources. The resources that are defined by Application.Resources are available no matter what page or other UI is loaded as the current RootVisual of the application. Specifying resources at the application level might be useful if you are loading different possible pages into the RootVisual, and want a way to avoid duplicating the same resources in each possible page. Also, if you are writing values into a resource dictionary at run time, the application scope provides a location where those resources can persist for the application 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. For more information, see Creating a New Control by Creating a ControlTemplate.

NoteNote:

Do not confuse the concepts related to ResourceDictionary with the Resources build action, .resx files, and other "resources" that are discussed in the context of how you might structure the project that produces an application in MSBUILD or a development environment such as Visual Studio. Although the concept of resources for build actions and application structure can overlap with ResourceDictionary usages, the ResourceDictionary can generally be thought of as providing a self-contained resources system that incorporates XAML as its primary definition format.

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 through an attribute usage. For example, to set the value of the Background property of a Button to be a resource you define, you would declare the following 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 that defines application UI-->
<Button Background="{StaticResource fadeBrush}" .../>

In this example, the two pieces of XAML might not even be in the same XAML file. The ResourceDictionary might be defined in Resources or 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, the following is an 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>
NoteNote:

You cannot explicitly use a property element usage surrounding StaticResource as object element content. In Silverlight the StaticResource markup extension only supports usage as an attribute value (not as an object element). This is different from WPF, where XAML object elements for StaticResource are permitted as an alternative syntax.

Lookup Behavior for StaticResource

The lookup behavior for a 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.

The lookup sequence then checks the next object tree parent. 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, and so on. This sequence continues until the root element of the XAML is reached, exhausting the search of all possible immediate resource locations.

It is typical 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, then the next lookup behavior step is to check Application.Resources. Immediate resources are also sometimes referred to in this documentation as page-level resources. A typical XAML file that requests ResourceDictionary resources is also a XAML file where the root is the Silverlight UserControl that defines either the first RootVisual or subsequent "pages" for navigation.

If the requested key is still not found in any immediate or application-level resource dictionary, or as part of a control definition package for the control-specific generic.xaml, a XAML parse exception occurs. In certain circumstances, the XAML parse exception might be a run time exception that is not detected either by a XAML markup compile or a XAML design environment. For more information about this concept, see Code-Behind and Partial Classes.

Based on the resource dictionary lookup behavior, 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 element level where there is a FrameworkElement.Resources property to maintain key uniqueness. 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 yet obtain different results, depending on the scope from which the StaticResource reference was made.

Merged Resource Dictionaries

Silverlight supports merged resource dictionaries, a concept that is also supported in WPF XAML and its ResourceDictionary implementation. 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 FrameworkElement.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 location. You can use merged dictionaries for the FrameworkElement.Resources or Application.Resources; 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 is another ResourceDictionary declared as a XAML object element. But this ResourceDictionary should not have additional keyed resources as its own content. Instead, this ResourceDictionary should 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 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 lookup sequence, a MergedDictionaries dictionary is checked only after checking all the keyed resources of the ResourceDictionary that declared MergedDictionaries.

You can specify more than one ResourceDictionary within MergedDictionaries. Once 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 order that they are declared in the MergedDictionaries property. In other words, the resource lookup logic in the collection of merged resource dictionaries is last in, first out.

Within the scope of the ResourceDictionary, the dictionaries are checked for key uniqueness. However, that scope does not extend across MergedDictionaries boundaries. For example, you could define the same string key of muddyBrush for different resources if you declared an object with that key once in the outer ResourceDictionary, and then again within any or all of the resource dictionaries that the ResourceDictionary references as merged resource dictionaries. Within MergedDictionaries , if the key is found in the primary resource dictionary, the lookup stops there. Otherwise, each subsequently declared dictionary in MergedDictionaries is checked, with the last added merged dictionary checked first.

The same rules for lookup between primary and merged dictionaries apply for immediate resources (FrameworkElement.Resources). Also, if all lookups including the immediate merged dictionaries fail, then the lookup continues onward to application resources. Resources itself then can have merged resource dictionaries, with the same lookup behaviors for merged resource dictionaries applying (looks at the key values within the application-level resource dictionary itself first, then refers to any merged resource dictionaries in inverse order of their definition).

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 merged resource dictionary, and it can serve as the fallback value.

Remember that any value provided in a primary resource dictionary is always looked up before entering into the merged dictionaries, so if you want to use the fallback technique, do not define the resource in a primary resource dictionary.

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 exist 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 are intended for additional by-resource use are defined at, or near the start of, each respective resource dictionary.

References Between Application and Immediate Resources

Resources defined for the application cannot make references to immediate resources. This is equivalent to attempting a forward reference, because the application resources are actually processed first (prior to any RootVisual being loaded). However, any immediate resource can make a reference to an application resource, and this can be a useful technique for avoiding forward reference situations.

UserControl Usage Scope

A UserControl has a special situation for resource lookup behavior because a UserControl 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 (it cannot access application 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 an object tree loaded as a Silverlight RootVisual, and can access application resources.

Resource Dictionaries and XamlReader.Load

You can use a ResourceDictionary as either the root or a part of the XAML input for XamlReader.Load. 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.

StaticResource in Templates

Any StaticResource in a template is evaluated in the context of where the template is defined, instead of where the template is applied. Usually this means that a StaticResource reference in a template must come from other items that are defined lexically previous from it in the same ResourceDictionary or from Application.Resources.

Referencing Resources from Code

In code, you can reference a resource by using the indexer (Item). In this indexer case, because a ResourceDictionary is a string-keyed dictionary, the indexer uses the string key rather than an integer index. Usually a prerequisite to a code indexing call to a ResourceDictionary is to retrieve a specific ResourceDictionary (either an immediate ResourceDictionary somewhere in the object tree by getting FrameworkElement.Resources or the application ResourceDictionary by calling Application.Current) and then Application.Resources. When referencing resources through code, the lookup behavior does not traverse from immediate resources to application resources; the lookup behavior is self-contained to the specific ResourceDictionary instance that you called the indexer on. Also, if you request a key that does not exist in the ResourceDictionary, no exception is thrown; the return value is simply provided as null. You might still get an exception though, if the specific property you were trying to set with a properly cast Item[Object] return could not 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 would result in a XamlParseException, 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 the Item[Object] proprty of the primary dictionary to find any objects that were actually defined in the merged dictionary. The logic here mimics the lookup behavior; if there are multiple objects in merged dictionaries that each have the same key, the object from the last sequential added dictionary is the object returned by Item[Object].

NoteNote:

Silverlight does not implement the FindResource method that exists in the WPF framework API.

Run-Time Usages

You can declare a resource dictionary as a discrete XAML file, and load it at run time with Load and load-from-package APIs from application services. In this case, ResourceDictionary is declared as an object element, serving as the root element of the XAML. You must map the appropriate XML namespaces (default for Silverlight and x: for XAML) onto the ResourceDictionary element if you plan to use it as the root element. Then you can add object elements for items that define the resources, each with a key. However, if this ResourceDictionary is intended to provide resources called by StaticResource in XAML, you must attach the ResourceDictionary to an existing Resources property so that it participates in the lookup sequence (for example you could use Startup to set Application.Resources).

Another possible run-time usage is to add items to a ResourceDictionary by calling Add. You could add to either immediate resources or application resources. At run-time, there is little that distinguishes a ResourceDictionary versus a dictionary you could have created yourself, except that a ResourceDictionary is often populated from the initial XAML, and ResourceDictionary is convenient because you can obtain a ResourceDictionary from existing FrameworkElement.Resources / Application.Resources properties.

The run-time Add call requires a key, which satisfies the requirement that each item in a ResourceDictionary has a key. If you add items to a ResourceDictionary at run-time, you cannot provide a value for x:Name that is accessible to the XAML namescope of the main object tree because x:Name is only processed by an initial XAML load. Although you can first call XamlReader.Load to load XAML that includes the internal x:Name / Name attributes, and then add the created object tree as a resource, this does not make the run-time set Name findable. This is because Load always creates a discrete XAML namescope. The restriction on setting x:Name at runtime for a resource is typically not a problem; however, it can become an issue if you are trying to use storyboards to animate properties on objects that are added to resources at run-time and need a TargetName. For these cases, you can use a true PropertyPath to indirectly target the property by starting the property path from an object in the tree that does have an available name in the main XAML namescope. Or, you can use Storyboard.SetTarget for targeting instead.

Reusing Resources

So long as the resource's type supports a sharing or deferral mechanism, you can assign a value or object that is defined in a resource dictionary to more than one object's properties by making multiple references to the same keyed resource.

  • Templates and styles defined in a ResourceDictionary can always be shared and reused, because the mechanisms that apply templates or set styled properties are designed for that purpose.

  • Value types, such as strings, can be used more than one time. Any value type that is reused is just copied. (Note: Including strings and other System-defined value types in object element XAML usage so that they can be keyed requires mapping an XML namespace with prefix for System and mscorlib; see Silverlight XAML Namespaces, and Mapping XAML Namespaces as Prefixes).

  • The resource system also supports built-in sharing of certain objects if they are used more than one time. These objects are:

    • All subclasses of Brush including ImageBrush

    • All subclasses of Transform

    • FontFamily

    • Most (but not all) Silverlight-defined structures

    • User-defined classes, such as object data sources or service implementations.

Objects that go in a visual tree, such as any UIElement, generally cannot be retrieved from a resource dictionary more than one time. Attempting multiple retrievals generates a XAML parse exception if the resource is referenced by StaticResource. Attempting to reuse object definitions is a scenario for templates, not for a {StaticResource} reference from a resource dictionary. Another approach for object definition reuse is to use XAML input for XamlReader.Load but call the method multiple times on the same XAML input to return more than one object. To make this simpler, you can define the XAML input as a string resource, and then run Load(String) multiple times, referencing the string resource as input.

generic.xaml

generic.xaml is a special implementation and deployment technique used for controls that generally incorporates a ResourceDictionary. Inside generic.xaml only, both Name / x:Name and x:Key are optional on Style elements so long as the TargetType attribute is specified (the TargetType is implicitly used as the key). Another aspect that is unique to generic.xaml is that syntax that references the {x:Type} markup extension is supported when you are setting TargetType for styles and template resources. This is to support template compatibility and migration with WPF, where the {x:Type} markup extension is supported by a backing XAML markup extension. Outside of generic.xaml, the Silverlight XAML parser uses implicit conversion for any property of type Type, and explicit use of {x:Type} is not supported and generates a XAML parse error. For more information about generic.xaml, see Creating a New Control by Creating a ControlTemplate.

NoteNote:

{x:Type} does not truly exist in Silverlight; it is a special parser behavior to ignore the explicit usage in generic.xaml and to then process the inner string through a native Type conversion as Silverlight does normally.

ResourceDictionary Implementation Differences from WPF

  • As noted in the "Keys and Resources" section, a Silverlight ResourceDictionary can have x:Name instead of, or in addition to, an x:Key. WPF always requires x:Key except for certain explicit key cases related to styles and templates.

  • Silverlight does not support a FindResource API (not strictly a ResourceDictionary issue, but relevant to the overall usage).

  • Silverlight does not support {DynamicResource}. If you are migrating XAML from WPF, you can convert these to {StaticResource} and define them in a ResourceDictionary, or you must otherwise replace the references. Again, this is not strictly a ResourceDictionary issue.

  • In WPF, the base type for templates (FrameworkTemplate) also has a Resources property. The Silverlight version of FrameworkTemplate does not have a Resources property.

  • The Silverlight version of Style does not have a Resources property.

  • Silverlight XAML does not support x:Shared or x:Static.

  • Silverlight has a smaller range of shareable types than WPF, which is an implementation detail of how ResourceDictionary is implemented. Some types that are shareable in WPF, such as Geometry derived types, are not shareable in Silverlight.

  • Silverlight does not support referencing a UIElement as a resource (excepting applied templates, which is a different concept). WPF permits referencing a UIElement as a resource;  using a ResourceDictionary as a refactoring technique in WPF XAML is a valid scenario as long as you only insert that UIElement into the object tree once. Silverlight blocks even the first usage.

Resource Dictionaries and Localization

Resource dictionaries might contain strings that are intended to be localized, either as sys:String objects or as string values for attributes of specific objects. If you use a purely ResourceDictionary-based technique for localization of UI that is defined in XAML, this technique generally requires that each locale has a separate compile and XAP package. Therefore, you produce one XAP per locale for your application and handle the deployment distribution issues of these XAML files by specifying Source differently per-locale at the HTML or Silverlight initialization level.

There is an alternative recommended technique for dealing with localizable UI strings from XAML in your Silverlight-based application, which integrates well with the existing Visual Studio resx resources infrastructure and the project templates for Silverlight. This technique relies on creating a custom resources class for localizable strings, combined with the Silverlight data binding and resource dictionary features. For more information, see "Making XAML Localizable" section of Localizing Silverlight-based Applications.

ResourceDictionary for JavaScript API

ResourceDictionary can also be accessed by the JavaScript API for limited scenarios. This topic does not cover those scenarios. See ResourceDictionary.