资源字典

.NET Multi-platform App UI (.NET MAUI) ResourceDictionary 是 .NET MAUI 应用使用资源的存储库。 存储在 ResourceDictionary 中的典型资源包括样式、控件模板、数据模板、转换器和颜色。

可以使用 StaticResourceDynamicResource 标记扩展来引用存储在 ResourceDictionary 中的 XAML 资源并将其应用于元素。 在 C# 中,还可以在 ResourceDictionary 中定义资源,然后使用基于字符串的索引器引用资源并将其应用于元素。

提示

在 Visual Studio 中,基于 ResourceDictionary XAML 的文件由代码隐藏文件提供支持,可通过 .NET MAUI ResourceDictionary (XAML) 项模板添加到项目中

创建资源

每个 VisualElement 派生对象都有一个 Resources 属性,该属性是可以包含资源的 ResourceDictionary。 同样, Application 派生对象具有一个 Resources 属性,该属性是可以包含资源的 ResourceDictionary

.NET MAUI 应用只能包含派生自 Application 的单个类,但通常使用派生自 VisualElement 的许多类,包括页面、布局和视图。 这些对象中的任何一个都可以将其 Resources 属性设置为包含资源的 ResourceDictionary。 选择特定 ResourceDictionary 的放置位置会影响资源的使用位置:

除隐式样式外,资源字典中的每个资源都必须具有由 x:Key 特性定义的唯一字符串键。

以下 XAML 展示了 App.xaml 文件中以应用程序级别 ResourceDictionary 定义的资源:

<Application xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="ResourceDictionaryDemo.App">
    <Application.Resources>

        <Thickness x:Key="PageMargin">20</Thickness>

        <!-- Colors -->
        <Color x:Key="AppBackgroundColor">AliceBlue</Color>
        <Color x:Key="NavigationBarColor">#1976D2</Color>
        <Color x:Key="NavigationBarTextColor">White</Color>
        <Color x:Key="NormalTextColor">Black</Color>

        <!-- Images -->
        <x:String x:Key="BackgroundImage">background</x:String>
        <x:String x:Key="MenuIcon">menu.png</x:String>
        <x:String x:Key="SearchIcon">search.png</x:String>

        <!-- Implicit styles -->
        <Style TargetType="NavigationPage">
            <Setter Property="BarBackgroundColor"
                    Value="{StaticResource NavigationBarColor}" />
            <Setter Property="BarTextColor"
                    Value="{StaticResource NavigationBarTextColor}" />
        </Style>

        <Style TargetType="ContentPage"
               ApplyToDerivedTypes="True">
            <Setter Property="BackgroundColor"
                    Value="{StaticResource AppBackgroundColor}" />
        </Style>

    </Application.Resources>
</Application>

在此示例中,资源字典定义了一个 Thickness 资源、多个 Color 资源和两个隐式 Style 资源。

重要

直接在 Resources 属性元素标记之间插入资源会自动创建 ResourceDictionary 对象。 但是,在可选 ResourceDictionary 标记之间放置所有资源也是有效的。

使用资源

每个资源都有一个使用 x:Key 特性指定的键,该键将成为其在 ResourceDictionary 中的字典键。 该键用于从包含 StaticResourceDynamicResource XAML 标记扩展的 ResourceDictionary 中引用资源。

StaticResource 标记扩展类似于 DynamicResource 标记扩展,两者都使用字典键从资源字典引用值。 但是,当 StaticResource 标记扩展执行单个字典查找时,DynamicResource 标记扩展会保留指向字典键的链接。 因此,如果替换了与键关联的字典条目,则该更改将应用于视觉对象元素。 这样,就可以在应用中进行运行时资源更改。 有关标记扩展的详细信息,请参阅 XAML 标记扩展

以下 XAML 示例演示如何使用资源,以及如何在 StackLayout 中定义其他资源:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="ResourceDictionaryDemo.MainPage"
             Title="Main page">
    <StackLayout Margin="{StaticResource PageMargin}"
                 Spacing="6">
        <StackLayout.Resources>
            <!-- Implicit style -->
            <Style TargetType="Button">
                <Setter Property="FontSize" Value="14" />
                <Setter Property="BackgroundColor" Value="#1976D2" />
                <Setter Property="TextColor" Value="White" />
                <Setter Property="CornerRadius" Value="5" />
            </Style>
        </StackLayout.Resources>

        <Label Text="This app demonstrates consuming resources that have been defined in resource dictionaries." />
        <Button Text="Navigate"
                Clicked="OnNavigateButtonClicked" />
    </StackLayout>
</ContentPage>

在此示例中,ContentPage 对象使用在应用程序级别资源字典中定义的隐式样式。 StackLayout 对象使用在应用程序级别资源字典中定义的 PageMargin 资源,而 Button 对象使用在 StackLayout 资源字典中定义的隐式样式。 这会生成如以下屏幕截图中所示的外观:

使用资源字典资源。

重要

特定于单个页面的资源不应包含在应用程序级别资源字典中,因为随后会在应用启动时(而不是页面需要时)对这些资源进行分析。 有关详细信息,请参阅减小应用程序资源字典大小

资源查找行为

当使用 StaticResourceDynamicResource 标记扩展引用资源时,会发生以下查找过程:

  • 在资源字典中检查请求的键是否存在,如果存在,检查设置属性的元素。 如果找到请求的键,则返回其值,并且终止查找过程。
  • 如果未找到匹配项,则查找过程向上搜索可视化树,检查每个父元素的资源字典。 如果找到请求的键,则返回其值,并且终止查找过程。 否则,该过程将继续向上,直至到达根元素。
  • 如果在根元素上找不到匹配项,则会检查应用程序级别资源字典。
  • 如果仍然找不到匹配项,则会引发 XamlParseException

因此,当 XAML 分析程序遇到 StaticResourceDynamicResource 标记扩展时,它会使用找到的第一个匹配项,通过向上浏览可视化树来搜索匹配键。 如果此搜索在该页面结束,并且键仍未找到,则 XAML 分析程序将搜索附加到 App 对象的 ResourceDictionary。 如果仍未找到键,则会引发异常。

重写资源

当资源共享键时,在可视化树较低部分中定义的资源将优先于较高部分中定义的资源。 例如,在应用程序级别将 AppBackgroundColor 资源设置为 AliceBlue,将被设置为 Teal 的页面级别 AppBackgroundColor 资源重写。 同样,页面级别 AppBackgroundColor 资源将被布局或视图级别 AppBackgroundColor 资源重写。

独立资源字典

还可以将 ResourceDictionary 创建为不受代码隐藏文件支持的独立 XAML 文件。 要创建独立的 ResourceDictionary,请使用 .NET MAUI ResourceDictionary (XAML) 项模板向项目添加新的 ResourceDictionary 文件,并删除其代码隐藏文件。 然后,在 XAML 文件中,从文件开头附近的 ResourceDictionary 标记中移除 x:Class 特性。 此外,在 XML 标头后添加 <?xaml-comp compile="true" ?>,以确保编译 XAML。

还可以将 ResourceDictionary 创建为不受代码隐藏文件支持的独立 XAML 文件。 要创建独立的 ResourceDictionary,请使用 .NET MAUI ResourceDictionary (XAML) 项模板向项目添加新的 ResourceDictionary 文件,并删除其代码隐藏文件。 然后,在 XAML 文件中,从文件开头附近的 ResourceDictionary 标记中移除 x:Class 特性。 默认情况下,独立 ResourceDictionary 已编译其 XAML,除非 <?xaml-comp compile="false" ?> 在 XML 标头后面指定。

注意

独立 ResourceDictionary 必须包含 MauiXaml 的生成操作。

以下 XAML 示例显示名为 MyResourceDictionary.xaml 的独立 ResourceDictionary

<?xml version="1.0" encoding="UTF-8" ?>
<ResourceDictionary xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
                    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
    <DataTemplate x:Key="PersonDataTemplate">
        <ViewCell>
            <Grid RowSpacing="6"
                  ColumnSpacing="6">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="0.5*" />
                    <ColumnDefinition Width="0.2*" />
                    <ColumnDefinition Width="0.3*" />
                </Grid.ColumnDefinitions>
                <Label Text="{Binding Name}"
                       TextColor="{StaticResource NormalTextColor}"
                       FontAttributes="Bold" />
                <Label Grid.Column="1"
                       Text="{Binding Age}"
                       TextColor="{StaticResource NormalTextColor}" />
                <Label Grid.Column="2"
                       Text="{Binding Location}"
                       TextColor="{StaticResource NormalTextColor}"
                       HorizontalTextAlignment="End" />
            </Grid>
        </ViewCell>
    </DataTemplate>
</ResourceDictionary>

在此示例中,ResourceDictionary 包含单个资源,该资源是 DataTemplate 类型的对象。 可以通过将 MyResourceDictionary.xaml 合并到其他资源字典进行使用。

合并资源字典

可以通过将一个或多个 ResourceDictionary 对象合并到另一个 ResourceDictionary 中来合并资源字典。

合并本地资源字典

可以通过创建 ResourceDictionary 对象,将其 Source 属性设置为包含资源的 XAML 文件的文件名,从而将本地 ResourceDictionary 文件与另一个 ResourceDictionary 合并:

<ContentPage ...>
    <ContentPage.Resources>
        <!-- Add more resources here -->
        <ResourceDictionary Source="MyResourceDictionary.xaml" />
        <!-- Add more resources here -->
    </ContentPage.Resources>
    ...
</ContentPage>

此语法不会实例化 MyResourceDictionary 类。 而是会引用 XAML 文件。 因此,在设置 Source 属性时,不需要代码隐藏文件,并且可以从 MyResourceDictionary.xaml 文件的根标记中移除 x:Class 特性。

重要

只能从 XAML 设置 ResourceDictionary.Source 属性。

从其他程序集合并资源字典

还可以通过将 ResourceDictionary 添加到 ResourceDictionaryMergedDictionaries 属性,将其与另一个 ResourceDictionary 合并。 此方法允许合并资源字典,而不管它们驻留在哪个进程集中。 从外部程序集合并资源字典需要 ResourceDictionary 满足以下条件:将生成操作设置到 MauiXaml,具备代码隐藏文件,并在文件的根标记中定义 x:Class 特性。

警告

ResourceDictionary 类还会定义一个 MergedWith 属性。 但是,此属性已被弃用,不应再使用。

以下代码示例显示要添加到页面级别 ResourceDictionaryMergedDictionaries 集合中的两个资源字典:

<ContentPage ...
             xmlns:local="clr-namespace:ResourceDictionaryDemo"
             xmlns:theme="clr-namespace:MyThemes;assembly=MyThemes">
    <ContentPage.Resources>
        <ResourceDictionary>
            <!-- Add more resources here -->
            <ResourceDictionary.MergedDictionaries>
                <!-- Add more resource dictionaries here -->
                <local:MyResourceDictionary />
                <theme:DefaultTheme />
                <!-- Add more resource dictionaries here -->
            </ResourceDictionary.MergedDictionaries>
            <!-- Add more resources here -->
        </ResourceDictionary>
    </ContentPage.Resources>
    ...
</ContentPage>

在此示例中,同一程序集中的资源字典以及外部程序集中的资源字典合并到页面级资源字典中。 此外,还可以在 MergedDictionaries 属性-元素标记中添加其他 ResourceDictionary 对象,并在这些标记之外添加其他资源。

重要

ResourceDictionary 中只能有一个 MergedDictionaries 属性-元素标记,但可以根据需要放入任意数量的 ResourceDictionary 对象。

当合并的 ResourceDictionary 资源共享相同的 x:Key 特性值时,.NET MAUI 会使用以下资源优先级:

  1. 资源字典的本地资源。
  2. 通过 MergedDictionaries 集合合并的资源字典中包含的资源,它们在 MergedDictionaries 属性中以倒序列出。

提示

如果应用包含多个大型资源字典,搜索资源字典可能会是一项计算密集型任务。 因此,为了避免不必要的搜索,应确保应用程序中的每个页面仅使用适用于页面的资源字典。

从代码中使用基于 XAML 的资源字典

XAML 中定义的资源字典可以在代码中使用,前提是 ResourceDictionary 由代码隐藏文件提供支持。 在 Visual Studio 中,对于由代码隐藏文件提供支持的基于 XAML 的 ResourceDictionary 文件,可通过 .NET MAUI ResourceDictionary (XAML) 项模板添加到项目中。

代码隐藏支持的资源字典的屏幕截图。

然后,可以从 C# 使用由代码隐藏文件支持的基于 XAML 的资源字典,方法是将字典添加到资源字典的 MergedDictionaries 集合:

Resources.MergedDictionaries.Add(new MyMauiApp.Resources.Styles.MyColors());
Resources.MergedDictionaries.Add(new MyMauiApp.Resources.Styles.MyStyles());

通过代码中的密钥访问资源

可以像访问任何其他字典一样,从代码访问资源字典中的资源。

以下示例演示如何从页面的资源字典中检索和应用资源:

// Retrieve the Primary color value which is in the page's resource dictionary
var hasValue = Resources.TryGetValue("Primary", out object primaryColor);

if (hasValue)
{
    myLabel.TextColor = (Color)primaryColor;
}

这是建议的方法,可确保如果 .NET MAUI 无法从代码检索资源,则不会引发 KeyNotFoundException。 当合并的资源字典由 XAML 文件中定义的资源和内联资源组成时,可能会发生这种情况。 有关详细信息,请参阅 GitHub 问题 #11214

注意

若要从代码中检索应用范围资源,请访问 App.Current.Resources 资源字典。