Главная » Разработка для Windows Phone 7 » Элемент Path Windows Phone 7

0

Хотя классы Line, Polyline и Polygon удобны и просты в использовании, последний потомок Shape, класс Path (Контур), объединяет в себе практически всю их функциональность.

Класс Path самостоятельно определяет всего одно свойство: Data типа Geometry (Геометрический элемент). Геометрические элементы являются очень важной концепцией в векторной графике на Silverlight. В общем случае геометрический элемент – это коллекция прямых линий и кривых, некоторые из которых могут соединяться друг с другом (или нет) и определять замкнутые области (или нет). В некоторых графических средах разработки геометрические элементы называют контурами. В Silverlight Path – это элемент, использующий объект Geometry как значение свойства Data.

Важно понимать, что объект Geometry – это лишь голые координатные точки. Для геометрического элемента нет понятия кистей, или толщины линии, или стилей. Поэтому чтобы действительно получить какое-то визуальное представление на экране, Geometry необходимо комбинировать с элементом Path. Geometry определяет координаты, Path определяет кисти обводки и заливки.

В иерархии классов Silverlight Geometry следующую позицию: Object

DependencyObject (абстрактный) Geometry (абстрактный)

LineGeometry (запечатанный) RectangleGeometry (запечатанный) EllipseGeometry (запечатанный) GeometryGroup (запечатанный) PathGeometry (запечатанный)

Если среди производных от Shape мы будем пользоваться преимущественно одним только Path, то среди потомков Geometry нас интересует главным образом класс PathGeometry. Но, конечно, мы обсудим все классы, потому что часто удобно использовать именно их. Наследоваться от Geometry напрямую нельзя.

Geometry определяет четыре открытых свойства:

•                                   статическое свойство только для чтения Empty (Пустой) типа Geometry

•         статическое свойство только для чтения StandardFlatteningTolerance (Стандартный допуск, используемый для кусочно-линейной аппроксимации) типа double

•                                   свойство только для чтения Bounds (Границы) типа Rect

•                                   свойство Transform типа Transform

Самыми полезными являются два последних. Свойство Bounds задает минимальный прямоугольник, который может вместить геометрический элемент, и Transform позволяет применять преобразования к данному геометрическому элементу (как будет продемонстрировано ниже).

LineGeometry (Геометрический элемент линия) определяет два свойства типа Point: StartPoint и EndPoint:

<Grid Background="LightCyan"> <Path Stroke="Maroon"

StrokeThickness="4" StrokeDashArray="3 1"> <Path.Data>

<LineGeometry StartPoint="100 50"

EndPoint="300 150" />

</Path.Data> </Path> </Grid>

Обратите внимание на разделение обязанностей между Geometry и Path: Geometry обеспечивает координаты; и Path обеспечивает все данные для формирования визуального представления.

Когда мы знаем о существовании элементов Line и Polyline, LineGeometry может казаться лишним, но в отличие от Line и Polyline у LineGeometry есть два свойства-зависимости типа Point. Эти свойства могут быть очень полезными в качестве целей анимаций в некоторых сценариях.

RectangleGeometry (Геометрический элемент прямоугольник) описывает свойство Rect типа Rect. Это структура, определяющая прямоугольник с помощью четырех чисел: два числа задают координаты верхнего левого угла, и два других числа – размеры прямоугольника. В XAML эти четыре числа задаются в такой последовательности: координаты x и y верхнего левого угла и за ними ширина и высота прямоугольника:

<Grid Background="LightCyan"> <Path Stroke="Maroon"

StrokeThickness="8" Fill="Green"> <Path.Data>

<RectangleGeometry

Rect="100 50 300 200" /> </Path.Data> </Path> </Grid>

В данном примере координаты нижнего правого угла прямоугольника – (400, 250). В коде структура Rect имеет три конструктора, которые позволяют нам задать прямоугольник либо через Point и Size, либо посредством двух объектов Point, либо используя строку из четырех чисел, как в XAML: (x,y, ширина, высота).

Свойство Bounds класса Geometry также типа Rect. Для приведенного выше RectangleGeometry свойство Bounds будет возвращать те же значения: (100, 50, 300, 200). Для LineGeometry из предыдущего примера Bounds возвратит (100, 50, 200, 100).

RectangleGeometry также определяет свойства RadiusX и RadiusY для скругления углов:

<Grid Background="LightCyan"> <Path Stroke="Maroon"

StrokeThickness="8" Fill="Green"> <Path.Data>

<RectangleGeometry

Rect="100 50 300 200" RadiusX="10 0" RadiusY="50" /> </Path.Data> </Path> </Grid>

Класс EllipseGeometry (Геометрический элемент эллипс) также имеет свойства RadiusX и RadiusY, но они используются для задания длин осей эллипса. Центр эллипса в EllipseGeometry задается посредством свойства Center типа Point:

<Grid Background="LightCyan"> <Path Stroke="Maroon"

StrokeThickness="8" Fill="Green"> <Path.Data>

<EllipseGeometry

Center="250 150" RadiusX="150" RadiusY="10 0" /> </Path.Data> </Path> </Grid>

Для определения местоположения окружности или эллипса часто удобнее задавать их центр, а не верхний левый угол (как с элементом Ellipse), особенно если учесть, что эти фигуры не имеют углов!

Давайте немного поупражняемся в интерактивном рисовании с помощью проекта TouchAndDrawCircles (Прикоснись и нарисуй круги). Когда пользователь касается экрана, это приложение создает новый круг, используя классы Path и EllipseGeometry. По мере того как пользователь ведет пальцем по экрану, круг увеличивается. Как только пользователь снимает палец с экрана, круг закрашивается случайным цветом. Уже существующий круг можно перемещать по экрану посредством касания.

В файле MainPage.xaml сетка для содержимого изначально пуста. Единственное, я задал ей отличный от нуля Background, чтобы она могла формировать события манипуляций:

Проект Silverlight: TouchAndDrawCircles Файл: MainPage.xaml (фрагмент)

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0" Background="Transparent" />

В файле выделенного кода всего несколько полей для отслеживания того, что происходит:

Проект Silverlight: TouchAndDrawCircles Файл: MainPage.xaml.cs (фрагмент)

public partial class MainPage : PhoneApplicationPage {

Random rand = new Random(); bool isDrawing, isDragging; Path path;

EllipseGeometry ellipseGeo;

Два поля типа Boolean указывают на текущее действие. Поле Path имеет действительное значение только во время отрисовки нового круга. Поле EllipseGeometry действительно при отрисовке нового круга или перемещении существующего.

Перегрузка метода OnManipulationStarted инициирует операцию рисования или перетягивания, но не допускает одновременного выполнения обеих операций. Значением свойства OriginalSource аргументов события может быть либо элемент Path – это означает, что пользователь коснулся одного из существующих кругов и хочет переместить его – либо ContentPanel, что инициирует новую операцию рисования:

Проект Silverlight: TouchAndDrawCircles Файл: MainPage.xaml.cs (фрагмент)

protected override void OnManipulationStarted(ManipulationStartedEventArgs args) {

if (isDrawing || isDragging) return;

if (args.OriginalSource is Path) {

ellipseGeo = (args.OriginalSource as Path).Data as EllipseGeometry; isDragging = true;

args.ManipulationContainer = ContentPanel; args.Handled = true;

}

else if (args.OriginalSource == ContentPanel) {

ellipseGeo = new EllipseGeometry(); ellipseGeo.Center = args.ManipulationOrigin; path = new Path();

path.Stroke = this.Resources["PhoneForegroundBrush"] as Brush;

path.Data = ellipseGeo;

ContentPanel.Children.Add(path);

isDrawing = true; args.Handled = true;

}

base.OnManipulationStarted(args);

}

В XAML-файле я задал свойству Background объекта ContentPanel значение Transparent, чтобы обеспечить возможность формирования событий Manipulation. Если значением свойства OriginalSource является Grid, то этот Grid будет и ManipulationContainer, и ManipulationOrigin. Это точка, которая необходима для задания значения свойства Center этого нового EllipseGeometry.

Для реализации операции перетягивания перегруженный метод OnManipulationDelta использует свойство DeltaManipulation аргументов события, с помощью которого изменяет свойство Center объекта EllipseGeometry

Проект Silverlight: TouchAndDrawCircles Файл: MainPage.xaml.cs (фрагмент)

protected override void OnManipulationDelta(ManipulationDeltaEventArgs args) {

if (isDragging) {

Point center = ellipseGeo.Center;

center.X += args.DeltaManipulation.Translation.X; center.Y += args.DeltaManipulation.Translation.Y; ellipseGeo.Center = center;

args.Handled = true;

}

else if (isDrawing) {

Point translation = args.CumulativeManipulation.Translation; double radius = Math.Max(Math.Abs(translation.X),

Math.Abs(translation.Y)); ellipseGeo.RadiusX = radius; ellipseGeo.RadiusY = radius;

args.Handled = true;

}

base.OnManipulationDelta(args);

}

Для сравнения, при реализации операции рисования этот метод изменяет значения свойств RadiusX и RadiusY объекта EllipseGeometry. Для этого он использует свойство CumulativeManipulation, которое хранит всю манипуляцию, начиная с момента формирования события ManipulationStarted. Причина применения другого свойство проста: если пользователь инициирует операцию рисования и затем перемещает палец влево или вверх, коэффициенты переноса будут отрицательными. Но эти отрицательные числа должны быть преобразованы в положительное значение радиуса окружности. Оказывается проще взять абсолютное значение суммы всех коэффициентов переноса, чем менять существующие размеры.

Когда пользователь снимает палец с экрана, формируется событие OnManipulationCompleted (По завершении манипуляции), обеспечивающее очистку:

Проект Silverlight: TouchAndDrawCircles Файл: MainPage.xaml.cs (фрагмент)

protected override void OnManipulationCompleted(ManipulationCompletedEventArgs args) {

if (isDragging) {

isDragging = false; args.Handled = true;

}

else if (isDrawing) {

Color clr = Color.FromArgb(255, (byte)rand.Next(25 6),

(byte)rand.Next(256), (byte)rand.Next(25 6)); path.Fill = new SolidColorBrush(clr);

isDrawing = false; args.Handled = true;

}

base.OnManipulationCompleted(args);

}

Очистка для операции переноса проста. Но операция рисования должна быть завершена заданием элементу Path случайной кисти Fill.

При работе с приложением можно заметить небольшую задержку между моментом, когда пользователь касается экрана, чтобы начать отрисовку или перетягивание круга, и фактическим началом выполнения этих операций. Это особенность событий Manipulation.

Источник: Чарльз Петзольд, Программируем Windows Phone 7, Microsoft Press, © 2011.

По теме:

  • Комментарии