为应用设置主题
.NET Multi-platform App UI (.NET MAUI) 应用可以使用 DynamicResource
标记扩展动态响应运行时的样式更改。 此标记扩展类似于 StaticResource
标记扩展,两者都使用字典键从 ResourceDictionary 中提取值。 但是,当 StaticResource
标记扩展执行单个字典查找时,DynamicResource
标记扩展会保留指向字典键的链接。 因此,如果替换与键关联的值,则会将更改应用于 VisualElement。 这使得可以在 .NET MAUI 应用中实现运行时主题设置。
在 .NET MAUI 应用中实现运行时主题设置的过程如下所示:
- 在 ResourceDictionary 中定义用于每个主题的资源。 有关详细信息,请参阅定义主题。
- 在应用的 App.xaml 文件中设置默认主题。 有关详细信息,请参阅设置默认主题。
- 使用
DynamicResource
标记扩展在应用中使用主题资源。 有关详细信息,请参阅使用主题资源。 - 添加代码以在运行时加载主题。 有关详细信息,请参阅在运行时加载主题。
重要
如果不需要在运行时动态更改应用主题,请使用 StaticResource
标记扩展。 如果在应用运行时预测切换主题,请使用 DynamicResource
标记扩展,使资源能够在运行时更新。
以下屏幕截图展示了已设置主题的页面,包括使用浅色主题的 iOS 应用和使用深色主题的 Android 应用:
注意
在运行时更改主题需要使用 XAML 或 C# 样式定义,并且无法使用 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=" • Cynical but not unfriendly."
Style="{StaticResource SmallLabelStyle}" />
<Label Text=" • Seven varieties of grimaces."
Style="{StaticResource SmallLabelStyle}" />
<Label Text=" • Doesn't laugh at your jokes."
Style="{StaticResource SmallLabelStyle}" />
</StackLayout>
...
</Grid>
</ScrollView>
</ContentPage>
直接使用主题资源时,应通过 DynamicResource
标记扩展使用。 但是,在用到一个使用 DynamicResource
标记扩展的样式时,应通过 StaticResource
标记扩展使用它。
有关样式设置的详细信息,请参阅使用 XAML 设置应用样式。 有关 DynamicResource
标记扩展的详细信息,请参阅动态样式。
在运行时加载主题
在运行时选择主题时,应用应:
- 从应用中删除当前主题。 可通过清除应用级 ResourceDictionary 的
MergedDictionaries
属性达成此目的。 - 加载所选主题。 可通过将所选主题的实例添加到应用级 ResourceDictionary 的
MergedDictionaries
属性达成此目的。
然后,任何使用 DynamicResource
标记扩展设置属性的 VisualElement 对象都将应用新的主题值。 之所以发生这种情况,是因为 DynamicResource
标记扩展保持有指向字典键的链接。 因此,替换与键关联的值时,更改将应用于 VisualElement 对象。
在示例应用程序中,通过包含 Picker 的模式页选择了主题。 以下代码显示了在所选主题更改时执行的 OnPickerSelectionChanged
方法:
以下示例演示如何删除当前主题并加载新主题:
ICollection<ResourceDictionary> mergedDictionaries = Application.Current.Resources.MergedDictionaries;
if (mergedDictionaries != null)
{
mergedDictionaries.Clear();
mergedDictionaries.Add(new DarkTheme());
}