Обнаружение изменения темы системы в WPF - PullRequest
5 голосов
/ 15 июня 2011

Мне нужно, чтобы мое приложение WPF определяло, когда DWM включается / выключается или когда изменяется системная тема.
Такое событие существует в WinForms, но я не вижу его в WPF.

Ответы [ 3 ]

7 голосов
/ 15 июня 2011

Я не слышал о событии WinForms , которое срабатывает, когда окно WinForms получает сообщения от системы, однако у него есть собственный метод WndProc(), который можно переопределить. Вы, вероятно, путаете оконные сообщения для событий формы. Ах, так что это событие StyleChanged, которое вызывается в окнах WinForms. Остальная часть моего ответа все еще стоит, хотя.

WPF также не тесно связан с Windows API, поскольку это технология высокого уровня, которая вкладывает много абстракций от внутренних компонентов. С одной стороны, он рисует все в окне сам по себе и не просит систему сделать его для этого ( EDIT: , поэтому в WPF такого StyleChanged нет событие). Тем не менее, Windows отправляет сообщения во все окна при переключении DWM и при смене темы, и вы все еще можете перейти к низкому уровню от уровня WPF, чтобы получить доступ к этим сообщениям и соответствующим образом управлять элементами управления WPF.

Присоедините оконную процедуру к HWND вашего окна WPF (дескриптор окна) как часть события SourceInitialized вашего окна. В вашей оконной процедуре обработайте оконные сообщения WM_DWMCOMPOSITIONCHANGED и WM_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;
    }
}
2 голосов
/ 27 марта 2013

Событие SystemEvents.UserPreferenceChanged также делает свое дело. UserPreferenceChanged (в японском языке)

1 голос
/ 26 августа 2016

К сожалению, принятое решение не работает с изменениями цветовой темы Aero, и шестнадцатеричные числа сообщений WM перепутаны - но я согласен, что это очень полезно, если вы хотите перехватывать сообщения WM в WPF.Некоторое время я пытался найти решение этой проблемы, и я думаю, что он нашел решение для всех возможных случаев (для аэро и классических тем).

Изменение цвета Aero вызывает WM_DWMCOLORIZATIONCOLORCHANGEDmessage.

Чтобы определить, когда меняется цветовая тема, вы должны использовать несколько методов.Событие 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, которая вызвала это событие (с помощью вызова API DwmGetColorizationParameters), а не что-то еще.Посмотрите эти ответы о том, как можно получить цвета Aero: Получить активный цвет автоматической цветовой темы Windows 8

...