最可靠方式是通过 Windows.UI.ViewManagement.UISettings 获取系统主题偏好,需在 UI 线程初始化并监听 ColorValuesChanged 事件;兼容旧版可 fallback 到注册表 AppsUseLightTheme 值,但须安全读取且注意其仅控制应用主题。

Windows 10/11 中如何用 C# 检测系统深色模式
在 Windows 10(1809+)和 Windows 11 上,C# 程序可通过 Windows.UI.ViewManagement.UISettings 获取当前系统主题偏好,这是最可靠、原生的方式。它不依赖注册表硬读取,也不受第三方主题工具干扰。
注意:该 API 属于 UWP 命名空间,但 .NET 5+(含 .NET Core 3.1)的桌面应用(WinForms/WPF)也能调用,前提是目标平台为 net5.0-windows10.0.17763.0 或更高,并引用 Microsoft.Windows.SDK.Contracts NuGet 包(仅需安装一次)。
常见错误现象:UISettings 构造失败、返回空值、或 GetColorValue 报 System.Runtime.InteropServices.COMException —— 多因未启用“Windows 兼容包”或运行在非 UI 线程(如后台线程直接 new UISettings)。
- 必须在 UI 线程中初始化
UISettings(WPF 中可用Dispatcher.Invoke,WinForms 中确保在Load事件内执行) - 推荐使用
UISettings().GetColorValue(UIColorType.Background)判断:深色模式下该值通常为接近#FF121212的暗色;浅色模式下接近#FFFFFFFF - 更稳妥的做法是结合
UIElementType和GetColorValue双重验证,避免个别 OEM 定制系统颜色异常导致误判
兼容旧版 Windows(如 Win7/8.1)的注册表 fallback 方案
若需支持 Windows 7 或未安装 SDK 合约的环境,可退而读取注册表键 HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize 下的 AppsUseLightTheme 值(DWORD)。该值为 0 表示深色模式启用,1 表示浅色。
注意:此注册表项仅在 Windows 10 1607+ 引入,Win7/8.1 下根本不存在,直接读取会抛 ArgumentException 或返回 null。不能无条件当作兜底方案。
- 务必先用
RegistryKey.OpenBaseKey尝试打开HKEY_CURRENT_USER,再用OpenSubKey安全访问子路径,避免未处理异常 - 读取后应检查返回值是否为
null或GetValue是否为null,再转int,否则可能触发InvalidCastException - 该值只控制「应用主题」,不反映系统级控件(如标题栏)状态,与
UISettings行为不完全一致
WPF 应用中监听主题变化(实时响应)
用户可能在程序运行中切换深色/浅色模式,UISettings 本身不提供事件,但可通过 UISettings.ColorValuesChanged 事件监听颜色变更。这是唯一能实时捕获系统主题切换的机制。
容易踩的坑:事件回调可能在非 UI 线程触发,直接更新控件会抛 InvalidOperationException;且该事件在某些低版本 Windows 上可能不触发(尤其启用了高对比度时)。
- 订阅前确保
UISettings实例已创建并持有强引用(避免 GC 回收导致事件失效) - 回调中用
Dispatcher.BeginInvoke(WPF)或Control.Invoke(WinForms)切回 UI 线程再更新样式 - 建议同时监听
SystemParameters.HighContrast变化,因为高对比度模式优先级高于深色模式,会覆盖主题逻辑
为什么不要用 SystemColors 或 ThemeResource 直接判断
SystemColors.WindowColor 或 WPF 的 {DynamicResource {x:Static SystemColors.WindowColorKey}} 是运行时解析的值,无法用于“判断”——它们只是当前渲染结果,不是源头信号。一旦主题切换但资源未刷新(如未触发重绘),你读到的仍是旧值。
更严重的是:这些值受 DPI 缩放、高对比度、甚至第三方主题注入影响,稳定性极差。曾有案例显示,某国产安全软件注入后让 SystemColors.ControlLightLight 在深色模式下反常变亮,导致整套自动换肤逻辑失效。
- 永远把
UISettings或注册表作为「输入源」,而非从渲染结果反推 - 如果必须用颜色值做判断,请限定使用
UIColorType.Background+UIColorType.Foreground组合,避开易被篡改的控件色(如 ButtonFace、Highlight) - 在 .NET 6+ 中,还可考虑
Application.Current.RequestedTheme(仅限 WPF +EnableWindowsCompatibility开启时),但它本质仍是包装了UISettings
深色模式检测看似简单,实际要横跨 API 兼容性、线程上下文、事件生命周期、以及 OEM/第三方干扰四个层面。最容易被忽略的是:没做 UISettings 初始化失败的降级处理,导致程序在老旧设备或受限环境直接崩溃而不是优雅回退。










