Detect system theme change in WPF

后端 未结 3 1922
悲哀的现实
悲哀的现实 2020-12-03 12:57

I need, for my WPF app, to detect when the DWM is turned on/off or when the system theme changes.
There is such an event in WinForms, but I can\'t see any in WPF.

3条回答
  •  予麋鹿
    予麋鹿 (楼主)
    2020-12-03 13:23

    Unfortunately the accepted solution does not work with Aero color theme changes, and the WM message hex numbers are mixed up - but I agree that it is very useful if you want to catch WM messages in WPF. I've been trying to find a solution to this problem for a while, and I think I've got it solved for all possible cases (for aero and classic themes).

    The Aero color change triggers the WM_DWMCOLORIZATIONCOLORCHANGED message.

    To detect when the color theme changes you have to use multiple methods. The Form.StyleChanged event is going to detect all theme change, except for Aero color changes. Here is an alternative solution to StyleChanged. (Ok, I know this is WinForms, but you've got the idea. The WPF equivalent is in the accepted answer anyway.)

        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);
        }
    

    For Aero color themes, the SystemEvents.UserPreferenceChanged event works too (thanks sees!):

        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
            }
        }
    

    As you can see above, it is far from intuitive. Aero color change triggers a 'General' preference change event, even though there are many more suitable ones for this, like 'VisualStyle', etc...

    If you want to be thorough, you should compare the saved DWM color to the current DWM color, to make sure it was indeed an Aero color theme that triggered this event (using the DwmGetColorizationParameters API call), and not something else. See these answers on how Aero colors can be retrieved: Get the active color of Windows 8 automatic color theme

提交回复
热议问题