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;
}

最后运行起来,点击“切换主题”按钮即可看到效果。

发布时间:2025-05-08
其他阅读

解决sqlite依赖无法打包单文件的问题

在一次WPF开发中,选用了sqlite作为内嵌数据库,使用 ystem.Data.SQLite 库来调用,在使用 Fody 进行单文件打包时,发现打包文成后会出现 x86 和 x64 两个特定的文件夹,分别对应着32位和64位的 SQLite.Interop.dll,本文介绍修改项目文件来实现将 sqlite 通信库一起打包成单文件的方法。

查看原文

WPF使用云母材质

在最新的Windows 11 OS中,微软为流畅设计(Fluent Design)带来了新的云母材质,云母材质一种不透明的动态效果,微软将其作为Windows 11应用窗体的默认材质。

查看原文

Angular使用路由复用实现单页多窗(Tab页形式)

我们在开发后台管理系统时,一个很重要的需求就是多窗口编辑,来回切换使用。一种思路是使用iframe来记录多个多页面展示,本文通过Angular路由复用来实现这一需求。

查看原文

Winform中设置控件边框

本文将会介绍在Winform中如何设置控件的边框,可应用于Form和Panel等。

查看原文

Blazor文件上传解决方案

Blazor 是由 Asp.Net Core 团队推出的一个Web前端SPA解决方案,其中包括了使用 WebAssembly 的 Blazor Wasm 和使用 SignalR 进行实时交互的 Blazor server。本篇文章中使用的是 Blazor Wasm 方案来验证上传文件的操作。

查看原文