如何在WPF C#中实现标题栏对系统明暗模式的适配?
问题说明
我想对我的WPF应用做少量自定义,打算保留原生标题栏,不做自定义标题栏改造。但遇到的问题是:即便Windows系统已经设置为暗色模式,WPF应用的标题栏仍然显示为亮色模式,我没有找到合适的指导教程来实现标题栏对明暗两种模式的适配。
StackOverflow上的多数回答都建议通过样式自定义标题栏及按钮等所有控件,但我不确定这是不是最优方案。
有没有人知道如何将WPF标题栏切换为暗色模式,以及如何切回亮色模式?我当前使用的技术栈是.NET 5 + WPF + C# :)
我已将Windows设置为暗色模式(Hell = 亮色,Dunkel = 暗色)
问题补充
尝试过使用WindowChrome的方案但遇到报错:插入WindowChrome的样式Setter属性时,收到报错提示“System.Windows.Shell”命名空间下不存在“WindowChrome”属性,Setter.Value部分也报相同错误。
尝试使用的代码:
<Setter Property="shell:WindowChrome"> <Setter.Value> <shell:WindowChrome /> </Setter.Value> </Setter>
尝试引入的命名空间:
xmlns:shell="System.Windows.Shell" xmlns:shell1="http://schemas.microsoft.com/expression/blend/2008" xmlns:shell2="http://schemas.microsoft.com/netfx/2009/xaml/presentation"
解决方案
1. 先解决WindowChrome报错问题
.NET 5 版本的WPF已经将WindowChrome集成到PresentationFramework程序集中,命名空间引用方式变更,正确的XAML引用语法为:
xmlns:shell="clr-namespace:System.Windows.Shell;assembly=PresentationFramework"
如果你不想自定义标题栏,完全不需要使用WindowChrome,直接调用Windows系统API修改原生标题栏属性即可,所有原生交互逻辑都会保留,无需额外适配。
2. 调用DWM API修改原生标题栏明暗模式
该方案支持Windows 10 1809及以上版本,不需要修改任何标题栏样式,最大化最小化按钮、拖拽、系统右键菜单、Windows11 snap布局功能都完全保留。
步骤1:导入API和常量
在窗口的后台C#代码中添加如下内容:
using System.Runtime.InteropServices; using System.Windows.Interop;
// 适配不同Windows版本的暗色模式属性标识 private const int DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 = 19; private const int DWMWA_USE_IMMERSIVE_DARK_MODE = 20; [DllImport("dwmapi.dll")] private static extern int DwmSetWindowAttribute(IntPtr hwnd, int attr, ref int attrValue, int attrSize);
步骤2:编写标题栏模式切换方法
private bool SetTitleBarMode(bool isDarkMode) { IntPtr windowHandle = new WindowInteropHelper(this).Handle; if (windowHandle == IntPtr.Zero) return false; int attributeValue = isDarkMode ? 1 : 0; int attributeId = Environment.OSVersion.Version.Build >= 19041 ? DWMWA_USE_IMMERSIVE_DARK_MODE : DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1; return DwmSetWindowAttribute(windowHandle, attributeId, ref attributeValue, sizeof(int)) == 0; }
步骤3:调用方法切换模式
在窗口初始化完成后调用,例如在SourceInitialized事件中触发:
protected override void OnSourceInitialized(EventArgs e) { base.OnSourceInitialized(e); // 开启暗色标题栏,传false则切换为亮色 SetTitleBarMode(true); }
如果需要跟随系统主题自动切换,可以注册Microsoft.Win32.SystemEvents.UserPreferenceChanged事件,监听系统主题变更后调用上述方法即可。
内容的提问来源于stack exchange,提问作者Dominic Saladin

