Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get the active color of Windows 8 automatic color theme

Tags:

In Windows 8, I have set the color scheme to automatic and configured my wallpaper to change after x minutes. The color scheme changes according to the active wallpaper.

I'm developing a WPF application and would like to have my gradients change when Windows changes the color scheme to match the current wallpaper.

Is there a way get the current/actual color scheme and be notified of the change in C#?

like image 773
user1868880 Avatar asked Dec 01 '12 15:12

user1868880


People also ask

How can we change the Colour scheme of desktop?

Select Start > Settings . Select Personalization > Colors. Under Choose your color, select Light. To manually select an accent color, choose one under Recent colors or Windows colors, or select Custom color for an even more detailed option.

How do I change the theme color in Windows 10?

In Windows 10, on the left side of the Settings window, under Personalization, select the Colors option, if not already selected. Choose a color from the palette in the center of the screen under the Windows colors section.

How do I change the color scheme on Windows 7?

To change the color and translucency in Windows 7, follow these steps: Right-click anywhere on the desktop and click Personalize from the pop-up menu. When the Personalization window appears, click Window Color. When the Window Color and Appearance window appears, as shown in Figure 3, click the color scheme you want.


2 Answers

Yes, it's possible. However be warned: this encompasses quite a bit of Win32 interop (this means P/Invokes into native DLLs from managed code), and is only doable with certain undocumented APIs. Although, the only undocumented features involved are for obtaining the window color scheme (or as the DWM calls it, the window colorization color), which is covered in this other question:

Vista/7: How to get glass color?

In my own project, I make use of a call to 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; } 

I've tested it and it works great with Windows 8 and its automatic window colorization feature. As suggested in the link above, you can look in the registry for the color values as an alternative to a P/Invoke, but I haven't tested that method, and as stated these are undocumented and not guaranteed to be stable.

Once you obtain the color for drawing your gradient brushes, the brushes won't update when the window color scheme changes, whether manually or automatically by Windows. Thankfully, Windows broadcasts the WM_DWMCOLORIZATIONCOLORCHANGED window message whenever that happens, so you simply need to listen for that message and update your colors whenever it's sent. You do this by hooking onto the window procedure (WndProc()).

The value of WM_DWMCOLORIZATIONCOLORCHANGED is 0x320; you'll want to define that as a constant somewhere so you can use it in code.

Also, unlike WinForms, WPF windows don't have a virtual WndProc() method to override, so you have to create and hook one in as a delegate to their associated window handles (HWNDs).

Taking some example code from these answers of mine:

  • How do I make a WPF window movable by dragging the extended window frame?
  • Detect system theme change in WPF

We have:

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

When Windows transitions the color change, WM_DWMCOLORIZATIONCOLORCHANGED is dispatched at every keyframe in the transition, so you'll receive numerous messages at a short burst during the color change. This is normal; just update your gradient brushes as usual and you'll notice that when Windows transitions the window color scheme, your gradients will transition smoothly along with the rest of the window frames as well.

Remember that you may need to account for situations where the DWM isn't available, such as when running on Windows XP, or when running on Windows Vista or later with desktop composition disabled. You'll also want to ensure you don't overuse this, or you may incur a significant performance hit and slow down your app.

like image 150
BoltClock Avatar answered Nov 03 '22 00:11

BoltClock


This can be done in .NET 4.5 and later without P/Invokes. The SystemParameters class now has static WindowGlassBrush and WindowGlassColor properties along with a StaticPropertyChanged event.

From XAML, you can bind to the WindowGlassBrush property like:

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

However, with this assignment the Background color won't get updated automatically when Windows changes its colors. Unfortunately, SystemParameters does not provide WindowGlassBrushKey or WindowGlassColorKey properties to use as ResourceKeys with DynamicResource, so getting change notifications requires code behind to handle the StaticPropertyChanged event.

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();         }     } } 
like image 34
Bill Menees Avatar answered Nov 02 '22 23:11

Bill Menees