Подтвердить что ты не робот

Получить активный цвет автоматической цветовой темы Windows 8

В Windows 8 я установил цветовую схему для автоматической настройки и настройки моих обоев для изменения через x минут. Цветовая схема изменяется в зависимости от активных обоев.

Я разрабатываю приложение WPF и хочу, чтобы мои градиенты менялись, когда Windows меняет цветовую схему для соответствия текущим обоям.

Есть ли способ получить текущую/фактическую цветовую схему и получать уведомление об изменении в С#?

4b9b3361

Ответ 1

Да, это возможно. Однако будьте осторожны: это включает в себя довольно много Win32 interop (это означает, что P/вызывает в собственные DLL из управляемого кода) и работает только с некоторыми недокументированными API. Хотя единственными недокументированными функциями являются получение цветовой схемы окна (или, как называет DWM, цвет раскраски окна), который рассматривается в этом другом вопросе:

Vista/7: Как получить цвет стекла?

В моем собственном проекте я пользуюсь вызовом DwmGetColorizationParameters():

internal static class NativeMethods
{
    [DllImport("dwmapi.dll", EntryPoint="#127")]
    internal static extern void DwmGetColorizationParameters(ref DWMCOLORIZATIONPARAMS params);
}

public struct DWMCOLORIZATIONPARAMS
{
    public uint ColorizationColor, 
        ColorizationAfterglow, 
        ColorizationColorBalance, 
        ColorizationAfterglowBalance, 
        ColorizationBlurBalance, 
        ColorizationGlassReflectionIntensity, 
        ColorizationOpaqueBlend;
}

Я тестировал его, и он отлично работает с Windows 8 и его функцией автоматического раскрашивания окон. Как было предложено в ссылке выше, вы можете посмотреть в реестре значения цвета в качестве альтернативы P/Invoke, но я не тестировал этот метод, и, как указано, они недокументированы и не гарантируются как стабильные.

Как только вы получите цвет для рисования ваших градиентных кистей, кисти не будут обновляться при изменении цветовой схемы окна, будь то вручную или автоматически Windows. К счастью, Windows транслирует WM_DWMCOLORIZATIONCOLORCHANGED сообщение окна всякий раз, когда это происходит, поэтому вам просто нужно слушать это сообщение и обновлять свои цвета всякий раз, когда он отправляется, Вы делаете это, подключившись к процедуре окна (WndProc()).

Значение WM_DWMCOLORIZATIONCOLORCHANGED равно 0x320; вы хотите определить это как константу где-нибудь, чтобы вы могли использовать ее в коде.

Кроме того, в отличие от WinForms, окна WPF не имеют виртуального метода WndProc() для переопределения, поэтому вам нужно создать и подключить его в качестве делегата к связанным с ним окнам (HWND).

Взяв какой-то пример кода из моих ответов:

Имеем:

const int WM_DWMCOLORIZATIONCOLORCHANGED = 0x320;

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 static Color GetWindowColorizationColor(bool opaque)
{
    var params = NativeMethods.DwmGetColorizationParameters();

    return Color.FromArgb(
        (byte)(opaque ? 255 : params.ColorizationColor >> 24), 
        (byte)(params.ColorizationColor >> 16), 
        (byte)(params.ColorizationColor >> 8), 
        (byte) params.ColorizationColor
    );
}

private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
    switch (msg)
    {
        case WM_DWMCOLORIZATIONCOLORCHANGED:

            /* 
             * Update gradient brushes with new color information from
             * NativeMethods.DwmGetColorizationParams() or the registry.
             */

            return IntPtr.Zero;

        default:
            return IntPtr.Zero;
    }
}

Когда Windows переходит к изменению цвета, на каждом ключевом кадре перехода отправляется WM_DWMCOLORIZATIONCOLORCHANGED, поэтому при изменении цвета вы получаете многочисленные сообщения при коротком всплеске. Это нормально; просто обновите свои градиентные кисти, как обычно, и вы заметите, что когда Windows перейдет по цветовой схеме окна, ваши градиенты будут плавно перемещаться вместе с остальными рамами окна.

Помните, что вам может потребоваться учитывать ситуации, когда DWM недоступен, например, при работе в Windows XP или при работе в Windows Vista или более поздней версии с отключением рабочего стола. Вы также захотите, чтобы вы не злоупотребляли этим, или вы можете столкнуться с значительным успехом и замедлить свое приложение.

Ответ 2

Это можно сделать в .NET 4.5 и более поздних версиях без P/Invokes. Класс SystemParameters теперь имеет статические WindowGlassBrush и WindowGlassColor вместе с событием StaticPropertyChanged.

Из XAML вы можете привязываться к свойству WindowGlassBrush, например:

<Grid Background="{x:Static SystemParameters.WindowGlassBrush}">

Однако при этом назначении цвет фона не будет автоматически обновляться, когда Windows изменяет свои цвета. К сожалению, SystemParameters не предоставляет свойства WindowGlassBrushKey или WindowGlassColorKey для использования в качестве ResourceKeys с DynamicResource, поэтому для получения уведомлений о изменениях требуется код для обработки события StaticPropertyChanged.

public partial class MainWindow : Window
{
    public MainWindow()
    {
        this.InitializeComponent();
        SystemParameters.StaticPropertyChanged += this.SystemParameters_StaticPropertyChanged;

        // Call this if you haven't set Background in XAML.
        this.SetBackgroundColor();
    }

    protected override void OnClosed(EventArgs e)
    {
        SystemParameters.StaticPropertyChanged -= this.SystemParameters_StaticPropertyChanged;
        base.OnClosed(e);
    }

    private void SetBackgroundColor()
    {
        this.Background = SystemParameters.WindowGlassBrush;
    }

    private void SystemParameters_StaticPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
    {
        if (e.PropertyName == "WindowGlassBrush")
        {
            this.SetBackgroundColor();
        }
    }
}