Ответ 1
Да, это возможно. Однако будьте осторожны: это включает в себя довольно много Win32 interop (это означает, что P/вызывает в собственные DLL из управляемого кода) и работает только с некоторыми недокументированными API. Хотя единственными недокументированными функциями являются получение цветовой схемы окна (или, как называет DWM, цвет раскраски окна), который рассматривается в этом другом вопросе:
Vista/7: Как получить цвет стекла?
В моем собственном проекте я пользуюсь вызовом 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).
Взяв какой-то пример кода из моих ответов:
- Как сделать окно WPF подвижным, перетащив расширенный оконный фрейм?
- Обнаружить изменение темы системы в WPF
Имеем:
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 перейдет по цветовой схеме окна, ваши градиенты будут плавно перемещаться вместе с остальными рамами окна.
Помните, что вам может потребоваться учитывать ситуации, когда DWM недоступен, например, при работе в Windows XP или при работе в Windows Vista или более поздней версии с отключением рабочего стола. Вы также захотите, чтобы вы не злоупотребляли этим, или вы можете столкнуться с значительным успехом и замедлить свое приложение.