代码改变世界

资源字典——Silverlight(转自MSDN)

2010-11-16 10:11  若水xp  阅读(4595)  评论(2编辑  收藏  举报

源字典

Silverlight

资源字典是 ResourceDictionary 类支持的一个概念。资源字典是一个键控对象字典,可在 XAML 和代码中使用。XAML 是最常见的用法,特别是用于最初定义资源字典中的对象。资源字典可存在于应用程序结构中的多个位置,包括作为直接(页)资源、应用程序资源(作为 Application 对象的一部分)或作为 XAML 文件(在应用程序项目结构中单独保留)。资源字典通常支持两个主要 Silverlight 方案:定义控件模板(作为样式的一部分)和定义用于动画处理属性的演示图板。

 

本主题包括下列各节。

适用于 ResourceDictionary 用法的对象

在 Silverlight 中,并非所有类型和对象都适合 ResourceDictionary 用法。为了在 ResourceDictionary 中定义某个对象并从其中访问该对象,该对象必须成为可共享的对象。可共享是必要的,因为当应用程序的对象树最终完成构造并在运行时使用时,对象不能存在于树中的多个位置。 必须或者存在一个真正的共享机制,或者存在一个引用之类的间接机制。 这两种机制都要求附加的代码支持。从 UIElement 类型派生的任何对象在本质上都不是可共享的,除非该对象是从控件模板生成的(Silverlight 控件模板化子系统是间接机制的一个突出示例)。

Silverlight ResourceDictionary 用法支持以下对象类别的可共享用法:

  • 样式和模板。

  • 画笔和颜色。

  • 包括演示图板的动画类型。

  • 转换。

  • Matrix Matrix3DPoint 结构值。

  • 某些具有可设置和可构造属性(例如 ThicknessCornerRadius)的其他结构。但是,这些结构通常要求在 XAML 中使用初始化文本以便声明有用值;有关更多信息,请参见参考主题中的“XAML 用法”部分。

  • 在支持代码中定义然后在 XAML 中实例化为资源的自定义类型,如资源的转换器。

  • 字符串和基本数值,如 doubleint。请注意,XAML 中对这些系统类型的对象元素用法要求您映射 System 命名空间和 mscorlib 程序集(其中基元定义后备类型)。相关语法通常为 xmlns:sys="clr-namespace:System;assembly=mscorlib"。有关 XAML 命名空间映射的更多信息,请参见 Silverlight XAML 命名空间以及将 XAML 命名空间映射为前缀

键和资源

ResourceDictionary 中的项必须各有一个定义的键。在 XAML 中,可通过为作为 ResourceDictionary 内容添加的对象元素的 x:Key 特性提供值来指派键。

注意 说明:

在资源字典概念的 Silverlight 实现中,ResourceDictionary 可以有 x:Name,而不是 x:Key��或者二者兼而有之。如果未指定 x:Key,则 x:Name 用作键。x:Name / x:Key 替换支持某些应用程序可能仍在使用的旧式资源字典用法。如果 XAML 包含的 ResourceDictionary 所包含的项具有 x:Name,但没有 x:Key,则某些工具或开发环境可能针对该 XAML 发出警告。但是,x:Name / x:Key 替换对于运行时 XAML 分析将是有效的。

如果要在 XAML 标记中使用键控资源,通常可以通过一个 xmlns 声明映射 XAML 语言本身的 XAML 命名空间。这通常是必需的,因为 x:Namex:Key 是由 XAML 语言 XAML 命名空间定义的。

注意 说明:

Silverlight 文档假定 XAML 固有的典型映射前缀为 x:。 因此,文档中对“Key”的引用通常采用 x:Key 格式,同时预置假定的 x 前缀。此外,通常还在根元素级别同时映射默认 Silverlight 命名空间和 XAML 语言 XAML 命名空间。

Silverlight XAML ResourceDictionary 中的资源必须使用字符串作为它们的键名。有关键名的字符串值限制,请参见 XamlName 语法。Silverlight 中用于键名的值必须符合此语法。

如果您包括的项在资源字典中不具有可用键,则会发生分析器异常。当您复制键时,就会发生分析器异常。通常,如果键、无法创建的对象元素或资源查找存在问题,则在代码编译期间将不会始终检测到这些问题,仅当 Silverlight 在运行时尝试加载 XAML 时才会将它们报告为异常。

直接资源和应用程序资源

接受 ResourceDictionary 类型的值的两个属性是:FrameworkElement.ResourcesApplication.ResourcesFrameworkElement.Resources 提供直接资源。在 XAML 中,可以从任何满足以下条件的元素引用 FrameworkElement.Resources 中的键控资源:连接到这些资源所在的对象树。通常,为 XAML 页的根元素定义 FrameworkElement.Resources 值,并且通常而言,对于用户应用程序来说,UI 页的根元素为 UserControl。因此,常见用法是定义可由页用作 UserControl.Resources 内的元素的所有直接资源。

Application.Resources 提供应用程序范围的资源。无论哪个页作为应用程序的当前 RootVisual 加载,由 Application.Resources 定义的资源都是可用的。如果将不同的可能页加载到 RootVisual,并且需要一种方法来避免复制各个可能页中相同的资源,这可能很重要。此外,如果要在运行时将值写入资源字典,应用程序作用域将提供一个可以在应用程序生存期中保存这些资源的位置。

注意 说明:

请不要将与 ResourceDictionary 有关的概念同 Resources 生成操作、.resx 文件以及在关于如何组织项目(在 MSBUILD 或 Visual Studio 等开发环境中生成应用程序)结构的上下文中讨论的其他"资源"相混淆。尽管生成操作和应用程序结构的资源概念可以与 ResourceDictionary 用法重叠,但是 ResourceDictionary 通常可被视为提供自包含的资源系统,该系统将 XAML 作为其主要定义格式合并进来。

从 XAML 中引用资源

在 XAML 中,可通过使用 StaticResource 标记扩展来引用来自 ResourceDictionary 的现有资源。若要使用标记扩展,应始终引用通过属性 (Attribute) 用法设置的属性 (Property)。例如,若要将 ButtonBackground 属性的值设置为您定义的资源,可声明以下 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}" .../>

在此示例中,XAML 的两个部分甚至可能不位于同一 XAML 文件中。ResourceDictionary 可能在 Resources 或打包的控件主题 (generic.xaml) 中定义。

即使您设置的属性通常要求 XAML 中的属性元素用法来指定其值,您也应使用属性语法来用于资源引用。例如,下面是等效的属性元素用法(如果以内联方式定义 LinearGradientBrush,而不是引用 ResourceDictionary 资源):

<Button>  <Button.Background>    <LinearGradientBrush>      <GradientStop Color="Red" Offset="0"/>      <GradientStop Color="Gray" Offset="1"/>    </LinearGradientBrush>  </Button.Background></Button>
注意 说明:

不能将 StaticResource 周围的属性元素用法显式用作对象元素内容。在 Silverlight 中,StaticResource 标记扩展仅支持用作属性值(而不支持用作对象元素)。

StaticResource 的查找行为

StaticResource 的查找行为是 XAML 的标记编译处理将首先检查应用实际用法的对象是否能够保存 FrameworkElement.Resources 值。如果能,将检查该 ResourceDictionary 中是否有该键所对应的项。此级别的查找并不重要,因为您通常不会在同一对象上定义并引用资源。接下来发生的情况更重要。查找过程将检查下一个对象树父级是否存在 FrameworkElement.Resources,如果存在,则检查是否存在具有指定键字符串的字典项。该过程一直继续,直到到达了 XAML 的根元素。该过程通常在页的根级别定义所有直接资源,无论是为了利用此资源查找行为还是作为一种标记样式。

如果在直接资源中找不到请求的资源,则下一个查找步骤是检查 Application.Resources。在本文档中,直接资源有时候还称作页级别资源。请求 ResourceDictionary 资源的典型 XAML 文件也是这样一种 XAML 文件:其中,根是 Silverlight UserControl,它定义第一个 RootVisual 或随后的“页”以便用于导航。

如果仍然找不到所请求的键,将发生 XAML 分析器异常。在某些情况下,该 XAML 分析器异常可能是 XAML 标记编译检测不到的运行时异常。有关此概念的更多信息,请参见代码隐藏和分部类

基于此查找行为,可以特意定义多个具有与键相同的字符串值的资源,只要每个这样的资源都在一个不同的元素级别定义,且在该级别有一个保持键唯一性的 FrameworkElement.Resources 属性。只有检索到的第一个这样的对象才会用于 StaticResource。可以使用此行为在对象树的各个不同级别检索同一个 StaticResource 键,但是会得到不同的结果。

合并资源字典

Silverlight 支持合并资源字典,这是在 WPF XAML 及其 ResourceDictionary 实现中也支持的一种概念。使用合并资源字典可以通过引用外部文件来声明资源字典的内容。合并资源字典用法修改资源字典的两个默认特征:查找序列和键唯一性要求。

若要声明合并资源字典,需要将 MergedDictionaries 属性的属性元素添加到现有 ResourceDictionary 位置(通常为 FrameworkElement.Resources,但有时为 Application.Resources)。您必须将 ResourceDictionary 显式声明为一个对象元素才能在其中使用属性元素。现有的 ResourceDictionary 可能具有其他键控资源以及 MergedDictionaries 属性元素。MergedDictionaries 的内容为声明为对象元素的另一个 ResourceDictionary。但是,此 ResourceDictionary 不应将更多的键控资源作为自己的内容,而应只声明一个属性:Source。您可以在 MergedDictionaries 内指定多个 ResourceDictionary

例如,下面的 XAML 定义包含一个键控资源的 ResourceDictionary 以及通过 URI 引用两个不同资源字典的 MergedDictionaries 集合:

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

就查找序列而言,只有在检查完声明了 MergedDictionariesResourceDictionary 的所有键控资源后,才会检查 MergedDictionaries 字典。然后,会按 MergedDictionaries 内的每个字典在 MergedDictionaries 属性中声明的反向顺序逐个检查这些字典。换句话说,就是合并资源字典集合中的检索逻辑是按后进先出的顺序。

ResourceDictionary 的范围内,要检查字典的键唯一性。但是,该范围不超出 MergedDictionaries 边界。例如,可以为不同资源定义同一字符串键 muddyBrush,条件是在 ResourceDictionary 外部声明一次该键,而同一 ResourceDictionary 以源的方式引用作为合并资源字典的一些资源字典,然后可在其中任何一个资源字典内再次声明该键。在 MergedDictionaries 内,如果在主资源字典中找到该键,则会在此处停止查找。否则,会检查 MergedDictionaries 中随后声明的每个字典,最先检查最后添加的合并字典。如果仍未找到,则对朝向页面 XAML 根方向的下一个 FrameworkElement.Resources 以及 Application.Resources 范围继续查找。

可以结合使用查找序列和跨合并字典范围不强制使用唯一键来创建 ResourceDictionary 资源的回退优先级序列。例如,可以将特定画笔颜色的用户首选项存储在该序列的最后一个合并资源字典中。但是,如果尚不存在任何用户首选项,则可在居前的合并资源字典中为 ResourceDictionary 资源定义相同的键字符串,并且该资源可用作回退资源。

ResourceDictionary 中的前向引用

特定资源字典内的静态资源引用必须引用已使用某个键定义的资源以及在引用资源之前在词法上必须存在的资源。静态资源引用无法解析前向引用。因此,如果您使用静态资源引用,则必须设计资源字典结构,以便将进一步逐个使用的资源在各相应资源字典的开头或附近定义。

应用程序资源和直接资源之间的引用

为应用程序定义的资源不能引用直接资源。这等效于尝试前向引用,因为应用程序资源实际上是最先处理的。但是,任何直接资源都可以引用应用程序资源,这对于避免前向引用是一个很有用的技术。

资源字典和 XamlReader.Load

可以将 ResourceDictionary 用作 XamlReader.Load 的 XAML 输入的根或一部分。也可以在该 XAML 中包含 StaticResource 引用,前提是所有这样的引用在 XAML 中都完全是自包含的。XamlReader.Load 在不了解任何其他 ResourceDictionary 对象(甚至 Application.Resources)的上下文中分析 XAML。

模板中的 StaticResource

模板中的任何 StaticResource 都在定义模板而不是应用模板的上下文中评估。通常,这意味着模板中的 StaticResource 引用必须来自同一个 ResourceDictionary 中以前在词法上定义的其他项或来自 Application.Resources

从代码中引用资源

在代码中,可以使用索引器 (Item) 来引用资源。在这个索引器示例中,因为 ResourceDictionary 是一个字符串键控字典,所以索引器使用字符串键,而非整数索引。通常,对 ResourceDictionary 的代码索引调用的先决条件是检索特定的 ResourceDictionary(或者是通过获取 FrameworkElement.Resources 来检索位于对象树中某处的直接 ResourceDictionary,或者是通过调用 Application.CurrentApplication.Resources 来检索应用程序 ResourceDictionary。)

注意 说明:

Silverlight 不实现存在于 WPF 框架 API 中的 FindResource 方法。

运行时用法

可以将资源字典声明为离散的 XAML 文件,然后使用 Load 以及应用程序服务中的从包加载 API 在运行时加载它。在这种情况下,ResourceDictionary 被声明为对象元素,充当 XAML 的根元素。如果您打算将 ResourceDictionary 元素用作根元素,则必须将相应的 XML 命名空间(对于 Silverlight 为默认值,对于 XAML 为 x:)映射到该元素。然后,可以为定义资源的项添加对象元素,每个项都有一个相应的键。但是,如果此 ResourceDictionary 用于提供由 XAML 中的 StaticResource 调用的资源,则必须将 ResourceDictionary 附加到现有 Resources 属性,这样它就可以加入查找序列(例如,可以使用 Startup 来设置 Application.Resources)。

另一个可能的运行时用法是通过调用 AddResourceDictionary 中添加项。可以向直接资源中添加,也可以向应用程序资源中添加。运行时,ResourceDictionary 与您自己创建的字典几乎没有什么区别,只是 ResourceDictionary 通常从初始 XAML 中填充,并且 ResourceDictionary 比较方便,因为您可以从现有的 FrameworkElement.Resources / Application.Resources 属性中获取 ResourceDictionary

运行时 Add 调用需要一个键,这满足 ResourceDictionary 中的每个项都有一个键的要求。如果在运行时将项目添加到 ResourceDictionary,则无法为可由主对象树的 XAML 命名范围访问的 x:Name 提供值,因为 XAML 命名范围仅可由初始 XAML 加载进行设置。尽管您可以先调用 XamlReader.Load 来加载包含内部 x:Name / Name 属性的 XAML,然后将创建的对象树作为资源添加,但是这并不生成可查找的运行时集 NameLoad 始终创建离散的 XAML 命名范围。对于在运行时设置 Name 的限制通常不是问题,但如果您尝试使用演示图板对在运行时添加到资源的对象上的属性进行动画处理,并且需要 TargetName,则此限制可能会成为问题。对于这些情况,可以使用真正的 PropertyPath,通过从树中在主 XAML 命名范围中有可用名称的对象启动属性路径,来间接地以属性为目标。

重用资源

在某些情况下,可以将在资源中定义的值赋给多个对象。

  • ResourceDictionary 中定义的模板和样式始终可以重用,因为应用模板或设置样式属性的机制是专为此目的而设计的。

  • 值类型(如字符串)可以使用多次。重用的任何值类型都仅仅被复制。(请注意,在对象元素 XAML 用法中包含字符串和其他系统定义的值类型(以便可以对其进行键控)要求针对 System 和 mscorlib 使用前缀来映射 XML 命名空间;请参见 Silverlight XAML 命名空间以及将 XAML 命名空间映射为前缀。)

  • 资源系统还支持某些对象的内置共享(如果这些对象使用多次)。这些对象如下:

进入可视化树的对象(如任何 UIElement)通常不能从一个资源字典中检索多次。如果该资源由 StaticResource 引用,尝试多次检索会生成 XAML 分析器异常。尝试重用对象定义是模板的一个方案,而不是资源字典中的一个 {StaticResource}。对象定义重用的另一个可能方法是将 XAML 输入用于 XamlReader.Load,但对相同的 XAML 输入多次调用此方法以返回多个对象。

generic.xaml

generic.xaml 是一种用于控件的特殊实现技术,通常合并 ResourceDictionary。如果指定了 TargetType 属性,则仅在 generic.xaml 中,Name / x:Namex:Key 对于 Style 元素都是可选的。generic.xaml 特有的另一个方面是:为样式和模板资源设置 TargetType 时支持引用 {x:Type} 标记扩展的语法。这是为了支持与 WPF 的兼容性和迁移功能,其中 {x:Type} 标记扩展是全局支持的。在 generic.xaml 外部,Silverlight XAML 分析器将隐式转换用于 Type 类型的任何属性,显式使用 {x:Type} 不受支持,会生成分析器错误。有关 generic.xaml 的更多信息,请参见通过创建 ControlTemplate 来创建新的控件

注意 说明:

{x:Type} 在 Silverlight 中并不真正存在;它是一个特殊的分析器行为,以忽略 generic.xaml 中的显式用法,并在 Silverlight 正常运行时通过本机 Type 转换处理内部字符串。

与 WPF 在 ResourceDictionary 实现上的差异

  • 如"键和资源"一节所述,Silverlight ResourceDictionary 可以有 x:Name,而不是 x:Key,或者二者兼而有之。除某些与样式和模板有关的显式键情况之外,WPF 始终需要 x:Key

  • Silverlight 不支持 FindResource API(不是严格意义上的 ResourceDictionary 问题,但是与总体用法有关)。

  • Silverlight 不支持 {DynamicResource}。如果要从 WPF 迁移 XAML,则可以将其转换为 {StaticResource},并在 ResourceDictionary 中定义它们,否则必须替换相关引用。同样,这也不是严格意义上的 ResourceDictionary 问题。

  • 在 WPF 中,模板的基类型 (FrameworkTemplate) 还具有一个 Resources 属性。Silverlight 版本的 FrameworkTemplate 不具有 Resources 属性。

  • Silverlight 版本的 Style 不具有 Resources 属性。

  • Silverlight XAML 不支持 x:Shared,也不支持 x:Static

  • Silverlight 具有比 WPF 范围更小的可共享类型,它是实现 ResourceDictionary 的方式的实现详细信息。WPF 中的某些可共享类型(如 Geometry 派生类型)在 Silverlight 中不可共享。

  • Silverlight 不支持引用 UIElement 作为资源(应用的模板除外,它的概念不同)。WPF 允许引用 UIElement 作为资源,在 WPF XAML 中使用 ResourceDictionary 作为一种重构技术是非常有效的方案,您只需要将该 UIElement 插入对象树一次。

资源字典和本地化

资源字典可以包含要本地化的字符串,这些字符串可以作为 sys:String 对象,也可以作为特定对象的特性字符串值。如果您使用完全基于 ResourceDictionary 的技术来本地化在 XAML 中定义的 UI,则此技术通常要求每个区域设置都具有单独的编译和 XAP。因此,要为应用程序的每个区域设置生成一个 XAP,并通过在 HTML 或 Silverlight 初始化级别为每个区域设置指定不同来处理这些 XAML 文件的部署分发问题。

在基于 Silverlight 的应用程序中,推荐使用另一种方法来处理 XAML 中的可本地化 UI 字符串,该方法可与用于 Silverlight 的现有 Visual Studio resx 资源基础结构及项目模板完美集成。此方法依赖于为可本地化字符串创建自定义资源类,并结合 Silverlight 数据绑定和资源字典功能。有关更多信息,请参见本地化基于 Silverlight 的应用程序中的"使 XAML 可本地化"部分。