Эффект AeroGlass
89WPF --- Элементы управления WPF --- Эффект AeroGlass
Удивительно, но в WPF по-прежнему не предлагается встроенного способа для использования эффекта Aero Glass (прозрачность Aero), который впервые появился в Windows Vista и продолжает поддерживаться в Windows 7. Этот эффект обеспечивает окна размытыми стеклянными рамками, через которые можно видеть другие окна и их содержимое.
Прежде чем двигаться дальше, важно отметить, что в среде Windows ХР эффект Aero Glass работать не будет. Поэтому во избежание проблем понадобится писать код, проверяющий версию операционной системы и аккуратно отключающий этот эффект, когда он не поддерживается. Самым простым способом для определения, в среде ли Windows Vista происходит выполнение, является чтение статического свойства OSVersion класса System.Environment:
if (Environment.OSVersion.Version.Major >= 6)
{
// Функциональные возможности Vista поддерживаются.
}
В приложениях, выполняющихся под управлением Windows Vista и Windows 7, эффект Aero Glass появляется автоматически в неклиентской области окна. В случае если приложение отображает стандартное окно со стандартной рамкой в WPF и выполняется на поддерживающем интерфейс Aero компьютере, окно получается с привлекательной полупрозрачной рамкой.
Для поддержки интерфейса Aero на компьютере должна быть установлена современная версия Windows, отличная от Windows ХР, Windows Vista Home Basic и Windows 7 Starter. Кроме того, должна поддерживаться соответствующая видеокарта, а также включена функция Aero Glass.
В некоторых приложениях эффект Aero Glass распространяется и на клиентскую область окна. К числу таких приложений относится Internet Explorer, где стеклянный эффект захватывает и строку адреса, а также проигрыватель Windows Media, где стеклянный эффект применяется по отношению к элементам управления воспроизведением. Такие чудеса можно творить и в своих собственных приложениях, учитывая два описанных ниже ограничения:
Область размытого стекла всегда начинается с краев окна. Это означает, что создать стеклянную "вставку" где-нибудь в середине окна нельзя. Однако добиться такого эффекта можно, просто разместив на стеклянной рамке полностью непрозрачные элементы WPF.
Не стеклянная область внутри окна всегда определяется как прямоугольник.
В WPF нет классов для реализации такого эффекта. Вместо этого здесь требуется вызывать функцию DwmExtendFrameIntoClientArea() из Win32 API. (Префикс Dwm означает Desktop Window Manager — диспетчер окон рабочего стола, который отвечает за управление данным эффектом.) Вызов этой функции позволяет расширить рамку так, чтобы она захватывала и клиентскую область окна, делая толще какой-то один или все края. Ниже показано, как импортировать функцию DwmExtendFramelntoClientArea(), чтобы получить возможность ее вызова внутри приложения:
[DllImport("DwmApi.dll")]
public static extern int DwmExtendFrameIntoClientArea(
IntPtr hwnd,
ref Margins pMarInset);
Также необходимо определить фиксированную структуру Margins:
[StructLayout(LayoutKind.Sequential)]
public struct Margins
{
public int cxLeftWidth;
public int cxRightWidth;
public int cyTopHeight;
public int cyBottomHeight;
}
Здесь имеется потенциальная сложность. Как упоминалось ранее, в системе измерений WPF используются независимые от устройства единицы, размер которых устанавливается на основе настройки DPI системы. Однако в функции DwmExtendFramelntoClientArea() применяются физические пиксели. Для гарантии того, что элементы WPF будут присоединяться к расширенной стеклянной рамке независимо от параметров DPI системы, эта настройка DPI должна учитываться при расчетах.
Самой простой способ извлечь значения настройки DPI системы — воспользоваться классом System.Drawing.Graphics, который имеет два свойства — DpiX и DpiY, представляющие DPI для окна. Ниже показан вспомогательный метод, который берет дескриптор окна и набор единиц WPF и возвращает объект Margin с откорректированными соответствующим образом размерами в физических пикселях:
public static Margins GetDpiAdjustedMargins(IntPtr windowHandle, int left, int right, int top, int bottom)
{
// Получить Dpi системы
System.Drawing.Graphics desktop = System.Drawing.Graphics.FromHwnd(windowHandle);
float DesktopDpiX = desktop.DpiX;
float DesktopDpiY = desktop.DpiY;
// Установка полей
VistaGlassHelper.Margins margins = new VistaGlassHelper.Margins();
margins.cxLeftWidth = Convert.ToInt32(left * (DesktopDpiX / 96));
margins.cxRightWidth = Convert.ToInt32(right * (DesktopDpiX / 96));
margins.cyTopHeight = Convert.ToInt32(top * (DesktopDpiX / 96));
margins.cyBottomHeight = Convert.ToInt32(right * (DesktopDpiX / 96));
return margins;
}
К сожалению, класс System.Drawing.Graphics является частью Windows Forms. Поэтому для получения доступа к нему понадобится добавить ссылку на сборку System.Drawing.dll.
Последнее, что осталось сделать — применить поля в отношении окна с помощью функции DwmExtendFramelntoClientArea(). В следующем коде показан объединенный вспомогательный метод, который принимает параметры полей WPF и ссылку на окно WPF, после чего извлекает Win32-дескриптор окна, корректирует поля и пытается расширить стеклянную рамку:
public static void ExtendGlass(Window win, int left, int right, int top, int bottom)
{
// Получение Win32-дескриптора для окна WPF
WindowInteropHelper windowInterop = new WindowInteropHelper(win);
IntPtr windowHandle = windowInterop.Handle;
HwndSource mainWindowSrc = HwndSource.FromHwnd(windowHandle);
mainWindowSrc.CompositionTarget.BackgroundColor = Colors.Transparent;
VistaGlassHelper.Margins margins =
VistaGlassHelper.GetDpiAdjustedMargins(windowHandle, left, right, top, bottom);
int returnVal = VistaGlassHelper.DwmExtendFrameIntoClientArea(mainWindowSrc.Handle, ref margins);
if (returnVal < 0)
{
throw new NotSupportedException("Operation failed.");
}
}
Все эти "ингредиенты" упаковываются в единственный класс по имени VistaGlassHelper, который может вызываться из любого окна. Для того чтобы код работал, он должен вызываться перед отображением окна. Идеальной возможностью для этого является событие Window.Loaded. Кроме того, нужно не забыть установить свойство Background в Transparent, чтобы стеклянная рамка просматривалась сквозь поверхность рисования WPF.
На рисунке ниже показан пример утолщения верхнего края стеклянной рамки:
При создании такого окна содержимое в верхней части группируется в один элемент Border. Это позволяет замерить высоту границы (т.е. элемента Border) и использовать это значение для расширения стеклянной рамки. (Конечно, стеклянная рамка устанавливается только один раз, а именно — при первом создании окна. В случае изменения содержимого или размеров окна и увеличения или сжатия элемента Border этот элемент больше не будет присоединяться к стеклянной рамке.)
При желании расширить стеклянный край так, чтобы он захватывал все окно целиком, следует просто передать для каждой стороны в качестве параметра поля значение -1. Ниже показано, как будет выглядеть окно в таком случае.
При использовании эффекта Aero Glass также необходимо учитывать и то, как внешний вид содержимого будет меняться в случае размещения окна поверх других фоновых изображений. Например, при размещении в стеклянной области окна текста черного цвета, этот текст будет легче читать на светлом фоне, чем на темном (хотя разборчивым он будет в обоих случаях). Для повышения удобочитаемости текста и обеспечения четкого отображения содержимого на разных фонах принято добавлять какой-нибудь эффект подсвечивания.
Например, черный цвет с белой подсветкой будет выглядеть одинаково разборчиво как на светлом, так и на темном фоне. Windows Vista предлагает неуправляемую функцию для рисования эффекта подсветки, которая называется DrawThemeTextEx(). Однако у WPF имеется множество собственных приемов, позволяющих получать точно такой же (если не лучше) результат. Двумя наиболее яркими примерами таких приемов являются использование декоративной кисти для закрашивания текста и добавление к тексту растрового эффекта.
В примере использования эффекта Aero Glass было показано, как с помощью функции DwmExtendFrameIntoClientArea() можно создавать утолщенный стеклянный край (или даже вообще полностью стеклянное окно). Однако DwmExtendFrameIntoClientArea() является далеко не единственной полезной функцией в API-интерфейсе Windows. Множество других API-функций, имена которых начинаются с Dwm, позволяют взаимодействовать с диспетчером окон рабочего стола.
Например, с помощью функции DwmIsCompositionEnabled() можно проверить, включен ли эффект Aero Glass, а функция DwmEnableBlurBehindWindow() позволяет применить стеклянный эффект к конкретной области в окне. Кроме того, также существует несколько функций, которые позволяют получать живое миниатюрное представление других приложений.