Рисунки
26WPF --- Графика и анимация WPF --- Рисунки
Как уже известно, абстрактный класс Geometry представляет фигуру или путь. Абстрактный класс Drawing играет дополняющую роль. Он представляет двухмерные рисунки — другими словами, содержит всю информацию, которая нужна для вывода части векторного или растрового изображения.
Хотя существует несколько типов классов рисунков, среди них GeometryDrawing — один из тех, которые работают с рассмотренными ранее геометриями. Он добавляет детали очерчивания границ и заполнения областей — т.е. описывает, как геометрия должна быть нарисована. Объект GeometryDrawing можно воспринимать как единственную фигуру в части векторного рисунка. Например, можно преобразовать стандартный файл .wmf в коллекцию объектов GeometryDrawing, готовых к вставке в пользовательский интерфейс.
Давайте рассмотрим простой пример. Ранее было показано, как определяется простой объект PathGeometry, представляющий треугольник:
<PathGeometry>
<PathFigure IsClosed="True" StartPoint="10,100">
<LineSegment Point="100,100" />
<LineSegment Point="100,50" />
</PathFigure>
</PathGeometry>
Этот объект PathGeometry можно применить для построения GeometryDrawing следующим образом:
<GeometryDrawing Вrush="Yellow">
<GeometryDrawing.Pen>
<Pen Brush="Blue" Thickness = "3"></Pen>
</GeometryDrawing.Pen>
<GeometryDrawing.Geometry>
<PathGeometry>
<PathFigure IsClosed="True" StartPoint="10,100">
<LineSegment Point="100,100" />
<LineSegment Point="100,50" />
</PathFigure>
</PathGeometry>
</GeometryDrawing.Geometry>
</GeometryDrawing>
Здесь PathGeometry определяет фигуру (треугольник). GeometryDrawing задает внешний вид фигуры (желтый треугольник с синим контуром). Ни PathGeometry, ни GeometryDrawing не являются элементами, так что ни тот, ни другой нельзя использовать непосредственно для добавления самостоятельно нарисованного содержимого в окно. Вместо этого нужно будет применять другой класс, поддерживающий рисование.
Класс GeometryDrawing вводит новую деталь: класс System.Windows.Media.Pen. Класс Pen (перо) предоставляет свойства Brush и Thickness, использованные в предыдущем примере, наряду со свойствами, относящимися к штрихам, о которых говорилось во время обсуждения фигур (StartLine, EndLineCap, DashStyle, DashCap, LineJoin и MiterLimit). Фактически большинство классов-наследников Shape внутренне применяют объекты Pen в своем коде рисования, но предлагают связанные с перьями свойства для непосредственного использования.
GeometryDrawing — не единственный класс для рисования в WPF (хотя он и наиболее важный, когда речь идет о двухмерной векторной графике). Фактически класс Drawing предназначен для того, чтобы представлять все типы двухмерной графики, и только небольшая группа классов наследуется от него. Все они перечислены ниже:
Класс | Описание | Свойства |
---|---|---|
GeometryDrawing | Создает оболочку для геометрии с кистью, заполняющей ее, и пером, очерчивающим контур | Geometry, Brush, Pen |
ImageDrawing | Создает оболочку для графического изображения (обычно растрового изображения из файла) с прямоугольником, определяющим его границы | ImageSource, Rect |
VideoDrawing | Комбинирует MediaPlayer, используемый для воспроизведения видео, с прямоугольником, задающим его границы. | Player, Rect |
GlyphRunDrawing | Создает оболочку для низкоуровневого текстового объекта, известного как GlyphRun, с рисующей его кистью | GlyphRun, ForegroundBrush |
DrawingGroup | Комбинирует коллекцию объектов Drawing любого типа. DrawingGroup позволяет создавать составные рисунки и применять эффекты ко всей коллекции сразу, используя одно из его свойств | BitmapEffect, BitmapEffectInput, Children, ClipGeometry, GuidelineSet, Opacity, OpacityMask, Transform |
Отображение рисунка
Поскольку классы-наследники Drawing не являются элементами, они не могут быть помещены в пользовательский интерфейс. Вместо этого для отображения рисунка должен использоваться один из трех классов, описанных ниже:
- DrawingImage
Позволяет разместить рисунок внутри элемента Image
- DrawingBrush
Позволяет упаковать рисунок с кистью, которую можно использовать для заполнения любой поверхности
- DrawingVisual
Позволяет поместить рисунок в низкоуровневый визуальный объект. Визуальные объекты не требуют накладных расходов как настоящие элементы, но могут отображаться, если реализована необходимая инфраструктура.
У всех этих классов есть нечто общее — они предоставляют возможность отображать двухмерное содержимое с меньшими накладными расходами.
Например, предположим, что необходимо воспользоваться частью векторного рисунка для создания пиктограммы кнопки. Наиболее удобный (и затратный в смысле ресурсов) способ сделать это — поместить Canvas внутрь кнопки, а затем поместить в Canvas набор элементов-наследников Shape. Как уже известно, если принять такой подход, то каждый элемент будет полностью независимым, с собственным пространством в памяти, обработкой событий и т.п.
Более удачный подход заключается в сокращении количества элементов за счет применения элемента Path. Поскольку каждый путь имеет единственный штрих и заполнение, понадобится множество объектов Path, но все же, скорее всего, удастся несколько сократить количество элементов.
Начав работать с элементом Path, вы переключаетесь от отдельных фигур к отдельным геометриям. Уровень абстракции можно поднять, если извлечь информацию о геометрии, штрихе и заполнении из пути и превратить ее в рисунок. Затем рисунки можно скомбинировать вместе в объект DrawingGroup и поместить этот объект в DrawingImage, а его, в свою очередь — в элемент Image.
Хотя DrawingImage обеспечивает большую экономию, все же можно увеличить эффективности и удалить один элемент с помощью DrawingBrush. Одним из продуктов, использующих данный подход, является Expression Blend.
Базовая идея заключается в помещении DrawingImage в DrawingBrush, примерно так:
<Button ... >
<Button.Background>
<DrawingBrush>
<DrawingBrush.Drawing>
<DrawingGroup>
<GeometryDrawing ...>
<GeometryDrawing ...>
<GeometryDrawing ...>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</Button.Background>
</Button>
Подход с DrawingBrush — это не то же самое, что подход с DrawingImage, показанный ранее, поскольку стандартные способы изменения размеров содержимого в Image отличаются. По умолчанию свойство Image.Stretch установлено в Uniform, что масштабирует изображение вверх и вниз для заполнения доступного пространства. По умолчанию свойство DrawingBrush.Stretch равно Fill, что может исказить изображение.
Одна причуда подхода на основе DrawingBrush заключается в том, что содержимое исчезает, когда курсор мыши перемещается над кнопкой и для рисования ее поверхности применяется новая кисть. Но когда используется подход на основе Image, рисунок остается нетронутым. Чтобы справиться с этой проблемой, нужно создать шаблон элемента управления для кнопки, который не будет перерисовывать фон таким же образом.
В любом случае, используется объект DrawingImage сам по себе или в оболочке DrawingBrush, стоит подумать об улучшении разметки с применением ресурсов. Основная идея состоит в определении каждого объекта DrawingImage или DrawingBrush в виде отдельного ресурса, на который затем можно ссылаться по мере необходимости. Это особенно хорошая мысль, если необходимо отобразить некоторое содержимое в более чем одном элементе или в более чем одном окне, потому что тогда можно просто повторно использовать ресурс, а не копировать целый блок разметки.