7

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

4

3 回答 3

9

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

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

SourceInitialized作为窗口事件的一部分,将窗口过程附加到 WPF 窗口的 HWND(窗口句柄) 。在您的窗口过程中,分别处理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;
    }
}
于 2011-06-15T16:07:28.743 回答
3

事件 SystemEvents.UserPreferenceChanged 也可以解决问题。 UserPreferenceChanged(日语)

于 2013-03-27T06:28:54.240 回答
3

不幸的是,接受的解决方案不适用于 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 自动颜色主题的活动颜色

于 2016-08-26T08:41:07.110 回答