检测WPF中的系统主题更改

对于我的WPF应用程序,我需要检测DWM何时打开/关闭或系统主题何时更改。
在WinForms中有这样的事件,但我在WPF中看不到任何事件。

我没有听说过WinForms窗口从系统接收消息时触发的WinForms事件 ,但是它有自己的WndProc()方法可以覆盖。 您可能会混淆表单事件的窗口消息。 啊,所以这是在WinForms窗口中调用的StyleChanged事件。 我的其余部分仍然有效。

WPF与Windows API没有密切联系,因为它是一种高级技术,可以从内部投入大量抽象。 首先,它自己在窗口中绘制所有内容 ,并且不要求系统为它绘制图形( 编辑:这就是为什么WPF缺少这样的StyleChanged事件)。 也就是说,当切换DWM时以及主题发生变化时,Windows会向所有窗口发送消息,您仍然可以从WPF层向下钻取到低级别以访问这些消息并相应地操作WPF控件。

将窗口过程附加到WPF窗口的HWND(窗口句柄),作为窗口SourceInitialized事件的一部分。 在窗口过程中,分别处理WM_DWMCOMPOSITIONCHANGEDWM_THEMECHANGED窗口消息。

这是一个简单的例子( 根据我的这个问题改编的样板代码):

 private IntPtr hwnd; private HwndSource hsource; private void Window_SourceInitialized(object sender, EventArgs e) { if ((hwnd = new WindowInteropHelper(this).Handle) == IntPtr.Zero) { throw new InvalidOperationException("Could not get window handle."); } hsource = HwndSource.FromHwnd(hwnd); hsource.AddHook(WndProc); } private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { switch (msg) { case WM_DWMCOMPOSITIONCHANGED: // Define this as 0x31A case WM_THEMECHANGED: // Define this as 0x31E // Respond to DWM being enabled/disabled or system theme being changed return IntPtr.Zero; default: return IntPtr.Zero; } } 

事件SystemEvents.UserPreferenceChanged也可以解决这个问题。 UserPreferenceChanged(在Japaense中)

遗憾的是,已接受的解决方案不适用于Aero颜色主题更改,并且WM消息hex数字混淆了 – 但我同意如果您想要在WPF中捕获WM消息,它非常有用。 我一直试图找到这个问题的解决方案一段时间,我想我已经解决了所有可能的情况(对于航空和经典主题)。

Aero颜色更改会触发WM_DWMCOLORIZATIONCOLORCHANGED消息。

要检测颜色主题何时更改,您必须使用多种方法。 除了Aero颜色更改外, Form.StyleChanged事件将检测所有主题更改 。 这是StyleChanged的替代解决方案。 (好吧,我知道这是WinForms,但是你已经有了这个想法。无论如何,WPF等价物都在接受的答案中。)

  private const int WM_DWMCOLORIZATIONCOLORCHANGED = 0x320; private const int WM_DWMCOMPOSITIONCHANGED = 0x31E; private const int WM_THEMECHANGED = 0x031A; protected override void WndProc(ref Message m) { switch(m.Msg) { case WM_DWMCOLORIZATIONCOLORCHANGED: case WM_DWMCOMPOSITIONCHANGED: case WM_THEMECHANGED: // you code here break; default: break; } base.WndProc(ref m); } 

对于Aero颜色主题,SystemEvents.UserPreferenceChanged事件也可以工作(谢谢看!):

  Microsoft.Win32.SystemEvents.UserPreferenceChanged += SystemEvents_UserPreferenceChanged; private void SystemEvents_UserPreferenceChanged(object sender, Microsoft.Win32.UserPreferenceChangedEventArgs e) { if (e.Category == Microsoft.Win32.UserPreferenceCategory.General) { // your code here, compare saved theme color with current one } } 

如您所见,它远非直观。 Aero颜色更改会触发“常规”首选项更改事件,即使有更多合适的更改事件,例如“VisualStyle”等…

如果你想要彻底,你应该将保存的DWM颜色与当前的DWM颜色进行比较,以确保它确实是一个触发此事件的Aero颜色主题(使用DwmGetColorizationParameters API调用),而不是其他东西。 请参阅以下有关如何检索Aero颜色的答案: 获取Windows 8自动颜色主题的活动颜色