在 Windows 11 22H2 中自定义窗口标题栏颜色
适用系统:Windows 11 版本 22H2(Build 22621)及以上
适用场景:Win32 或 .NET 桌面应用(WPF / WinForms)
关键词:DWM、标题栏、自定义颜色、Windows 11、C#、DwmSetWindowAttribute
背景
在 Windows 11 22H2(2022 年 10 月更新,Build 22621) 中,微软悄悄开放了几个新的 DWM(Desktop Window Manager)属性,其中就包括了:
DWMWA_BORDER_COLOR:设置窗口边框色
DWMWA_CAPTION_COLOR:设置标题栏背景色
DWMWA_TEXT_COLOR:设置标题栏文字(包括按钮图标)颜色
这意味着,我们终于可以在不破坏系统原生体验的前提下,为窗口标题栏指定任意颜色!
本文将带你用 C# 封装这三个 API,并提供完整、可复用的工具类,轻松实现标题栏个性化。
技术原理
Windows 11 的标题栏由 DWM 渲染,而非传统 GDI。因此,要修改其外观,必须通过 DWM 提供的接口。
微软在 dwmapi.dll 中新增了 DwmSetWindowAttribute 函数支持以下两个属性(常量值):
1 2 3
| #define DWMWA_BORDER_COLOR 34 #define DWMWA_CAPTION_COLOR 35 #define DWMWA_TEXT_COLOR 36
|
调用时传入一个 RGB 颜色值(格式为 0x00BBGGRR),即可生效。注意:Alpha 通道被忽略,且颜色顺序是 B-G-R,而非常见的 R-G-B。
⚠️ 重要提示:
这两个属性仅在 Windows 11 22H2 及更高版本有效。在 Windows 10 或早期 Windows 11 上调用会失败(返回非零 HRESULT)。
C# 封装实现
下面是一个完整的静态工具类 DwmApi,封装了标题栏颜色设置功能,并附带辅助方法用于获取进程窗口句柄,还贴心的给了详细注释,就算完全不看这篇文章也可以拿来就用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
| using System; using System.Collections.Generic; using System.Diagnostics; using System.Drawing; using System.Runtime.InteropServices;
public static class DwmApi { private delegate bool EnumerateWindowsProc(IntPtr hWnd, IntPtr lParam);
[DllImport("user32.dll", EntryPoint = "EnumThreadWindows")] private static extern bool EnumerateWindows( int dwThreadId, EnumerateWindowsProc lpfn, IntPtr lParam );
[DllImport("dwmapi.dll")] private static extern int DwmSetWindowAttribute( IntPtr hwnd, int dwAttribute, ref uint pvAttribute, int cbAttribute );
private const int DWMWA_BORDER_COLOR = 34; private const int DWMWA_CAPTION_COLOR = 35; private const int DWMWA_TEXT_COLOR = 36;
public static IntPtr[] EnumerateProcessWindowHandles(int processId) { List<IntPtr> handles = [];
var process = Process.GetProcessById(processId);
foreach (ProcessThread thread in process.Threads) { EnumerateWindows(thread.Id, (hWnd, lParam) => { handles.Add(hWnd);
return true; }, IntPtr.Zero); }
return handles.ToArray(); }
public static bool SetCaptionColor(IntPtr hwnd, Color color) { uint rgb = color.R | (uint)color.G << 8 | (uint)color.B << 16; int hr = DwmSetWindowAttribute(hwnd, DWMWA_CAPTION_COLOR, ref rgb, sizeof(uint));
return hr == 0; }
public static bool SetCaptionTextColor(IntPtr hwnd, Color color) { uint rgb = color.R | (uint)color.G << 8 | (uint)color.B << 16; int hr = DwmSetWindowAttribute(hwnd, DWMWA_TEXT_COLOR, ref rgb, sizeof(uint));
return hr == 0; } public static bool SetWindowBorderColor(IntPtr hwnd, Color color) { uint rgb = color.R | (uint)color.G << 8 | (uint)color.B << 16; int hr = DwmSetWindowAttribute(hwnd, DWMWA_BORDER_COLOR, ref rgb, sizeof(uint)); return hr == 0; } }
|
✅ 关键细节说明:
颜色格式转换:
Color 结构是 R, G, B,但 DWM 要求 0x00BBGGRR,因此我们通过位运算重组:
1
| uint rgb = color.R | (uint)color.G << 8 | (uint)color.B << 16;
|
错误处理:
DwmSetWindowAttribute 返回 HRESULT,成功时为 S_OK (0)。我们据此判断是否支持。
窗口句柄获取:
提供了 EnumerateProcessWindowHandles 方法,方便在不知道具体 HWND 时通过进程 ID 获取所有窗口(适用于外部进程控制或调试)。
使用示例
1 2 3 4 5 6 7 8 9 10 11 12
| public partial class Form1 : Form { protected override void OnHandleCreated(EventArgs e) { base.OnHandleCreated(e);
DwmApi.SetCaptionColor(this.Handle, Color.FromArgb(30, 30, 60)); DwmApi.SetCaptionTextColor(this.Handle, Color.White); DwmApi.SetWindowBorderColor(hwnd, Colors.White); } }
|
2. 在 WPF 中使用
WPF 窗口需启用 AllowsTransparency="False"(默认),并通过 WindowInteropHelper 获取句柄:
1 2 3 4 5 6 7 8 9 10 11 12
| public partial class MainWindow : Window { protected override void OnSourceInitialized(EventArgs e) { base.OnSourceInitialized(e);
var hwnd = new WindowInteropHelper(this).Handle; DwmApi.SetCaptionColor(hwnd, Color.Purple); DwmApi.SetCaptionTextColor(hwnd, Colors.White); DwmApi.SetWindowBorderColor(hwnd, Colors.White); } }
|
3. 在 Avalonia 中使用
1 2 3 4 5 6 7 8 9 10 11 12
| public MainWindow() { InitializeComponent(); var hwnd = TryGetPlatformHandle(); if (hwnd != null) { DwmApi.SetCaptionColor(hwnd.Handle, Color.FromArgb(255, 30, 30, 80)); DwmApi.SetCaptionTextColor(hwnd.Handle, Color.White); DwmApi.SetWindowBorderColor(hwnd, Colors.White); } }
|
4. 修改其他进程的窗口(需权限)
1 2 3 4 5 6 7 8 9 10 11 12
| var notepad = Process.GetProcessesByName("notepad").FirstOrDefault(); if (notepad != null) { var windows = DwmApi.EnumerateProcessWindowHandles(notepad.Id); foreach (var hwnd in windows) { DwmApi.SetCaptionColor(hwnd, Color.LightGreen); DwmApi.SetCaptionTextColor(hwnd, Color.DarkGreen); DwmApi.SetWindowBorderColor(hwnd, Colors.White); } }
|
🔒 注意:修改其他进程窗口可能需要管理员权限,且某些受保护进程(如任务管理器)会拒绝访问。
注意事项与限制
仅支持 Windows 11 22H2+
在旧系统上调用会静默失败(返回 false),建议先检测系统版本:
1 2 3 4
| if (Environment.OSVersion.Version.Build >= 22621) { }
|
不支持透明或半透明
Alpha 通道被忽略,只能设置纯色。
最小化/最大化按钮颜色跟随文字色
设置 DWMWA_TEXT_COLOR 后,三个窗口按钮(关闭、最大化、最小化)的图标颜色也会随之改变,但对于通过美化工具自定义的窗口按钮图标无效。
高对比度模式下可能被覆盖
系统高对比度主题会强制使用系统配色,此时自定义颜色可能不生效。
尾声
Windows 11 22H2 终于为开发者打开了标题栏自定义的大门。虽然功能仍较基础(仅支持纯色),但已足够满足大多数品牌化或主题化需求。通过本文提供的 DwmApi 类,我们可以轻松在 .NET 应用中集成这一特性,打造更具个性的桌面体验。
有关的东东:
本作品由 Mioter 于 2025-10-17 01:46:03 发布
除特别声明外,本站作品均采用
CC BY-NC-SA 4.0 许可协议,转载请注明来自
Mioter的博客