为应用设置主题

Browse sample. 浏览示例

.NET Multi-platform App UI (.NET MAUI) 应用可以使用 DynamicResource 标记扩展动态响应运行时的样式更改。 此标记扩展类似于 StaticResource 标记扩展,两者都使用字典键从 ResourceDictionary 提取值。 但是,当 StaticResource 标记扩展执行单个字典查找时,DynamicResource 标记扩展会保留指向字典键的链接。 因此,如果替换与键关联的值,则会将更改应用于 VisualElement。 这使得可以在 .NET MAUI 应用中实现运行时主题设置。

在 .NET MAUI 应用中实现运行时主题设置的过程如下所示:

  1. ResourceDictionary 中定义用于每个主题的资源。 有关详细信息,请参阅定义主题
  2. 在应用的 App.xaml 文件中设置默认主题。 有关详细信息,请参阅设置默认主题
  3. 使用 DynamicResource 标记扩展在应用中使用主题资源。 有关详细信息,请参阅管理主题资源
  4. 添加代码以在运行时加载主题。 有关详细信息,请参阅在运行时加载主题

重要

如果不需要在运行时更改应用主题,请使用 StaticResource 标记扩展。

以下屏幕截图展示了已设置主题的页面,包括使用浅色主题的 iOS 应用和使用深色主题的 Android 应用:

Screenshot of the main page of a themed app.

注意

在运行时更改主题需要使用 XAML 样式,并且无法使用 CSS。

.NET MAUI 还可以响应系统主题更改。 系统主题可能会因各种原因而更改,具体取决于设备配置。 这包括用户明确更改的系统主题,基于一天中的时间而更改的系统主题,以及由于环境因素(如低亮度)而更改的主题。 有关详细信息,请参阅响应系统主题更改

定义主题

主题被定义为 ResourceDictionary 中存储的资源对象的集合。

以下示例为名为 LightTheme 的浅色主题显示 ResourceDictionary

<ResourceDictionary xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
                    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                    x:Class="ThemingDemo.LightTheme">
    <Color x:Key="PageBackgroundColor">White</Color>
    <Color x:Key="NavigationBarColor">WhiteSmoke</Color>
    <Color x:Key="PrimaryColor">WhiteSmoke</Color>
    <Color x:Key="SecondaryColor">Black</Color>
    <Color x:Key="PrimaryTextColor">Black</Color>
    <Color x:Key="SecondaryTextColor">White</Color>
    <Color x:Key="TertiaryTextColor">Gray</Color>
    <Color x:Key="TransparentColor">Transparent</Color>
</ResourceDictionary>

以下示例为名为 DarkTheme 的深色主题显示 ResourceDictionary

<ResourceDictionary xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
                    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                    x:Class="ThemingDemo.DarkTheme">
    <Color x:Key="PageBackgroundColor">Black</Color>
    <Color x:Key="NavigationBarColor">Teal</Color>
    <Color x:Key="PrimaryColor">Teal</Color>
    <Color x:Key="SecondaryColor">White</Color>
    <Color x:Key="PrimaryTextColor">White</Color>
    <Color x:Key="SecondaryTextColor">White</Color>
    <Color x:Key="TertiaryTextColor">WhiteSmoke</Color>
    <Color x:Key="TransparentColor">Transparent</Color>
</ResourceDictionary>

每个 ResourceDictionary 都包含定义其各自主题的 Color 资源,每个 ResourceDictionary 都使用相同的键值。 有关资源字典的详细信息,请参阅资源字典

重要

每个 ResourceDictionary 都需要一个代码隐藏文件,用于调用 InitializeComponent 方法。 这是必要的,以便可以在运行时创建表示所选主题的 CLR 对象。

设置默认主题

应用需要有默认主题,以便控件具有它们所使用的资源的值。 通过将主题的 ResourceDictionary 合并到 App.xaml 中定义的应用级别 ResourceDictionary,可以设置默认主题:

<Application xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="ThemingDemo.App">
    <Application.Resources>
        <ResourceDictionary Source="Themes/LightTheme.xaml" />
    </Application.Resources>
</Application>

有关合并资源字典的详细信息,请参阅合并的资源字典

使用主题资源

当应用想要使用 ResourceDictionary 中存储的表示主题的资源时,应通过 DynamicResource 标记扩展来使用。 这可确保若在运行时选择不同的主题,将应用新主题中的值。

以下示例展示了可应用于应用中所有 Label 对象的三种样式:

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

        <Style x:Key="LargeLabelStyle"
               TargetType="Label">
            <Setter Property="TextColor"
                    Value="{DynamicResource SecondaryTextColor}" />
            <Setter Property="FontSize"
                    Value="30" />
        </Style>

        <Style x:Key="MediumLabelStyle"
               TargetType="Label">
            <Setter Property="TextColor"
                    Value="{DynamicResource PrimaryTextColor}" />
            <Setter Property="FontSize"
                    Value="25" />
        </Style>

        <Style x:Key="SmallLabelStyle"
               TargetType="Label">
            <Setter Property="TextColor"
                    Value="{DynamicResource TertiaryTextColor}" />
            <Setter Property="FontSize"
                    Value="15" />
        </Style>

    </Application.Resources>
</Application>

这些样式在应用级别资源字典中定义,以便多个页面可以使用它们。 每个样式都通过 DynamicResource 标记扩展使用主题资源。

然后,这些样式由页面使用:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:ThemingDemo"
             x:Class="ThemingDemo.UserSummaryPage"
             Title="User Summary"
             BackgroundColor="{DynamicResource PageBackgroundColor}">
    ...
    <ScrollView>
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="200" />
                <RowDefinition Height="120" />
                <RowDefinition Height="70" />
            </Grid.RowDefinitions>
            <Grid BackgroundColor="{DynamicResource PrimaryColor}">
                <Label Text="Face-Palm Monkey"
                       VerticalOptions="Center"
                       Margin="15"
                       Style="{StaticResource MediumLabelStyle}" />
                ...
            </Grid>
            <StackLayout Grid.Row="1"
                         Margin="10">
                <Label Text="This monkey reacts appropriately to ridiculous assertions and actions."
                       Style="{StaticResource SmallLabelStyle}" />
                <Label Text="  &#x2022; Cynical but not unfriendly."
                       Style="{StaticResource SmallLabelStyle}" />
                <Label Text="  &#x2022; Seven varieties of grimaces."
                       Style="{StaticResource SmallLabelStyle}" />
                <Label Text="  &#x2022; Doesn't laugh at your jokes."
                       Style="{StaticResource SmallLabelStyle}" />
            </StackLayout>
            ...
        </Grid>
    </ScrollView>
</ContentPage>

直接使用主题资源时,应通过 DynamicResource 标记扩展使用。 但是,在用到一个使用 DynamicResource 标记扩展的样式时,应通过 StaticResource 标记扩展使用。

有关样式设置的详细信息,请参阅使用 XAML 设置应用的样式。 有关 DynamicResource 标记扩展的更多信息,请参见动态样式

在运行时加载主题

在运行时选定了主题时,应用应执行以下操作:

  1. 从应用中删除当前主题。 这是通过清除应用级别 ResourceDictionaryMergedDictionaries 属性实现的。
  2. 加载所选主题。 这是通过将所选主题的实例添加到应用级别 ResourceDictionaryMergedDictionaries 属性来实现的。

然后,任何使用 DynamicResource 标记扩展设置属性的 VisualElement 对象都将应用新的主题值。 这是因为 DynamicResource 标记扩展要保留一个到字典键的链接。 因此,当替换与键关联的值时,会将更改应用于 VisualElement 对象。

在示例应用程序中,主题是通过包含 Picker 的模式页面选择的。 下面的代码显示了 OnPickerSelectionChanged 方法,该方法在所选主题更改时执行:

下面的示例展示了如何删除当前主题并加载新主题:

ICollection<ResourceDictionary> mergedDictionaries = Application.Current.Resources.MergedDictionaries;
if (mergedDictionaries != null)
{
    mergedDictionaries.Clear();
    mergedDictionaries.Add(new DarkTheme());
}