“ResourceDictionary” 和 “XAML” 资源引用

可以使用 XAML 为应用定义 UI 或资源。 资源通常是预期多次使用的某些对象的定义。 若要稍后引用 XAML 资源,请为充当其名称的资源指定密钥。 可以在应用内或应用内的任何 XAML 页面中引用资源。 可以使用 Windows 运行时 XAML 中的 ResourceDictionary 元素定义资源。 然后,可以使用 StaticResource 标记扩展ThemeResource 标记扩展来引用资源。

你可能希望最常声明为 XAML 资源的 XAML 元素包括 StyleControlTemplate、动画组件和 Brush 子类。 在这里,我们将介绍如何定义 ResourceDictionary 和带键资源,以及 XAML 资源如何与作为应用或应用包一部分定义的其他资源相关联。 我们还介绍了资源字典的高级功能,例如:MergedDictionariesThemeDictionaries

先决条件

深入了解 XAML 标记。 我们建议阅读 XAML 概述

定义和使用 XAML 资源

XAML 资源是从 XAML 标记中多次引用的对象。 资源在 ResourceDictionary 中定义,通常位于单独的文件或标记页顶部,如下所示。

<Page
    x:Class="MSDNSample.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <Page.Resources>
        <x:String x:Key="greeting">Hello world</x:String>
        <x:String x:Key="goodbye">Goodbye world</x:String>
    </Page.Resources>

    <TextBlock Text="{StaticResource greeting}" Foreground="Gray" VerticalAlignment="Center"/>
</Page>

在本示例中:

  • <Page.Resources>…</Page.Resources> - 定义资源字典。
  • <x:String> - 使用键“greeting”定义资源。
  • {StaticResource greeting} - 使用键“greeting”查找资源,该资源被分配给 TextBlockText 属性。

注意 不要将与 ResourceDictionary 相关的概念与 生成操作、资源(.resw)文件或其他“资源”(在构建生成应用包的代码项目上下文中讨论)混淆。

资源不必是字符串;它们可以是任何可共享对象,例如样式、模板、画笔和颜色。 但是,控件、形状和其他 FrameworkElement不可共享,因此无法将其声明为可重用资源。 有关共享的详细信息,请参阅本主题后面的 XAML 资源必须可共享 部分。

在这里,画笔和字符串都声明为资源,并由页面中的控件使用。

<Page
    x:Class="SpiderMSDN.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <Page.Resources>
        <SolidColorBrush x:Key="myFavoriteColor" Color="green"/>
        <x:String x:Key="greeting">Hello world</x:String>
    </Page.Resources>

    <TextBlock Foreground="{StaticResource myFavoriteColor}" Text="{StaticResource greeting}" VerticalAlignment="Top"/>
    <Button Foreground="{StaticResource myFavoriteColor}" Content="{StaticResource greeting}" VerticalAlignment="Center"/>
</Page>

所有资源都需要有一个密钥。 通常,该键是使用 x:Key="myString". 定义的字符串。 但是,还有其他几种指定键的方法:

  • StyleControlTemplate 需要 TargetType,如果未指定 x:Key,则使用 TargetType 作为键。 在这种情况下,键是实际的 Type 对象,而不是字符串。 (请参阅以下示例)
  • 如果未指定 x:Key,具有 TargetTypeDataTemplate 资源将使用 TargetType 作为键。 在这种情况下,键是实际的 Type 对象,而不是字符串。
  • 可以使用 x:Name 而不是 x:Key。 但是,x:Name 还会为资源生成后台代码字段。 因此,x:Name 的效率低于 x:Key,因为加载页面时需要初始化该字段。

StaticResource 标记扩展只能使用字符串名称(x:Keyx:Name)检索资源。 有时,XAML 框架在决定未设置 StyleContentTemplateItemTemplate 属性的控件应使用哪种样式和模板时,也会查找隐式样式资源(这些资源使用 TargetType 而不是 x:Key 或 x:Name)。

此处, Style 具有 typeof(Button)的隐式键,并且由于页面底部的 Button 未指定 Style 属性,因此它会查找具有 typeof(Button)键的样式:

<Page
    x:Class="MSDNSample.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <Page.Resources>
        <Style TargetType="Button">
            <Setter Property="Background" Value="Red"/>
        </Style>
    </Page.Resources>
    <Grid>
       <!-- This button will have a red background. -->
       <Button Content="Button" Height="100" VerticalAlignment="Center" Width="100"/>
    </Grid>
</Page>

有关隐式样式及其工作原理的更多信息,请参阅 控件样式控件模板

在代码中查找资源

你可以像访问任何其他字典一样访问资源字典的成员。

警告

在代码中执行资源查找时,只会查看字典中的 Page.Resources 资源。 与 StaticResource 标记扩展不同,如果未在第一个字典中找到资源,则代码不会回退到 Application.Resources 字典。

 

此示例演示如何从页面的资源字典中检索 redButtonStyle 资源:

<Page
    x:Class="MSDNSample.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <Page.Resources>
        <Style TargetType="Button" x:Key="redButtonStyle">
            <Setter Property="Background" Value="red"/>
        </Style>
    </Page.Resources>
</Page>
    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();
            Style redButtonStyle = (Style)this.Resources["redButtonStyle"];
        }
    }
    MainPage::MainPage()
    {
        InitializeComponent();
        Windows::UI::Xaml::Style style = Resources().TryLookup(winrt::box_value(L"redButtonStyle")).as<Windows::UI::Xaml::Style>();
    }

若要从代码中查找应用范围资源,请使用 Application.Current.Resources 获取应用的资源字典,如下所示。

<Application
    x:Class="MSDNSample.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:SpiderMSDN">
    <Application.Resources>
        <Style TargetType="Button" x:Key="appButtonStyle">
            <Setter Property="Background" Value="red"/>
        </Style>
    </Application.Resources>

</Application>
    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();
            Style appButtonStyle = (Style)Application.Current.Resources["appButtonStyle"];
        }
    }
    MainPage::MainPage()
    {
        InitializeComponent();
        Windows::UI::Xaml::Style style = Application::Current().Resources()
                                                               .TryLookup(winrt::box_value(L"appButtonStyle"))
                                                               .as<Windows::UI::Xaml::Style>();
    }

还可以在代码中添加应用程序资源。

执行此作时,需要记住两点。

  • 首先,需要在任何页面尝试使用该资源之前添加资源。
  • 其次,不能在应用的构造函数中添加资源。

如果在 Application.OnLaunched 方法中添加资源,可以避免这两个问题,如下所示。

// App.xaml.cs
    
sealed partial class App : Application
{
    protected override void OnLaunched(LaunchActivatedEventArgs e)
    {
        Frame rootFrame = Window.Current.Content as Frame;
        if (rootFrame == null)
        {
            SolidColorBrush brush = new SolidColorBrush(Windows.UI.Color.FromArgb(255, 0, 255, 0)); // green
            this.Resources["brush"] = brush;
            // … Other code that VS generates for you …
        }
    }
}
// App.cpp

void App::OnLaunched(LaunchActivatedEventArgs const& e)
{
    Frame rootFrame{ nullptr };
    auto content = Window::Current().Content();
    if (content)
    {
        rootFrame = content.try_as<Frame>();
    }

    // Do not repeat app initialization when the Window already has content,
    // just ensure that the window is active
    if (rootFrame == nullptr)
    {
        Windows::UI::Xaml::Media::SolidColorBrush brush{ Windows::UI::ColorHelper::FromArgb(255, 0, 255, 0) };
        Resources().Insert(winrt::box_value(L"brush"), winrt::box_value(brush));
        // … Other code that VS generates for you …

每个 FrameworkElement 都可以有 ResourceDictionary

FrameworkElement 是一个供控件继承的基类,并且拥有 Resources 属性。 因此,可以将本地资源字典添加到任何 FrameworkElement

在这里,页面边框 都有各自的资源字典,它们都有一个名为“greeting”的资源。 名为“ textBlock2”的 TextBlock 位于 边框内,因此其资源查找首先查找 边框的资源,然后是 页面的资源,然后是 应用程序 资源。 TextBlock 将显示“Hola mundo”。

若要从代码访问该元素的资源,请使用该元素的 Resources 属性。 在代码中访问 FrameworkElement的资源(而不是在 XAML 中访问)时,只会在该字典中查找,而不会在父元素的字典中查找。

<Page
    x:Class="MSDNSample.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Page.Resources>
        <x:String x:Key="greeting">Hello world</x:String>
    </Page.Resources>
    
    <StackPanel>
        <!-- Displays "Hello world" -->
        <TextBlock x:Name="textBlock1" Text="{StaticResource greeting}"/>

        <Border x:Name="border">
            <Border.Resources>
                <x:String x:Key="greeting">Hola mundo</x:String>
            </Border.Resources>
            <!-- Displays "Hola mundo" -->
            <TextBlock x:Name="textBlock2" Text="{StaticResource greeting}"/>
        </Border>

        <!-- Displays "Hola mundo", set in code. -->
        <TextBlock x:Name="textBlock3"/>
    </StackPanel>
</Page>

    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();
            textBlock3.Text = (string)border.Resources["greeting"];
        }
    }
    MainPage::MainPage()
    {
        InitializeComponent();
        textBlock3().Text(unbox_value<hstring>(border().Resources().TryLookup(winrt::box_value(L"greeting"))));
    }

合并的资源字典

合并的资源字典 会将一个资源字典整合到另一个资源字典中,通常是在不同的文件中。

提示 在 Microsoft Visual Studio 中,可以使用“>”菜单中的“添加 > 新项...”选择“ 资源字典”来创建资源字典文件。

在这里,你将在名为 Dictionary1.xaml 的单独 XAML 文件中定义资源字典。

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

若要使用该字典,请将其与页面的字典合并:

<Page
    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.MergedDictionaries>
                <ResourceDictionary Source="Dictionary1.xaml"/>
            </ResourceDictionary.MergedDictionaries>

            <x:String x:Key="greeting">Hello world</x:String>

        </ResourceDictionary>
    </Page.Resources>

    <TextBlock Foreground="{StaticResource brush}" Text="{StaticResource greeting}" VerticalAlignment="Center"/>
</Page>

下面是此示例中发生的情况。 在 <Page.Resources>中,声明 <ResourceDictionary>。 将资源添加到 <Page.Resources>时,XAML 框架会隐式创建资源字典;但是,在这种情况下,你不需要任何资源字典,而需要包含合并字典的资源字典。

因此,声明 <ResourceDictionary>,然后将内容添加到其 <ResourceDictionary.MergedDictionaries> 集合中。 每个条目都采用 <ResourceDictionary Source="Dictionary1.xaml"/>形式。 若要添加多个字典,只需在第一个条目后添加一个 <ResourceDictionary Source="Dictionary2.xaml"/> 条目。

之后 <ResourceDictionary.MergedDictionaries>…</ResourceDictionary.MergedDictionaries>,可以选择将其他资源放在主字典中。 您使用从合并字典中获取的资源,就像使用常规字典一样。 在上面的示例中,{StaticResource brush} 在子字典或合并字典(Dictionary1.xaml)中找到资源,而 {StaticResource greeting} 在主页字典中找到其资源。

在资源查找序列中,只有在检查完该 ResourceDictionary内所有其他键控资源后,才会检查 MergedDictionaries 字典。 搜索完该级别后,查找过程将进入合并字典,检查 MergedDictionaries 中的每一项。 如果存在多个合并字典,将按照它们在 MergedDictionaries 属性中声明的顺序进行逆序检查。 在以下示例中,如果 Dictionary2.xaml 和 Dictionary1.xaml 都声明了同一个键,则首先使用 Dictionary2.xaml 中的键,因为它是 MergedDictionaries 集中的最后一个键。

<Page
    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.MergedDictionaries>
                <ResourceDictionary Source="Dictionary1.xaml"/>
                <ResourceDictionary Source="Dictionary2.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Page.Resources>

    <TextBlock Foreground="{StaticResource brush}" Text="greetings!" VerticalAlignment="Center"/>
</Page>

在任一 ResourceDictionary的范围内,将检查字典是否具有键唯一性。 但是,该范围不会扩展到不同 MergedDictionaries 文件中的不同条目。

可以使用查找序列与合并字典范围中缺乏唯一键强制的组合,以创建 ResourceDictionary 资源的后备值序列。 例如,可以使用同步到应用状态和用户首选项数据的资源字典,将特定画笔颜色的用户首选项存储在序列中最后一个合并的资源字典中。 但是,如果尚不存在用户首选项,则可以在初始 MergedDictionaries 文件中为 ResourceDictionary 资源定义相同的键字符串,并且可以用作回退值。 请记住,在检查合并字典之前,始终会先检查您在主资源字典中提供的任何值。因此,如果您想使用回退技术,请不要在主资源字典中定义该资源。

主题资源和主题字典

ThemeResource 类似于 StaticResource,但在主题更改时会重新评估资源查找。

在此示例中,将 TextBlock 的前景色设置为当前主题中的一个值。

<TextBlock Text="hello world" Foreground="{ThemeResource FocusVisualWhiteStrokeThemeBrush}" VerticalAlignment="Center"/>

主题字典是一种特殊类型的合并字典,用于保存用户当前在其设备上使用的主题的不同资源。 例如,“浅色”主题可能使用白色画笔,而“深色”主题可能使用深色画笔。 画笔更改了它所解析的资源,但在其他方面,使用画笔作为资源的控件的构成可能相同。 若要在自己的模板和样式中重现主题切换行为,而不是使用 MergedDictionaries 作为属性将项合并到主字典中,请使用 ThemeDictionaries 属性。

ThemeDictionaries 中的每个 ResourceDictionary 元素 必须具有 x:Key 值。 该值是一个字符串,用于命名相关主题,例如“Default”、“Dark”、“Light”或“HighContrast”。 通常, Dictionary1Dictionary2 定义具有相同名称但不同值的资源。

在这里,浅色主题使用红色文本,深色主题使用蓝色文本。

<!-- 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 的前景色设置为当前主题中的一个值。

<Page
    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 版本>\Generic 中打开 XAML 文件。 请注意如何在 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”的资源,该 资源提供颜色 定义,以将应用文本颜色与来自作系统和用户首选项的系统窗口的文本颜色匹配。 您的应用中的其他 XAML 样式可以引用此样式,或者代码可以获取资源查找值(在示例中将其转换为颜色 )。

有关详细信息以及适用于使用 XAML 的 Windows 应用可用的特定于主题的系统资源的列表,请参阅 XAML 主题资源

如果请求的密钥仍然未在任何这些位置中找到,则会发生 XAML 分析错误/异常。 在某些情况下,XAML 分析异常可能是 XAML 标记编译作或 XAML 设计环境未检测到的运行时异常。

由于资源字典的分层查找行为,可以特意定义多个资源项,这些资源项与键具有相同的字符串值,前提是每个资源在不同的级别定义。 换句话说,尽管键在任何给定 ResourceDictionary 中必须是唯一的,但唯一性要求不会扩展到整个查找行为序列。 在查找期间,只有成功检索的第一个此类对象用于 XAML 资源引用,然后查找将停止。 可以利用此行为在应用 XAML 中的不同位置通过键请求相同的 XAML 资源,但根据引用 XAML 资源的范围以及特定查找的运行方式可能获得不同的资源。

在 ResourceDictionary 中转发引用

特定资源字典中的 XAML 资源引用必须引用已使用键定义的资源,并且该资源必须在资源引用之前按词法显示。 XAML 资源引用无法解析前向引用。 因此,如果从另一个资源中使用 XAML 资源引用,则必须设计资源字典结构,以便先在资源字典中定义其他资源使用的资源。

在应用级别定义的资源不能引用即时资源。 这类似于尝试进行前向引用,因为应用程序资源实际上是在应用首次启动时以及在加载任何导航页面内容之前就被处理了。 但是,任何即时资源都可以引用应用资源,这可以是避免前向引用情况的有用技术。

XAML 资源必须可共享

要使对象存在于 ResourceDictionary中,该对象必须 可共享

可共享是必需的,因为当在运行时构造和使用应用的对象树时,树中的多个位置不能存在对象。 在内部,资源系统会在请求每个 XAML 资源时创建资源值的副本,以在应用的对象图中使用。

ResourceDictionary 和 Windows 运行时 XAML 通常支持这些对象供共享使用:

如果遵循必要的实现模式,还可以将自定义类型用作可共享资源。 可以在支持代码(或包含的运行时组件)中定义此类,然后将这些类实例化为 XAML 中的资源。 示例包括对象数据源和用于数据绑定的 IValueConverter 实现。

自定义类型必须具有默认构造函数,因为这是 XAML 分析程序用来实例化类的构造函数。 用作资源的自定义类型在其继承中不能有 UIElement 类,因为 UIElement 永远无法被共享(它始终代表运行时应用程序对象图中某个特定位置的一个 UI 元素)。

UserControl 用法范围

UserControl 元素在资源查找的行为方面具有特殊性,因为它具有定义范围和使用范围的内在概念。 UserControl 必须能够在其定义范围内支持对 XAML 资源的引用查找,这意味着它无法访问应用的资源。 从 UserControl 使用范围来看,资源引用被视为处于用以查找其使用页面根目录的序列中(如同从加载对象树中任何对象作出的资源引用),并可以访问应用程序资源。

ResourceDictionary 和 XamlReader.Load

可以将 ResourceDictionary 用作 XamlReader.Load 方法的根或 XAML 输入的一部分。 您也可以在 XAML 中包含 XAML 资源引用,前提是所有此类引用都完整地包含在提交加载的 XAML 中。 XamlReader.Load 解析 XAML 时,所在的上下文不知晓任何其他 ResourceDictionary 对象,甚至不包括 Application.Resources。 此外,请勿在提交给 {ThemeResource} 的 XAML 中使用

在代码中使用 ResourceDictionary

ResourceDictionary 的大多数使用场景都是在 XAML 中处理的。 将 ResourceDictionary 容器和资源声明为 UI 定义文件中的 XAML 文件或 XAML 节点集。 然后使用 XAML 资源引用从 XAML 的其他部分请求这些资源。 不过,在某些情况下,你的应用可能想要使用运行应用时执行的代码来调整 ResourceDictionary 的内容,或者至少要查询 ResourceDictionary 的内容以查看资源是否已定义。 这些代码调用是在 ResourceDictionary 实例上进行的,因此,您必须首先检索一个实例——可以通过在对象树的某个位置获取直接的 ResourceDictionary 来实现,方法是获取 FrameworkElement.Resources或者 Application.Current.Resources

在 C# 或 Microsoft Visual Basic 代码中,可以使用索引器(Item)引用给定 ResourceDictionary 中的资源。 ResourceDictionary 是字符串键式字典,因此索引器使用字符串键而不是整数索引。 在 Visual C++ 组件扩展(C++/CX)代码中,使用 查找

使用代码检查或更改 ResourceDictionary时,类似于 查找 的 API 的行为不会从即时资源遍历到应用程序资源。这种行为是 XAML 分析器在 XAML 页面加载时才会发生的。 在运行时,密钥的范围限于你当时所用的 ResourceDictionary 实例内。 但是,该范围确实扩展到 MergedDictionaries

此外,如果请求 ResourceDictionary 中不存在的密钥,则可能没有错误;返回值可能只是以 null 的形式提供。 不过,如果尝试将返回的 null 用作值,仍可能会收到错误。 错误来自属性的 setter,而不是 ResourceDictionary 调用。 避免错误的唯一方法是属性接受 null 作为有效值。 请注意,此行为与 XAML 分析时 XAML 查找行为形成鲜明对比;在分析时无法从 XAML 解析提供的密钥会导致 XAML 分析错误,即使在属性可能接受 null 的情况下也是如此。

在运行时,合并资源字典被包含在引用这些合并字典的主资源字典的索引范围内。 换句话说,可以使用主字典的 查找 来查找合并字典中实际定义的任何对象。 在这种情况下,查找行为与分析时 XAML 查找行为类似:如果合并字典中存在多个对象,每个对象具有相同键,则返回上一个添加字典中的对象。

可以通过调用 Add(适用于 C# 或 Visual Basic)或 Insert(适用于 C++/CX)来向现有的 ResourceDictionary 中添加项。 可以将项添加到即时资源或应用资源。 其中任一 API 调用都需要一个密钥,这满足 ResourceDictionary 中的每个项必须具有密钥的要求。 但是,在运行时添加到 ResourceDictionary 的项与 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 标记扩展 来访问该行为,而不是使用 StaticResourceThemeResource。 大多数应用都不会出现需要这种情况的情况。 有关详细信息,请参阅 CustomXamlResourceLoader