WPF中切换主题功能
在现代 Windows 系统中,系统提供了亮色主题和暗色主题,Windows 自带的应用程序都已经适配该功能。本文介绍在使用 WPF 构建 Windows 窗口应用时怎么实现主题切换。
资源字典
在 WPF 中,资源是可在应用程序中的不同位置重复使用的对象,资源可以存储在 XAML 文件中,该文件称为资源字典文件,该文件没有配套的后端 cs 代码,使用纯粹的 xml 进行定义。在切换主题时,我们就需要用到资源文件。
颜色系统
首先,整理出整个应用的颜色系统,包含了边框颜色,背景颜色,主色,特殊色。如下所示:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Test"
xmlns:sys="clr-namespace:System;assembly=mscorlib">
<SolidColorBrush x:Key="BorderBrush" Color="#E5E5E5" />
<SolidColorBrush x:Key="PageBackgroundBrush" Color="#F9F9F9" />
<SolidColorBrush x:Key="PrimaryBrush" Color="#0D76F5" />
<SolidColorBrush x:Key="DangerBrush" Color="#FF1214" />
</ResourceDictionary>
将文件保存为 Light.xaml,在同目录下再创建 Dark.xaml 文件,颜色字段与 Light.xaml 中一致,颜色值改成暗色主题相关即可。如下所示:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Test"
xmlns:sys="clr-namespace:System;assembly=mscorlib">
<SolidColorBrush x:Key="BorderBrush" Color="#4D4D4D" />
<SolidColorBrush x:Key="PageBackgroundBrush" Color="#1C1C1F" />
<SolidColorBrush x:Key="PrimaryBrush" Color="#0D76F5" />
<SolidColorBrush x:Key="DangerBrush" Color="#FF1214" />
</ResourceDictionary>
在App.xaml中引入默认主题对应的资源,默认使用两色主题,这里引入Light.xaml。如下所示:
<Application
x:Class="Test.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Test">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!--使用相对位置引入资源字典文件-->
<ResourceDictionary Source="Colors/LightColor.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
颜色切换
将颜色应用到窗口程序中,设置窗口的背景颜色和卡片的边框颜色。
<Window
x:Class="Test.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Test"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="MainWindow"
Width="400"
Height="400"
Background="{DynamicResource PageBackgroundBrush}"
mc:Ignorable="d">
<Grid>
<StackPanel>
<TextBlock
FontSize="24"
Foreground="{DynamicResource DangerBrush}"
Text="Hello, World!" />
<Button
Margin="5"
Padding="10"
Background="{DynamicResource PrimaryBrush}"
Click="Button_Click"
Content="切换主题"
Foreground="White" />
<Border
Width="100"
Height="100"
Margin="10"
BorderBrush="{DynamicResource BorderBrush}"
BorderThickness="1" />
</StackPanel>
</Grid>
</Window>
在按钮的响应事件中替换对应的资源字典文件,将原来的资源字典文件移除即可。
private void Button_Click(object sender, RoutedEventArgs e)
{
List<ResourceDictionary> dictionaryList = new List<ResourceDictionary>();
foreach (ResourceDictionary dictionary in Application.Current.Resources.MergedDictionaries)
{
dictionaryList.Add(dictionary);
}
ResourceDictionary defaults = dictionaryList.FirstOrDefault(d => d.Source.OriginalString.EndsWith("Color.xaml"));
Application.Current.Resources.MergedDictionaries.Remove(defaults);
string requestedColor = $@"pack://application:,,,/Colors/{(isDark ? "Light" : "Dark")}Color.xaml";
ResourceDictionary resourceDictionary = new ResourceDictionary() { Source = new Uri(requestedColor) };
Application.Current.Resources.MergedDictionaries.Add(resourceDictionary);
isDark = !isDark;
}
最后运行起来,点击“切换主题”按钮即可看到效果。