检测WPF中的系统主题更改



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

我还没有听说过当WinForms窗口接收到来自系统的消息时会触发的WinForms事件,但它有自己的WndProc()方法,您可以覆盖它。您可能混淆了窗体事件的窗口消息Ah,所以它是在WinForms窗口中调用的StyleChanged事件。不过,我的其余回答仍然有效。

WPF也与Windows API没有紧密联系,因为它是一种高级技术,在内部投入了大量抽象。首先,它自己在窗口中绘制所有,并且不要求系统为其绘制(EDIT:这就是WPF缺少StyleChanged事件的原因)。也就是说,当DWM切换和主题更改时,Windows会向所有窗口发送消息,并且您仍然可以从WPF层深入到低级别以访问这些消息并相应地操作您的WPF控件。

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

这里有一个快速的例子(根据我的这个问题改编的样板代码):

private IntPtr hwnd;
private HwndSource hsource;
private const int WM_DWMCOMPOSITIONCHANGED= 0x31E;
private const int WM_THEMECHANGED = 0x31A;
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: 
        case WM_THEMECHANGED:         
            // Respond to DWM being enabled/disabled or system theme being changed
            return IntPtr.Zero;
        default:
            return IntPtr.Zero;
    }
}

不幸的是,接受的解决方案不适用于Aero颜色主题更改,并且WM消息的十六进制数字混淆了-但我同意,如果您想在WPF中捕获WM消息,这是非常有用的。一段时间以来,我一直在努力寻找这个问题的解决方案,我想我已经解决了所有可能的情况(航空和经典主题)。

Aero颜色更改触发WM_DWMCOLORIZATIONCOLORCHANGED消息。

要检测颜色主题何时更改,必须使用多种方法。Form.StyleChanged事件将检测所有主题更改,除了Aero颜色更改。以下是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自动颜色主题的活动颜色

事件SystemEvents.UserPreferenceChanged也起到了作用。UserPreferenceChanged(日语)

相关内容

  • 没有找到相关文章

最新更新