Obtenga el color activo del tema de color automático de Windows 8

En Windows 8, configuré el esquema de color como automático y configuré mi fondo de pantalla para cambiar después de x minutos. El esquema de color cambia de acuerdo con el fondo de pantalla activo.

Estoy desarrollando una aplicación WPF y me gustaría que mis degradados cambien cuando Windows cambie el esquema de colores para que coincida con el fondo de pantalla actual.

¿Hay alguna manera de obtener el esquema de color actual / actual y ser notificado del cambio en C #?

Si es posible. Sin embargo, tenga cuidado: esto abarca un poco de interoperabilidad de Win32 (esto significa P / invoca en DLL nativas del código administrado), y solo es factible con ciertas API no documentadas . Aunque, las únicas características no documentadas involucradas son para obtener el esquema de color de la ventana (o como lo llama DWM, el color de colorización de la ventana), que se trata en esta otra pregunta:

Vista / 7: ¿Cómo obtener el color del vidrio?

En mi propio proyecto, utilizo una llamada a 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; } 

Lo probé y funciona muy bien con Windows 8 y su característica automática de colorización de ventanas. Como se sugiere en el enlace anterior, puede buscar en el registro los valores de color como una alternativa a una P / Invocación, pero no he probado ese método, y como se indicó, estos no están documentados y no se garantiza que sean estables.

Una vez que obtenga el color para dibujar sus pinceles degradados, los pinceles no se actualizarán cuando el esquema de colores de la ventana cambie, ya sea de forma manual o automática por parte de Windows. Afortunadamente, Windows transmite el mensaje de ventana WM_DWMCOLORIZATIONCOLORCHANGED cada vez que eso sucede, por lo que simplemente necesita escuchar ese mensaje y actualizar sus colores cada vez que se envía. Para ello, enganche en el procedimiento de ventana ( WndProc() ).

El valor de WM_DWMCOLORIZATIONCOLORCHANGED es 0x320 ; querrá definir eso como una constante en alguna parte para poder usarlo en el código.

Además, a diferencia de WinForms, las ventanas de WPF no tienen un método WndProc() virtual para sobrescribir, por lo que debe crear y enlazar uno como un delegado a los manejadores de ventana asociados (HWND).

Tomando un código de ejemplo de estas respuestas mías:

  • ¿Cómo puedo hacer que una ventana de WPF se mueva arrastrando el marco de la ventana extendida?
  • Detectar cambios en el tema del sistema en WPF

Tenemos:

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

Cuando Windows hace la transición del cambio de color, WM_DWMCOLORIZATIONCOLORCHANGED se envía a cada fotogtwig clave en la transición, por lo que recibirá numerosos mensajes en una ráfaga corta durante el cambio de color. Esto es normal; simplemente actualice sus pinceles de gradiente como de costumbre y notará que cuando Windows transita el esquema de colores de la ventana, sus degradados se moverán suavemente junto con el rest de los marcos de las ventanas también.

Recuerde que es posible que necesite dar cuenta de situaciones en las que DWM no está disponible, como cuando se ejecuta en Windows XP, o cuando se ejecuta en Windows Vista o posterior con la composición de escritorio desactivada. También querrá asegurarse de no abusar de esto, o puede incurrir en un golpe de rendimiento significativo y ralentizar su aplicación.

Esto se puede hacer en .NET 4.5 y posteriores sin P / Invocaciones. La clase SystemParameters ahora tiene propiedades estáticas WindowGlassBrush y WindowGlassColor junto con un evento StaticPropertyChanged .

Desde XAML, puede enlazar a la propiedad WindowGlassBrush como:

  

Sin embargo, con esta asignación, el color de fondo no se actualizará automáticamente cuando Windows cambie sus colores. Desafortunadamente, SystemParameters no proporciona las propiedades WindowGlassBrushKey o WindowGlassColorKey para usar como ResourceKeys con DynamicResource, por lo que obtener notificaciones de cambio requiere código detrás para manejar el evento 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(); } } }