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:
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.