<!-- Dictionary1.xaml -->
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MSDNSample">
<SolidColorBrush x:Key="brush" Color="Red"/>
</ResourceDictionary>
<!-- Dictionary2.xaml -->
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MSDNSample">
<SolidColorBrush x:Key="brush" Color="blue"/>
</ResourceDictionary>
在此示例中,将 TextBlock 的前景设置为当前主题中的值。
x:Class="MSDNSample.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Page.Resources>
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary Source="Dictionary1.xaml" x:Key="Light"/>
<ResourceDictionary Source="Dictionary2.xaml" x:Key="Dark"/>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</Page.Resources>
<TextBlock Foreground="{StaticResource brush}" Text="hello world" VerticalAlignment="Center"/>
</Page>
对于主题字典,每当使用 ThemeResource 标记扩展进行引用并且系统检测到主题更改时,要用于资源查找的活动字典都会动态更改。 系统执行的查找行为基于将活动主题映射到特定主题字典的 x:Key 操作。
检查主题字典在默认 XAML 设计资源中的构建方式十分有用,这些资源与 Windows 运行时默认用作其控件的模板相对应。 使用文本编辑器或 IDE 打开 \(Program Files)\Windows Kits\10\DesignTime\CommonConfiguration\Neutral\UAP\<SDK version>\Generic using a text editor or your IDE。 请注意主题字典首先在 generic.xaml 中的定义方式,以及每个主题字典定义相同键的方式。 然后每个这样的键由构成各种键控元素的元素所引用,这些键控元素位于主题字典外部并且稍后在 XAML 中定义。 还存在用于设计的单独 themeresources.xaml 文件,该文件仅包含主题资源和额外模板,不包含默认控件模板。 这些主题区域是你将在 generic.xaml 中看到的内容副本。
当你使用 XAML 设计工具以编辑样式和模板的副本时,设计工具会提取 XAML 设计资源词典中的片段并将其作为应用和项目一部分的 XAML 字典元素的本地副本放置。
有关可用于应用的特定于主题的资源和系统资源的详细信息和列表,请参阅 XAML 主题资源。
针对 XAML 资源引用的查找行为
查找行为是描述 XAML 资源系统如何尝试查找 XAML 资源的术语。 当键从应用的 XAML 的某处被引用为 XAML 资源引用时,即发生查找行为。 首先,资源系统具有可预测行为,它将为此类行为根据作用域检查是否存在资源。 如果在初始作用域中未找到资源,该作用域将展开。 将在应用或系统可能定义了 XAML 资源的位置和作用域上继续查找行为。 如果所有可能的资源查找尝试均失败,通常会导致错误。 通常可以在开发过程中消除这些错误。
针对 XAML 资源引用的查找行为首先从应用实际用法的对象以及它自己的 Resources 属性开始。 如果此处存在 ResourceDictionary,则会检查该“ResourceDictionary”以查看其中是否存在具有所请求键的项 。 该第一级查找很少包含相关信息,因为你通常不会在同一个对象上定义某个资源,然后引用该资源。 实际上,Resources 属性通常不存在于此处。 你几乎可以在 XAML 中的任何位置进行 XAML 资源引用,而不限于在 FrameworkElement 子类的属性中。
查找序列随后检查应用的运行时对象树中的下一个父对象。 如果存在一个 FrameworkElement.Resources 并且它持有一个 ResourceDictionary,将请求具有指定键字符串的字典项。 如果找到资源,查找序列就会停止,并将该对象提供到执行引用的位置。 否则,查找行为会向对象树根方向前进到下一个父级对象。 此搜索会继续递归前进,直至到达 XAML 的根元素,从而完成对所有可能直接资源位置的搜索。
一种常见的做法是在页面的根级别定义所有直接资源,以利用该资源查找行为并将其用作 XAML 标记样式的一个惯例。
如果未在直接资源中找到所请求的资源,下一个查找步骤是检查 Application.Resources 属性。 Application.Resources 是放置由应用导航结构中多个页面引用的任何应用特定资源的最佳位置。
添加到 ResourceDictionary 的资源的顺序会影响它们的应用顺序。 XamlControlsResources
字典会重写许多默认的资源键,因此应先将其添加到 Application.Resources
,使之不会在应用中重写任何其他自定义样式或资源。
在引用查找中,控件模板有另一个可能的位置:主题字典。 主题字典是单个 XAML 文件,它具有一个 ResourceDictionary 元素作为其根。 主题字典可能是来自 Application.Resources 的一个合并字典。 主题字典也可能是一个特定于控件的主题字典,用于模板化的自定义控件。
最后,还有一种针对平台资源的资源查找。 平台资源包括为每个系统 UI 主题定义的控件模板,以及用于定义你在 Windows 运行时应用中为所有 UI 控件使用的默认外观的控件模板。 平台资源还包括与系统范围内的外观和主题相关的一组已命名资源。 从技术上来说,这些资源是 MergedDictionaries 项,因此在加载应用之后可用于从 XAML 或代码中查找。 例如,系统主题资源包括一个名为 “SystemColorWindowTextColor”的资源,该资源提供一个 Color 定义以使应用文本颜色与系统窗口的文本颜色(来自操作系统和用户首选项)相匹配。 应用的其他 XAML 样式可以引用该样式,或者你的代码可以获取资源查找值(并将该值转换为示例中的 Color)。
有关特定于主题的资源和系统资源(可用于使用 XAML 的 Windows 应用)的详细信息和列表,请参阅 XAML 主题资源。
如果请求的键仍未在这其中任何位置中找到,会发生 XAML 分析错误/异常。 某些情况下,XAML 分析异常可能是 XAML 标记编译操作或 XAML 设计环境未检测到的运行时异常。
根据针对资源字典的分层查找行为,你可特意定义多个资源项,每个资源项使用相同的字符串值作为键,只要将每个资源定义在不同的级别上即可。 换言之,尽管键在任何给定的 ResourceDictionary 中必须是唯一的,但该唯一性要求不会扩展到整个查找行为序列。 在查找过程中,只有成功检索到的首个此类对象会用于 XAML 资源引用,随即查找便会停止。 你可以利用此行为在应用 XAML 内的各个位置通过键请求相同的 XAML 资源,但会取回不同的资源,具体取决于进行 XAML 资源引用的范围以及特殊查找的行为方式。
ResourceDictionary 中的前向引用
一个特定资源字典中的 XAML 资源引用必须引用定义了键的资源,并且从字典顺序来讲,该资源必须出现在资源引用之前。 XAML 资源引用无法解析前向引用。 出于此原因,如果你从另一个资源内使用 XAML 资源引用,则必须设计资源字典结构,以便使其他资源使用的资源在资源字典中首先定义。
在应用级别定义的资源不能引用直接资源。 这相当于尝试前向引用,因为应用资源实际上最先被处理(在应用首次启动和加载任意导航页内容之前)。 但是,任何直接资源都可引用应用资源,这可以作为避免前向引用情形的一种有用技术。
XAML 资源必须可共享
要使对象可存在于 ResourceDictionary 中,该对象必须可共享 。
可共享是必要的,因为在构造以及在运行时使用应用的对象树时,对象不能存在于树中的多个位置。 就内部而言,在请求所有 XAML 资源时,资源系统会为在资源的对象图中使用的资源值创建副本。
通常,ResourceDictionary 和 Windows 运行时 XAML 支持将下列对象用于共享:
样式和模板(从 FrameworkTemplate 派生的 Style 和类)
画笔和颜色(从 Brush 派生的类和 Color 值)
动画类型,包括 Storyboard
转换(从 GeneralTransform 派生的类)
Matrix 和 Matrix3D
Point 值
其他与 UI 相关的某些结构,例如 Thickness 和 CornerRadius
XAML 固有数据类型
此外,如果遵循必要的实现模式,则可将自定义类型用作可共享资源。 在支持代码(或所含运行时组件)中定义这些类,然后在 XAML 中将其实例化为资源。 示例包括对象数据源和用于数据绑定的 IValueConverter 实现。
自定义类型必须包含默认构造函数,因为 XAML 分析程序须借助该构造函数对类进行实例化。 用作资源的自定义类型不能在其继承中包含 UIElement 类,因为“UIElement”无法共享(而只能一直用于表示运行时应用的对象图中某一位置中存在的一个 UI 元素) 。
UserControl 使用范围
对于资源查找行为,UserControl 元素有一个特殊情况,因为它有一个定义范围和使用范围的固有概念。 从其定义范围内进行 XAML 资源引用的 UserControl 必须能够支持在其自己的定义范围查找序列中查找该资源,也就是说,它不能访问应用资源。 在 UserControl 使用范围中,将资源引用视为在朝向其使用页面根方向的查询序列内(就像从加载的对象树中的一个对象执行的任何其他资源引用一样),并且可访问应用资源。
ResourceDictionary 和 XamlReader.Load
你可以将 ResourceDictionary 用作根或用作 XamlReader.Load 方法的 XAML 输入的一部分。 你还可以在该 XAML 中包含 XAML 资源引用(如果所有此类引用都完全自包含在为了加载而提交的 XAML 中)。 “XamlReader.Load”在不了解任何其他“ResourceDictionary”对象(甚至不了解 Application.Resources)的上下文中分析 XAML 。 此外,请不要从 XAML 内部使用提交到 XamlReader.Load 的 {ThemeResource}
。
通过代码使用 ResourceDictionary
大部分情况下,ResourceDictionary 均在 XAML 中专门处理。 你要在 UI 定义文件中将内部的 ResourceDictionary 容器和资源声明为 XAML 文件或 XAML 节点集。 然后,使用 XAML 资源引用从 XAML 的其他部分请求这些资源。 不过,在某些特定情况下,你的应用可能需要使用在应用运行时执行的代码来调整 ResourceDictionary 的内容,或者至少需要查询 ResourceDictionary 的内容以查看是否已定义某个资源。 这些代码调用在“ResourceDictionary”实例上执行,所以必须首先通过获得 FrameworkElement.Resources 来检索对象树中的即时 ResourceDictionary,或者检索 Application.Current.Resources
。
在 C# 或 Microsoft Visual Basic 代码中,你可以使用索引器 (Item) 引用给定 ResourceDictionary 中的资源。 ResourceDictionary 是一个字符串键控字典,因此索引器使用字符串键,而不使用整数索引。 在 Visual C++ 组件扩展 (C++/CX) 代码中,请使用 Lookup。
当使用代码检查或更改 ResourceDictionary 时,API 的行为(如 Lookup 或 Item)不会从直接资源遍历到应用资源;这是仅在加载 XAML 页面时发生的 XAML 分析程序行为。 在运行时,键作用域将自包含到此时所使用的 ResourceDictionary 实例中。 但是,该作用域不会扩展到 MergedDictionaries 中。
另外,如果你请求的键不在 ResourceDictionary 中,可能不会出现错误;但返回值可能只会提供为“null” 。 但是,如果你尝试将返回的 null 用作值,仍然可能会出现错误。 错误可能来自于属性的设置器,而不是你的 ResourceDictionary 调用。 避免错误的唯一方法是属性接受 null 作为有效值。 请注意此行为与 XAML 解析期间的 XAML 查找行为有何区别;如果在解析期间无法解析从 XAML 提供的键,会导致 XAML 解析错误,甚至在属性可以接受 null 时也是如此。
合并的资源字典包含在主要资源字典的索引范围内,该字典在运行时引用合并字典。 换句话说,可以使用主要字典的“Item”或 Lookup 查找在合并字典中实际定义的任何对象 。 在这种情况下,该查找行为类似于分析期间的 XAML 查找行为:如果合并字典中存在多个具有相同键的对象,将返回最后添加的字典中的对象。
你可以通过调用 Add(C# 或 Visual Basic)或 Insert (C++/CX) 向现有的 ResourceDictionary 添加项。 你可以将这些项添加到直接资源或应用资源。 这些 API 调用中的每一个都需要一个键,这满足 ResourceDictionary 中的每个项都必须有一个键的要求。 但是,你在运行时添加到 ResourceDictionary 的项与 XAML 资源引用无关。 在加载应用后首次分析 XAML 时,将执行必要的 XAML 资源引用查找。 此时,在运行时添加到集合的资源不可用,而更改 ResourceDictionary 并不会使已从中检索到的资源失效,即使更改该资源的值也是如此。
你还可以在运行时从某个 ResourceDictionary 删除项、复制部分或所有项,或执行其他操作。 ResourceDictionary 的成员列表指出了哪些 API 可用。 请注意,由于 ResourceDictionary 有一个投影的 API 可支持其基本集合接口,因此你的 API 选项会有所不同,具体取决于你使用的是 C# 或 Visual Basic 还是 C++/CX。
ResourceDictionary 和本地化
XAML ResourceDictionary 最初可能包含要本地化的字符串。 如果是这样,将这些字符串存储为项目资源,而不存储在 ResourceDictionary 中。 将字符串放在 XAML 外部,而为拥有元素提供一个 x:Uid 指令值。 然后,在资源文件中定义一个资源。 以 XUIDValue.PropertyName 的形式提供资源名称,并提供应该本地化的字符串的资源值。
自定义资源查找
对于高级方案,你可以实现其行为不同于本主题中描述的 XAML 资源引用查找行为的类。 要达到此目的,你可实现类 CustomXamlResourceLoader,然后可以通过使用 CustomResource 标记扩展进行资源引用(而不是使用 StaticResource 或 ThemeResource)来实现该行为。 大部分应用的方案将不会需要此操作。 有关详细信息,请参阅 CustomXamlResourceLoader。
ResourceDictionary
XAML概述
StaticResource 标记扩展
ThemeResource 标记扩展
XAML 主题资源
设置控件样式
x:Key 特性