Windows 8 では、配色を自動に設定し、x 分後に壁紙が変更されるように構成しました。アクティブな壁紙に合わせて配色が変化します。
私は WPF アプリケーションを開発していますが、現在の壁紙に合わせて Windows が配色を変更したときにグラデーションを変更したいと考えています。
現在/実際の配色を取得し、C# の変更を通知する方法はありますか?
これは、P/Invokes なしで .NET 4.5 以降で実行できます。SystemParametersクラスには、静的なWindowGlassBrushおよびWindowGlassColorプロパティと、StaticPropertyChangedイベントが含まれるようになりました。
XAML から、次のように WindowGlassBrush プロパティにバインドできます。
<Grid Background="{x:Static SystemParameters.WindowGlassBrush}">
ただし、この割り当てでは、Windows が色を変更しても、背景色は自動的に更新されません。残念ながら、 SystemParameters は、DynamicResource で ResourceKeys として使用するWindowGlassBrushKeyまたはWindowGlassColorKeyプロパティを提供しないため、変更通知を取得するには、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();
}
}
}
はい、可能です。ただし、注意してください: これにはかなりの Win32 相互運用機能 (マネージ コードからネイティブ DLL への P/ Invokes を意味します) が含まれており、文書化されていない特定の API でのみ実行できます。ただし、関連する文書化されていない唯一の機能は、ウィンドウの配色 (または、DWM ではウィンドウの色付けの色と呼ばれる) を取得するためのものです。これについては、この他の質問で説明されています。
私自身のプロジェクトでは、次の呼び出しを利用しています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 がウィンドウの配色を移行するときに、グラデーションが残りのウィンドウ フレームと同様にスムーズに移行することがわかります。
Windows XP で実行している場合や、デスクトップ コンポジションが無効になっている Windows Vista 以降で実行している場合など、DWM を使用できない状況を考慮しなければならない場合があることに注意してください。また、これを過度に使用しないようにする必要があります。そうしないと、パフォーマンスが大幅に低下し、アプリの速度が低下する可能性があります。