Обзор графической визуализации Windows Presentation Foundation
Обновлен: Ноябрь 2007
В этом разделе дан обзор визуального уровня WPF. Он посвящен роли класса Visual для поддержки визуализации в модели WPF.
В этом разделе содержатся следующие подразделы.
- Роль визуального объекта
- Использование визуальных объектов для построения элементов управления
- Визуальное дерево
- Поведение при визуальной отрисовке
- Класс VisualTreeHelper
- Связанные разделы
Роль визуального объекта
Класс Visual — это базовая абстракция, от которой наследуется каждый объект FrameworkElement. Он также служит точкой входа для написания новых элементов управления в приложении WPF и во многих случая может рассматриваться как дескриптор окна (HWND) в модели приложения Win32.
Объект Visual является ядром объекта WPF, основная цель которого — обеспечение поддержки визуализации. Элементы управления пользовательского интерфейса, такие как Button и TextBox, являются производными от класса Visual и используют его для сохранения своих визуализируемых данных. Объект Visual обеспечивает поддержку следующих возможностей.
Отображение выходных данных: визуализация сохраненного, сериализованного содержимого изображения визуального объекта.
Преобразование: выполнение преобразований визуального объекта.
Отсечение: обеспечение поддержки отсечения области визуального объекта.
Проверка нажатия: определение, содержатся ли координаты или контур в границах визуального объекта.
Вычисления ограничивающего прямоугольника: определение ограничивающего прямоугольника визуального объекта.
Однако объект Visual не включает поддержку не визуализируемых возможностей, перечисленных ниже.
Обработка событий
Разметка
Стили
Привязка данных
Глобализация
Объект Visual предоставляется как открытый абстрактный класс, от которого должны наследовать дочерние классы. На следующем рисунке показана иерархия визуальных объектов, которые представлены в приложении WPF.
Иерархия визуальных классов
Класс DrawingVisual
Объект DrawingVisual представляет собой облегченный класс рисования, который используется для отображения фигур, рисунков или текста. Этот класс считается облегченным, поскольку он не предоставляет возможности разметки или обработки событий, что повышает его производительность во время выполнения. По этой причине эти объекты идеально подходят для фоновых рисунков и коллекций картинок. Объект DrawingVisual можно использовать для создания пользовательского визуального объекта. Дополнительные сведения см. в разделе Использование объектов DrawingVisual.
Класс Viewport3DVisual
Объект Viewport3DVisual обеспечивает связь между двухмерными объектами Visual и объектами Visual3D. Класс Visual3D является базовым для всех трехмерных визуальных элементов. Для объекта Viewport3DVisual требуется определение значений Camera и Viewport. Камера позволяет просмотреть сцену. Окно просмотра устанавливает место отображения проекции на двумерной поверхности. Дополнительные сведения о трехмерных объектах в приложении WPF см. в разделе Обзор трехмерной графики.
Класс ContainerVisual
Класс ContainerVisual используется в качестве контейнера для коллекции объектов Visual. Класс DrawingVisual наследует от класса ContainerVisual, что позволяет ему содержать коллекцию визуальных объектов.
Рисование содержимого в визуальных объектах
Объект Visual хранит данные визуализации в виде списка инструкций векторной графики. Каждый элемент в списке инструкций представляет набор графических данных нижнего уровня и связанных ресурсов в сериализованном формате. Существуют четыре различных типа данных визуализации, которые могут содержать графическое содержимое.
Тип графического содержимого |
Описание |
---|---|
Векторная графика |
Представляет данные векторной графики и любую информацию, связанную с объектами Brush и Pen. |
Изображение |
Представляет изображение в пределах области, определенной объектом Rect. |
Глиф |
Представляет рисунок, отображающий объект GlyphRun, который представляет собой последовательность глифов из указанного ресурса шрифтов. Это способ представления текста. |
Видео |
Представляет рисунок, который отображает видео. |
Объект DrawingContext позволяет заполнить объект Visual визуальным содержимым. При использовании команд рисования объекта DrawingContext фактически происходит сохранение набора данных визуализации, которые позднее будут использоваться графической системой; рисование не выполняется на экране в режиме реального времени.
При создании элемента управления WPF, такого как Button, этот элемент управления неявно создает данные визуализации для рисования самого себя. Например, при установке свойства Content объекта Button элемент управления сохраняет визуальное представление глифа.
Объект Visual описывает свое содержимое в виде одного или нескольких объектов Drawing, содержащихся в группе DrawingGroup. Группа DrawingGroup также описывает маски прозрачности, преобразования, эффекты точечного рисунка и другие операции, которые применяются к ее содержимому. При отображении содержимого операции DrawingGroup применяются в следующем порядке: OpacityMask, Opacity, BitmapEffect, ClipGeometry, GuidelineSet и затем Transform.
Ниже показан порядок, в котором операции DrawingGroup применяются во время визуализации последовательности.
Порядок операций DrawingGroup
Дополнительные сведения см. в разделе Обзор объектов Drawing.
Отображение содержимого на уровне визуализации
Нельзя непосредственно создать экземпляр DrawingContext; однако можно получить контекст рисования из определенных методов, таких как DrawingGroup.Open и DrawingVisual.RenderOpen. В следующем примере извлекается DrawingContext из класса DrawingVisual и использует его, чтобы нарисовать прямоугольник.
// Create a DrawingVisual that contains a rectangle.
private DrawingVisual CreateDrawingVisualRectangle()
{
DrawingVisual drawingVisual = new DrawingVisual();
// Retrieve the DrawingContext in order to create new drawing content.
DrawingContext drawingContext = drawingVisual.RenderOpen();
// Create a rectangle and draw it in the DrawingContext.
Rect rect = new Rect(new System.Windows.Point(160, 100), new System.Windows.Size(320, 80));
drawingContext.DrawRectangle(System.Windows.Media.Brushes.LightBlue, (System.Windows.Media.Pen)null, rect);
// Persist the drawing content.
drawingContext.Close();
return drawingVisual;
}
Перечисление содержимого рисования на уровне визуализации
В дополнение к другим применениям, объекты Drawing также предоставляют объектную модель для перечисления содержимого объекта Visual.
Примечание. |
---|
При перечислении содержимого визуального объекта извлекаются объекты Drawing, а не базовое представление данных визуализации в виде списка инструкций векторной графики. |
В следующем примере метод GetDrawing используется для извлечения значения DrawingGroup объекта Visual и его перечисления.
public void RetrieveDrawing(Visual v)
{
DrawingGroup dGroup = VisualTreeHelper.GetDrawing(v);
EnumDrawingGroup(dGroup);
}
// Enumerate the drawings in the DrawingGroup.
public void EnumDrawingGroup(DrawingGroup drawingGroup)
{
DrawingCollection dc = drawingGroup.Children;
// Enumerate the drawings in the DrawingCollection.
foreach (Drawing drawing in dc)
{
// If the drawing is a DrawingGroup, call the function recursively.
if (drawing.GetType() == typeof(DrawingGroup))
{
EnumDrawingGroup((DrawingGroup)drawing);
}
else if (drawing.GetType() == typeof(GeometryDrawing))
{
// Perform action based on drawing type.
}
else if (drawing.GetType() == typeof(ImageDrawing))
{
// Perform action based on drawing type.
}
else if (drawing.GetType() == typeof(GlyphRunDrawing))
{
// Perform action based on drawing type.
}
else if (drawing.GetType() == typeof(VideoDrawing))
{
// Perform action based on drawing type.
}
}
}
Использование визуальных объектов для построения элементов управления
Многие объекты в приложении WPF состоят из других визуальных объектов, то есть они могут содержать различные иерархии объектов-потомков. Многие элементы пользовательского интерфейса в приложении WPF, такие как элементы управления, состоят из нескольких визуальных объектов, представляющих различные типы отрисовываемых элементов. Например, элемент управления Button может содержать другие объекты, включая ClassicBorderDecoratorContentPresenter и TextBlock.
В следующем коде показан элемент управления Button, определенный в разметке.
<Button Click="OnClick">OK</Button>
Если нужно перечислить визуальные объекты, составляющие элемент управления Button по умолчанию, необходимо найти иерархию визуальных объектов, как показано ниже.
Схема иерархии визуального дерева
Элемент управления Button содержит элемент ClassicBorderDecorator, который, в свою очередь, содержит элемент ContentPresenter. Элемент ClassicBorderDecorator отвечает за рисование границ и фона объекта Button. Элемент ContentPresenter отвечает за отображение содержимого объекта Button. В данном случае, поскольку отображается текст, элемент ContentPresenter содержит элемент TextBlock. Тот факт, что элемент управления Button использует элемент ContentPresenter, означает, что содержимое может быть представлено другими элементами, такими как Image, или геометрическим объектом, таким как EllipseGeometry.
Шаблоны элементов управления
Основным компонентом для включения элемента управления в иерархию элементов управления является класс ControlTemplate. Шаблон элемента управления определяет для элемента управления визуальную иерархию по умолчанию. При явной ссылке на элемент управления происходит неявное обращение к его визуальной иерархии. Можно переопределить значения по умолчанию для шаблона элемента управления, чтобы создать пользовательский внешний вид элемента управления. Например, можно изменить значение цвета фона элемента управления Button таким образом, чтобы он использовал значение цвета линейного градиента вместо значения сплошного цвета. Дополнительные сведения см. в разделе Пример шаблона ControlTemplate для кнопки.
Элемент пользовательского интерфейса, такой как элемент управления Button, содержит несколько списков инструкций векторной графики, описывающих полное определение отображения элемента управления. В следующем коде показан элемент управления Button, определенный в разметке.
<Button Click="OnClick">
<Image Source="images\greenlight.jpg"></Image>
</Button>
Если нужно перечислить визуальные объекты и списки инструкций векторной графики, составляющих элемент управления Button, необходимо найти иерархию объектов, как показано ниже.
Схема визуального дерева и отображения данных
Элемент управления Button содержит элемент ClassicBorderDecorator, который, в свою очередь, содержит элемент ContentPresenter. Элемент ClassicBorderDecorator отвечает за рисование всех отдельных графических элементов, составляющих границу и фон кнопки. Элемент ContentPresenter отвечает за отображение содержимого Button. В данном случае, поскольку выполняется отображение изображения, элемент ContentPresenter содержит элемент Image.
Существует несколько моментов, на которые следует обратить внимание при рассмотрении иерархии визуальных объектов и списков инструкций векторной графики.
Порядок в иерархии представляет порядок отображения графической информации. От корневого визуального элемента дочерние элементы следуют слева направо, сверху вниз. Если элемент имеет визуальные дочерние элементы, то они следуют до элементов того же уровня.
Неконечные элементы узла в иерархии, например ContentPresenter, используются для содержания дочерних элементов — они не содержат списки инструкций.
Если визуальный элемент содержит список инструкций векторной графики и визуальные дочерние объекты, то список инструкций в родительском визуальном элементе отображается перед изображениями в любом из визуальных дочерних объектов.
Элементы в списке инструкций векторной графики отображаются слева направо.
Визуальное дерево
Визуальное дерево содержит все визуальные элементы, которые используются в пользовательском интерфейсе приложения. Поскольку визуальный элемент содержит сохраненную графическую информацию, можно представить визуальное дерево как граф сцены, содержащий все сведения визуализации, необходимые для формирования выходных данных для отображения на дисплее. Это дерево является набором всех визуальных элементов, созданных непосредственно приложением либо в коде, либо в разметке. Визуальное дерево также содержит все визуальные элементы, созданные расширением шаблона элементов, таких как элементы управления и объекты данных.
В следующем коде показан элемент StackPanel, определенный в разметке.
<StackPanel>
<Label>User name:</Label>
<TextBox />
<Button Click="OnClick">OK</Button>
</StackPanel>
Если нужно перечислить визуальные объекты, которые входят в состав элемента StackPanel в примере разметки, необходимо найти иерархию визуальных объектов, как показано ниже.
Схема иерархии визуального дерева
Порядок отрисовки
Визуальное дерево определяет порядок отрисовки визуальных и графические объектов WPF. Порядок обхода начинается с корневого визуального элемента, который является самым верхним узлом визуального дерева. Потомки корневого визуального элемента следуют слева направо. Если визуальный элемент имеет дочерние элементы, они следуют до визуальных элементов одного уровня. Это означает, что содержимое дочерних визуальных элементов отображается перед собственным содержимым визуального элемента.
Схема порядка отрисовки визуального дерева
Корневой визуальный элемент
Корневой визуальный элемент является элементом иерархии визуального дерева верхнего уровня. В большинстве приложений базовым классом корневого визуального элемента является Window либо NavigationWindow. Однако если визуальные объекты размещены в приложения Win32, то корневой визуальный элемент является визуальным узлом верхнего уровня окна Win32. Дополнительные сведения см. в разделе Руководство по размещению визуальных объектов в приложении Win32.
Связь с логическим деревом
Логическое дерево в WPF представляет элементы приложения во время выполнения. Хотя напрямую этим деревом нельзя управлять, это представление приложения полезно для понимания наследования свойств и перенаправления событий. В отличие от визуального дерева, логическое дерево может представлять невизуальные объекты данных, такие как ListItem. В большинстве случаев логическое дерево очень близко сопоставлено определениям разметки приложения. В следующем коде показан элемент DockPanel, определенный в разметке.
<DockPanel>
<ListBox>
<ListBoxItem>Dog</ListBoxItem>
<ListBoxItem>Cat</ListBoxItem>
<ListBoxItem>Fish</ListBoxItem>
</ListBox>
<Button Click="OnClick">OK</Button>
</DockPanel>
Если нужно перечислить логические объекты, которые входят в состав элемента DockPanel в примере разметки, необходимо найти иерархию логических объектов, как показано ниже.
Схема логического дерева
И визуальное, и логическое дерево синхронизируются с текущим набором элементов приложения, отражая любое добавление, удаление или изменение элементов. Однако эти деревья представляют различные представления приложения. В отличие от визуального дерева, логическое дерево не расширяет элемент ContentPresenter элемента управления. Это означает, что не существует прямого однозначного соответствия между логическим деревом и визуальным деревом для одного набора объектов. Фактически вызов метода GetChildren объекта LogicalTreeHelper и метода GetChild объекта VisualTreeHelper с помощью одного и того же элемента в качестве параметра дает разные результаты.
Дополнительные сведения о логическом дереве см. в разделе Деревья в WPF.
Просмотр визуального дерева с помощью средства XamlPad
Средство XamlPad приложения WPF предоставляет возможность для просмотра и анализа визуального дерева, соответствующего текущему определенному содержимому XAML. Нажмите кнопку Показать визуальное дерево в строке меню для отображения визуального дерева. Ниже показано развертывание содержимого XAML в узлы визуального дерева в панели Обозреватель визуального дерева средства XamlPad:
Панель обозревателя визуального дерева в XamlPad
Обратите внимание, как элементы управления Label, TextBox и Button отображают отдельные иерархии визуальных объектов в панели Обозреватель визуального дерева XamlPad. Это происходит потому, что элементы управления WPF имеют объект ControlTemplate, содержащий визуальное дерево этого элемента управления. При явной ссылке на элемент управления происходит неявное обращение к его визуальной иерархии.
Анализ производительности визуальными средствами
WPF предоставляет набор средств профилирования производительности, позволяющих анализировать поведение приложения во время выполнения и определять типы оптимизации производительности для применения. Средство Visual Profiler обеспечивает широкие возможности графического представления данных о производительности путем их сопоставления непосредственно с визуальным деревом приложения. На этом снимке с экрана раздел Использование ЦП средства Visual Profiler позволяет выбрать точную схему использования объектом служб WPF, таких как отрисовка и разметка.
Вывод на экран данных Visual Profiler
Дополнительные сведения о средствах производительности WPF см. в разделе Средства профилирования производительности для WPF.
Поведение при визуальной отрисовке
Приложение WPF включает несколько возможностей, влияющих на отрисовку визуальных объектов: графика, отображаемая после группирования объектов, векторная графика и аппаратно-независимая графика.
Сохраненные графические режимы
Одной из основ для понимания роли объекта Visual является понимание различия между графическими системами режима интерпретации и сохраненного режима. В стандартном приложении Win32 на основе GDI или GDI+ используется графическая система режима интерпретации. Это означает, что за обновление части клиентской области, ставшей недопустимой в результате какого-либо действия, такого как изменения размера окна или изменения внешнего вида объекта, отвечает приложение.
Схема последовательности отрисовки Win32
В WPF, напротив, используется система сохраненного режима. Это означает, что объекты приложения, имеющие визуальное представление, определяют набор сериализованных графических данных. После определения графических данных за все запросы на перерисовку для отрисовываемых объектов приложения отвечает система. Даже во время выполнения можно изменить или создать объекты приложения и по-прежнему полагаться на систему, отвечающую на запросы перерисовки. Преимущество сохраненного режима графической системы заключается в том, что графические данные всегда сохраняются приложением в сериализованном состоянии, но за отрисовку по-прежнему отвечает система. На следующей схеме показано, как приложение использует WPF для ответа на запросы рисования.
Схема последовательности отрисовки WPF
Интеллектуальная перерисовка
Одним из преимуществ использования сохраненного режима для отображения графики является то, что WPF может эффективно оптимизировать необходимую перерисовку в приложении. Даже при наличии сложной сцены с различными уровнями прозрачности, как правило, необязательно писать специальный код для оптимизации перерисовки. Сравните с программированием Win32, в котором можно потратить значительные усилия при оптимизации приложения путем уменьшения объема перерисовки в области обновления. Пример сложной оптимизации перерисовки в приложениях Win32 см. в разделе Перерисовка в области обновления.
Векторная графика
Приложение WPF использует векторную графику в качестве формата данных отрисовки. Векторная графика — включающая SVG (Scalable Vector Graphics), метафайлы Windows (wmf) и шрифты TrueType — хранит данные отрисовки и передает их в виде списка инструкций, описывающих, как воссоздать изображение, используя графические примитивы. Например, шрифты TrueType являются контурными шрифтами, которые описывают набор линий, кривых и команд, а не массив точек. Одним из ключевых преимуществ векторной графики является возможность масштабирования до любого размера и разрешения.
В отличие от векторной графики, растровая графика хранит данные отрисовки как поточечное представление изображения, что в совокупности дает изображение, подготовленное для определенного разрешения. Одним из основных различий между растровым и векторным графическими форматами является соответствие с исходным изображением. Например, при изменении размера исходного изображения растровые графические системы растягивают изображение, тогда как векторные графические системы масштабируют изображения, сохраняя его качество.
На следующем рисунке показано исходное изображение, размер которого изменен на 300%. Обратите внимание, что в результате растягивания исходного точечного изображения возникают искажения, в отличие от растягивания векторного графического изображения.
Различия между растровой и векторной графикой
В следующей разметке показаны два определенных элемента Path. Второй элемент использует ScaleTransform, чтобы изменить размер инструкций рисования первого элемента на 300%. Обратите внимание, что инструкции рисования в элементах Path остаются неизменными.
<Path
Data="M10,100 C 60,0 100,200 150,100 z"
Fill="{StaticResource linearGradientBackground}"
Stroke="Black"
StrokeThickness="2" />
<Path
Data="M10,100 C 60,0 100,200 150,100 z"
Fill="{StaticResource linearGradientBackground}"
Stroke="Black"
StrokeThickness="2" >
<Path.RenderTransform>
<ScaleTransform ScaleX="3.0" ScaleY="3.0" />
</Path.RenderTransform>
</Path>
Разрешение и аппаратно-независимая графика
Существуют два фактора системы, определяющих размер текста и графики на экране: разрешение и число точек на дюйм (DPI). Разрешение описывает число точек, отображаемых на экране. Чем выше разрешение, тем меньше пиксели, в результате чего уменьшается размер отображаемой графики и текста. Размер рисунка на мониторе с разрешением 1024 x 768 станет намного меньше, если изменить разрешение на 1600 x 1200.
Другой системный параметр, число точек на дюйм, описывает размер дюйма экрана в точках. Большинство систем Windows имеют значение точек на дюйм, равное 96; это означает, что дюйм экрана содержит 96 точек. При увеличении количества точек на дюйм увеличивается дюйм экрана; при уменьшение количества точек на дюйм экрана уменьшается. Это означает, что дюйм экрана не соответствует размеру реального дюйма; в большинстве систем это, скорее всего, не так. При увеличении количества точек на дюйм изображения и текст, в которых учитывается этот параметр, становятся большего размера, так как был увеличен размер экранного дюйма. Увеличение количества точек на дюйм позволяет облегчить чтение текста, особенно при высоких разрешениях.
Не все приложения учитывают количество точек на дюйм: в некоторых приложениях в качестве единицы измерения используются аппаратно-зависимые пиксели; изменение количества точек на дюйм системы не влияет на эти приложения. Многие приложения используют единицы измерения, зависящие от количества точек на дюйм, для описания размеров шрифта, но для описания остальных элементов используют пиксели. Установка слишком маленького или слишком большого количества точек на дюйм может вызвать проблемы разметки для этих приложений, так как текст приложения масштабируется, используя количества точек на дюйм системы, а пользовательский интерфейс приложений — не использует. Данная проблема отсутствует для приложений, разработанных с использованием WPF.
WPF поддерживает автоматическое масштабирование с помощью аппаратно-независимой точки в качестве основной единицы измерения; вместо аппаратно-зависимых точек; изображения и текст масштабируются правильно без какого-либо дополнительного участия со стороны разработчика приложения. На следующем рисунке показан пример того, как текст и изображения WPF отображаются с различными параметрами количества точек на дюйм.
Изображения и текст с различными параметрами количества точек на дюйм
Класс VisualTreeHelper
Класс VisualTreeHelper является статическим вспомогательным классом, предоставляющим низкоуровневые функции для программирования на уровне визуализации объекта, что полезно в определенных случаях, таких как разработка пользовательских элементов управления высокой производительности. В большинстве случаев, объекты высокого уровня структуры WPF, такие как Canvas и TextBlock, предоставляют большую гибкость и просты в использовании.
Проверка нажатия
Класс VisualTreeHelper предоставляет методы проверки нажатия визуальных объектов, если стандартная проверки нажатия не соответствует потребностям пользователя. Можно использовать методы HitTest в классе VisualTreeHelper, чтобы определить, находится ли геометрический объект или значение координаты точки внутри границы данного объекта, такого как элемент управления или графический элемент. Например, можно использовать проверку нажатия, чтобы определить, попал ли щелчок ограничивающего прямоугольника объекта в границы фигуры круга. Также можно переопределить реализацию по умолчанию проверки нажатия для выполнения пользовательских вычислений проверки нажатия.
Дополнительные сведения о проверки нажатия см. в разделе Проверка попадания на визуальном уровне.
Перечисление визуального дерева
Класс VisualTreeHelper предоставляет функциональные возможности для перечисления членов визуального дерева. Чтобы извлечь родительский объект, вызовите метод GetParent. Чтобы извлечь дочерний элемент или прямой потомок визуального объекта, вызовите метод GetChild. Этот метод возвращает дочерний элемент Visual родительского объекта по указанному индексу.
В следующем примере показано, как перечислить всех потомков визуального объекта. Этот метод можно использовать, если нужно сериализовать всю отрисовываемую информацию иерархии визуального объекта.
// Enumerate all the descendants of the visual object.
static public void EnumVisual(Visual myVisual)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(myVisual); i++)
{
// Retrieve child visual at specified index value.
Visual childVisual = (Visual)VisualTreeHelper.GetChild(myVisual, i);
// Do processing of the child visual object.
// Enumerate children of the child visual object.
EnumVisual(childVisual);
}
}
В большинстве случаев логическое дерево является более полезным представлением элементов в приложении WPF. Хотя напрямую логическое дерево нельзя изменить, это представление приложения полезно для понимания наследования свойств и перенаправления событий. В отличие от визуального дерева, логическое дерево может представлять невизуальные объекты данных, такие как ListItem. Дополнительные сведения о логическом дереве см. в разделе Деревья в WPF.
Класс VisualTreeHelper предоставляет методы для возвращения ограничивающего прямоугольника визуальных объектов. Можно вернуть ограничивающий прямоугольник визуального объекта путем вызова метода GetContentBounds. Можно вернуть ограничивающий прямоугольник всех потомков визуального объекта, включая сам визуальный объект, вызвав метод GetDescendantBounds. В следующем коде показано, как выполнить вычисление ограничивающего прямоугольника визуального объекта и всех его потомков.
// Return the bounding rectangle of the parent visual object and all of its descendants.
Rect rectBounds = VisualTreeHelper.GetDescendantBounds(parentVisual);
См. также
Основные понятия
Оптимизация производительности: двумерная графика и обработка изображений
Проверка попадания на визуальном уровне
Использование объектов DrawingVisual
Руководство по размещению визуальных объектов в приложении Win32
Улучшение производительности приложений WPF