Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
В этом разделе представлен обзор визуального слоя WPF. Он сосредоточен на роли класса Visual в поддержке отрисовки в модели WPF.
Роль визуального объекта
Класс Visual — это базовая абстракция, от которой наследуется каждый FrameworkElement объект. Он также служит точкой входа для написания новых элементов управления в WPF, и во многих отношениях можно рассматривать как дескриптор окна (HWND) в модели приложения Win32.
Объект Visual является основным объектом WPF, основной ролью которого является предоставление поддержки отрисовки. Элементы управления пользовательским интерфейсом, такие как Button и TextBox, являются производными от класса Visual и используют его для сохранения данных отрисовки. Объект Visual обеспечивает поддержку:
Отображение: рендеринг сохраненного, сериализованного визуального содержимого.
Преобразования: выполнение преобразований в визуальном элементе.
Вырезка: предоставление поддержки области вырезки для визуального элемента.
Тестирование попаданий: определение того, содержится ли координата или геометрия в границах визуального элемента.
Вычисление ограничивающего прямоугольника визуального элемента.
Однако объект Visual не включает поддержку функций без отрисовки, таких как:
Обработка событий
Схема
Стили
Привязка данных
Глобализация
Visual представлен как открытый абстрактный класс, от которого должны быть производными дочерние классы. На следующем рисунке показана иерархия визуальных объектов, предоставляемых в WPF.
Класс DrawingVisual
DrawingVisual — это упрощенный класс рисования, используемый для отрисовки фигур, изображений или текста. Этот класс считается упрощенным, так как он не предоставляет разметку или обработку событий, что повышает производительность среды выполнения. По этой причине рисунки идеально подходят для фона и картинок. DrawingVisual можно использовать для создания пользовательского визуального объекта. Дополнительные сведения см. в разделе Использование объектов DrawingVisual.
Класс Viewport3DVisual
Предоставляет Viewport3DVisual мост между объектами 2D Visual и Visual3D. Класс Visual3D является базовым классом для всех трехмерных визуальных элементов. Требуется Viewport3DVisual, чтобы определить значение Camera и значение Viewport. Камера позволяет просматривать сцену. Окно просмотра определяет, где проекция проецируется на 2D-поверхность. Дополнительные сведения об 3D в WPF см. в обзоре трехмерной графики.
Класс ContainerVisual
Класс ContainerVisual используется в качестве контейнера для коллекции Visual объектов. Класс DrawingVisual является производным от ContainerVisual класса, позволяя ему содержать коллекцию визуальных объектов.
Рисование содержимого в визуальных объектах
Visual Объект сохраняет данные отрисовки в виде списка векторных графических инструкций. Каждый элемент в списке инструкций представляет низкоуровневый набор графических данных и связанных ресурсов в сериализованном формате. Различают четыре разных типа данных отрисовки, которые могут содержать содержимое для рисования.
Тип контента рисования | Описание |
---|---|
Векторная графика | Представляет векторные графические данные, а также любые связанные Brush и Pen сведения. |
Изображение | Представляет изображение в пределах региона, определенного параметром Rect. |
Глиф | Представляет рисунок, который отрисовывает объект GlyphRun, который представляет собой последовательность глифов из указанного ресурса шрифта. Это способ представления текста. |
Видео | Представляет рисунок, который отображает видео. |
Элемент DrawingContext позволяет заполнять Visual визуальным контентом. При использовании DrawingContext команд рисования объекта фактически сохраняется набор данных отрисовки, которые позже будут использоваться графической системой; вы не рисуете экран в режиме реального времени.
При создании элемента управления WPF, например Buttonэлемента управления, элемент управления неявно создает данные отрисовки для самого рисования. Например, задание Content свойства Button элемента управления вызывает хранение графического представления глифа.
A Visual описывает его содержимое как один или несколько Drawing объектов, содержащихся в объекте DrawingGroup. DrawingGroup также описывает маски непрозрачности, преобразования, эффекты растрового изображения и другие операции, которые применяются к его содержимому. DrawingGroupоперации применяются в следующем порядке при отображении содержимого: OpacityMask, Opacity, BitmapEffectClipGeometryGuidelineSetа затем .Transform
На следующем рисунке показан порядок, в котором операции DrawingGroup применяются во время отрисовки.
Порядок операций DrawingGroup
Дополнительные сведения см. в разделе "Обзор объектов рисования".
Рисование содержимого на визуальном уровне
Создать экземпляр 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;
}
' Create a DrawingVisual that contains a rectangle.
Private Function CreateDrawingVisualRectangle() As DrawingVisual
Dim drawingVisual As New DrawingVisual()
' Retrieve the DrawingContext in order to create new drawing content.
Dim drawingContext As DrawingContext = drawingVisual.RenderOpen()
' Create a rectangle and draw it in the DrawingContext.
Dim rect As New Rect(New Point(160, 100), New Size(320, 80))
drawingContext.DrawRectangle(Brushes.LightBlue, CType(Nothing, Pen), rect)
' Persist the drawing content.
drawingContext.Close()
Return drawingVisual
End Function
Перечисление графического содержимого на визуальном слое
Наряду с другими функциями, объекты Drawing также предоставляют объектную модель для перечисления содержимого объекта Visual.
Замечание
При перечислении содержимого визуального элемента вы извлекаете Drawing объекты, а не получаете основное представление данных отрисовки, которое представлено как список инструкций для векторной графики.
В следующем примере метод GetDrawing используется для получения значения DrawingGroup объекта Visual и его перечисления.
public void RetrieveDrawing(Visual v)
{
DrawingGroup drawingGroup = VisualTreeHelper.GetDrawing(v);
EnumDrawingGroup(drawingGroup);
}
// 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 is DrawingGroup group)
{
EnumDrawingGroup(group);
}
else if (drawing is GeometryDrawing)
{
// Perform action based on drawing type.
}
else if (drawing is ImageDrawing)
{
// Perform action based on drawing type.
}
else if (drawing is GlyphRunDrawing)
{
// Perform action based on drawing type.
}
else if (drawing is 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 элемента управления, чтобы оно использовало значение цвета линейного градиента вместо сплошного значения цвета. Дополнительные сведения см. в разделе "Стили кнопок" и "Шаблоны".
Элемент пользовательского интерфейса, например 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. Это означает, что между логическим деревом и визуальным деревом для одного набора объектов нет прямой связи. На самом деле вызов метода объекта LogicalTreeHelper и метода объекта VisualTreeHelper с использованием того же элемента в качестве параметра дает различные результаты.
Дополнительные сведения о логическом дереве см. в разделе Деревья в WPF.
Просмотр визуального дерева с помощью XamlPad
Средство WPF, XamlPad, предоставляет возможность просмотра и изучения визуального дерева, соответствующего текущему содержимому XAML. Нажмите кнопку "Показать визуальное дерево" в строке меню, чтобы отобразить визуальное дерево. Ниже показано расширение содержимого XAML на визуальные узлы дерева на панели обозревателя визуальных деревьев XamlPad:
Обратите внимание, как LabelTextBoxэлементы управления и Button элементы управления отображают отдельную иерархию визуальных объектов в панели обозревателя визуальных деревьев XamlPad. Это связано с тем, что элементы управления WPF содержат ControlTemplate визуальное дерево этого элемента управления. При явной ссылке на элемент управления вы неявно ссылаетесь на ее визуальную иерархию.
Профилирование визуальной производительности
В состав WPF входит пакет инструментов для профилирования производительности, с помощью которых можно проанализировать поведение приложения во время выполнения и определить, каким образом можно повысить производительность. Средство Visual Profiler предоставляет расширенное графическое представление данных о производительности путем сопоставления непосредственно с визуальным деревом приложения. На этом снимке экрана раздел Использование ЦП визуального профилировщика предоставляет точную разбивку сведений об использовании объектом служб WPF, таких как отрисовка и макет.
Выходные данные визуального профилировщика
Поведение визуальной отрисовки
WPF представляет несколько функций, влияющих на поведение отрисовки визуальных объектов: графика с сохранением состояния, векторная графика и графика, независимая от разрешения устройства.
Сохраненный режим графики
Одним из ключей для понимания роли визуального объекта является понимание разницы между непосредственным режимом и сохраненными графическими системами. Стандартное приложение Win32 на основе GDI или GDI+ использует графические системы немедленного режима. Это означает, что приложение отвечает за повторение части клиентской области, которая является недопустимой, из-за действия, такого как изменение размера окна или изменение внешнего вида объекта.
В отличие от этого, WPF использует сохраненную систему режима. Это означает, что объекты приложения, имеющие визуальный вид, определяют набор сериализованных данных рисования. После определения данных рисования система впоследствии будет отвечать на все запросы на перерисовку объектов приложения. Даже во время выполнения можно изменять или создавать объекты приложений и по-прежнему полагаться на систему для реагирования на запросы краски. Мощность в графической системе в сохраненном режиме заключается в том, что сведения о рисовании всегда сохраняются в сериализованном состоянии приложением, но отрисовка слева от системы. На следующей схеме показано, как приложение использует WPF для реагирования на запросы краски.
Интеллектуальная перерисовка
Одним из основных преимуществ использования режима сохраненных графических объектов является то, что WPF может эффективно оптимизировать, что необходимо перерисовать в приложении. Даже если у вас есть сложная сцена с различными уровнями непрозрачности, обычно не требуется писать специальный код для оптимизации перерасчета. Сравните это с программированием Win32, в котором можно приложить большие усилия для оптимизации приложения, сведя к минимуму объем перерисовки в области обновления. Дополнительные сведения см. в разделе "Перерисовка в регионе обновления" для примера сложности, связанной с оптимизацией перерисовки в приложениях Win32.
Векторная графика
WPF использует векторную графику в качестве формата данных отрисовки. Векторная графика, включающая масштабируемую векторную графику (SVG), метафайлы 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>
О разрешении и о графике Device-Independent
Существует два системных фактора, определяющих размер текста и графики на экране: разрешение и DPI. Разрешение описывает количество пикселей, отображаемых на экране. По мере того как разрешение становится выше, пиксели получают меньше, что приводит к уменьшению графики и текста. Рисунок, отображаемый на мониторе размером 1024 x 768, будет отображаться гораздо меньше, когда разрешение изменяется на 1600 x 1200.
Другой системный параметр, DPI, описывает размер экрана дюйма в пикселях. Большинство систем Windows имеют DPI 96, что означает, что экранный дюйм составляет 96 пикселей. Увеличение параметра DPI увеличивает размер экрана; уменьшение DPI уменьшает размер экрана. Это означает, что экранный дюйм не совпадает с размером реального дюйма; в большинстве систем это, вероятно, не так. По мере увеличения DPI графические элементы и текст, поддерживающие DPI, становятся крупнее, так как увеличивается воспринимаемый размер дюйма экрана. Увеличение DPI может упростить чтение текста, особенно при высоких разрешениях.
Не все приложения пользуются поддержкой DPI: некоторые используют аппаратные пиксели в качестве основной единицы измерения; Изменение системного DPI не влияет на эти приложения. Многие другие приложения используют единицы с поддержкой DPI для описания размеров шрифтов, но используют пиксели для описания всего остального. Слишком маленький или слишком большой DPI может привести к проблемам с макетом для этих приложений, так как текст приложений масштабируется в соответствии с системным значением DPI, но пользовательский интерфейс не масштабируется. Эта проблема устранена для приложений, разработанных с помощью WPF.
WPF поддерживает автоматическое масштабирование, используя пиксель, независимый от устройства, в качестве основной единицы измерения вместо аппаратных пикселей; графика и текст правильно масштабируются без дополнительной работы со стороны разработчика приложения. На следующем рисунке показан пример отображения текста и графики WPF в разных параметрах DPI.
Графика и текст в разных параметрах DPI
Класс 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);
}
}
' Enumerate all the descendants of the visual object.
Public Shared Sub EnumVisual(ByVal myVisual As Visual)
For i As Integer = 0 To VisualTreeHelper.GetChildrenCount(myVisual) - 1
' Retrieve child visual at specified index value.
Dim childVisual As Visual = CType(VisualTreeHelper.GetChild(myVisual, i), Visual)
' Do processing of the child visual object.
' Enumerate children of the child visual object.
EnumVisual(childVisual)
Next i
End Sub
В большинстве случаев логическое дерево является более полезным представлением элементов в приложении 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);
' Return the bounding rectangle of the parent visual object and all of its descendants.
Dim rectBounds As Rect = VisualTreeHelper.GetDescendantBounds(parentVisual)
См. также
.NET Desktop feedback