wpf 获取Windows 8自动颜色主题的活动颜色
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/13660976/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Get the active color of Windows 8 automatic color theme
提问by user1868880
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.
在 Windows 8 中,我已将配色方案设置为自动并将我的墙纸配置为在 x 分钟后更改。配色方案根据活动壁纸而变化。
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.
我正在开发 WPF 应用程序,并希望在 Windows 更改配色方案以匹配当前壁纸时更改渐变。
Is there a way get the current/actual color scheme and be notified of the change in C#?
有没有办法获得当前/实际的配色方案并收到 C# 更改的通知?
回答by BoltClock
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:
是的,这是可能的。但是请注意:这包含相当多的 Win32 互操作(这意味着从托管代码 P/Invokes 到本机 DLL),并且仅适用于某些未记录的 API。虽然,所涉及的唯一未记录的功能是用于获取窗口配色方案(或如 DWM 所称的,窗口着色颜色),这在另一个问题中有所介绍:
Vista/7: How to get glass color?
In my own project, I make use of a call to DwmGetColorizationParameters():
在我自己的项目中,我使用了以下调用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.
我已经对其进行了测试,它在 Windows 8 及其自动窗口着色功能中运行良好。正如上面链接中所建议的那样,您可以在注册表中查找颜色值作为 P/Invoke 的替代方法,但我还没有测试过该方法,并且如上所述,这些没有记录并且不能保证稳定。
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_DWMCOLORIZATIONCOLORCHANGEDwindow messagewhenever 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()).
一旦获得用于绘制渐变画笔的颜色,当窗口颜色方案更改时,无论是手动还是由 Windows 自动更改,画笔都不会更新。值得庆幸的是,每当发生这种情况时,Windows 都会广播WM_DWMCOLORIZATIONCOLORCHANGED窗口消息,因此您只需要监听该消息并在发送时更新您的颜色。您可以通过挂钩到窗口过程 ( WndProc()) 来完成此操作。
The value of WM_DWMCOLORIZATIONCOLORCHANGEDis 0x320; you'll want to define that as a constant somewhere so you can use it in code.
的值WM_DWMCOLORIZATIONCOLORCHANGED就是0x320; 您需要在某处将其定义为常量,以便您可以在代码中使用它。
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).
此外,与 WinForms 不同的是,WPF 窗口没有WndProc()要覆盖的虚拟方法,因此您必须创建一个虚拟方法并将其作为其关联的窗口句柄 (HWND) 的委托挂接。
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_DWMCOLORIZATIONCOLORCHANGEDis 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.
当 Windows 转换颜色更改时,WM_DWMCOLORIZATIONCOLORCHANGED会在转换中的每个关键帧处分派,因此在颜色更改期间您将在短时间内收到大量消息。这是正常的; 只需像往常一样更新您的渐变画笔,您就会注意到,当 Windows 转换窗口颜色方案时,您的渐变也将与其余窗口框架一起平滑过渡。
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.
请记住,您可能需要考虑 DWM 不可用的情况,例如在 Windows XP 上运行时,或在禁用桌面组合的 Windows Vista 或更高版本上运行时。您还需要确保不会过度使用它,否则可能会导致显着的性能下降并降低应用程序的速度。
回答by Bill Menees
This can be done in .NET 4.5 and later without P/Invokes. The SystemParametersclass now has static WindowGlassBrushand WindowGlassColorproperties along with a StaticPropertyChangedevent.
这可以在 .NET 4.5 及更高版本中完成,无需 P/Invokes。该SystemParameters类现在有静态WindowGlassBrush和WindowGlassColor了一起性质StaticPropertyChanged事件。
From XAML, you can bind to the WindowGlassBrush property like:
从 XAML,您可以绑定到 WindowGlassBrush 属性,例如:
<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 notprovide WindowGlassBrushKeyor WindowGlassColorKeyproperties to use as ResourceKeys with DynamicResource, so getting change notifications requires code behind to handle the StaticPropertyChanged event.
但是,通过此分配,当 Windows 更改其颜色时,背景颜色不会自动更新。不幸的是,SystemParameters并没有提供WindowGlassBrushKey或WindowGlassColorKey属性使用与DynamicResource ResourceKeys,所以得到变更通知要求背后来处理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();
}
}
}

