WPF中切换主题功能

发布时间:2025-05-08

在现代 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;
}

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

其他阅读

LinqPad 8 分享下载

LinqPad是一个 .Net 快速验证工具,使得.Net开发者可以不借助 Visual Studio 就可以快速验证想法,毕竟vs冷启动和创建项目耗时久,负担大。本文分享一个LinqPad学习版,可直接下载使用。

查看原文

管道技术——中间件的灵魂

在现代Web开发中,中间件技术使用越来越广泛,本文带大家了解中间件的基础,同时也是中间件的灵魂所在,管道技术。在C#中,依赖于委托,我们可以很容易就实现一个中间件管道。所以在阅读本文前,请确保你已经学会了什么是委托,包括但不限于Delegate,Action,Func。除此之外,本文还会使用到反射相关知识,请确保你已经学会了什么是反射。

查看原文

安卓开发 | 实现输入框自动全选内容

在安卓开发中,当用户手动选中输入框时。输入框内部的内容全部选中是一个比较提升用户体验的功能,主要用在一些复制的场景中。

查看原文

解决ssh登录后闲置一段时间断开

最新新买了一台服务器,使用 ssh 登录之后发现一段时间没有输入就会自动断开,这里记录一下解决方法。

查看原文

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

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

查看原文