Чтение онлайн

ЖАНРЫ

Язык программирования C#9 и платформа .NET5
Шрифт:

};

break;

default:

return;

}

// Установить левый верхний угол для рисования на холсте.

Canvas.SetLeft(shapeToRender, e.GetPosition(canvasDrawingArea).X);

Canvas.SetTop(shapeToRender, e.GetPosition(canvasDrawingArea).Y);

// Нарисовать фигуру.

canvasDrawingArea.Children.Add(shapeToRender);

}

На заметку! Возможно, вы заметили, что объекты

Ellipse
,
Rectangle
и
Line
, создаваемые в методе
canvasDrawingArea_MouseLeftButtonDown
,
имеют те же настройки свойств, что и соответствующие определения XAML. Вполне ожидаемо, код можно упростить, но это требует понимания объектных ресурсов WPF, которые будут рассматриваться в главе 27.

В коде проверяется переменная-член

_currentShape
с целью создания корректного объекта, производного от
Shape
. Затем устанавливаются координаты левого верхнего угла внутри
Canvas
с использованием входного объекта
MouseButtonEventArgs
. Наконец, в коллекцию объектов
UIElement
, поддерживаемую
Canvas
, добавляется новый производный от
Shape
объект. Если запустить программу прямо сейчас, то она должна позволить щелкать левой кнопкой мыши где угодно на холсте и визуализировать в позиции щелчка выбранную фигуру.

Удаление прямоугольников, эллипсов и линий с поверхности Canvas

Имея в распоряжении элемент

Canvas
с коллекцией объектов, может возникнуть вопрос: как динамически удалить элемент, скажем, в ответ на щелчок пользователя правой кнопкой мыши на фигуре? Это делается с помощью класса
VisualTreeHelper
из пространства имен
System.Windows.Media
. Роль "визуальных деревьев" и "логических деревьев" более подробно объясняется в главе 27, а пока организуйте обработку события
MouseRightButtonDown
объекта
Canvas
и реализуйте соответствующий обработчик:

private void CanvasDrawingArea_MouseRightButtonDown(object sender,

MouseButtonEventArgs e)

{

// Сначала получить координаты x,y позиции,

// где пользователь выполнил щелчок.

Point pt = e.GetPosition((Canvas)sender);

// Использовать метод HitTestO класса VisualTreeHelper, чтобы

// выяснить, щелкнул ли пользователь на элементе внутри Canvas.

HitTestResult result = VisualTreeHelper.HitTest(canvasDrawingArea, pt);

// Если переменная result не равна null, то щелчок произведен на фигуре.

if (result != null)

{

// Получить фигуру, на которой совершен щелчок, и удалить ее из Canvas.

canvasDrawingArea.Children.Remove(result.VisualHit as Shape);

}

}

Метод начинается с получения точных координат (

х
,
у
) позиции, где пользователь щелкнул внутри
Canvas
, и проверки попадания посредством статического метода
VisualTreeHelper.HitTest
. Возвращаемое значение — объект
HitTestResult
— будет установлено в
null
, если пользователь выполнил щелчок не на
UIElement
внутри
Canvas
. Если значение
HitTestResult
не равно
null
, тогда с помощью свойства
VisualHit
можно получить объект
UIElement
, на котором был совершен щелчок, и привести его к типу, производному от
Shape
(вспомните, что
Canvas
может содержать любой
UIElement
, а не только фигуры). Детали, связанные с "визуальным деревом", будут изложены в главе 27.

На заметку! По умолчанию метод

VisualTreeHelper.HitTest
возвращает объект
UIElement
самого верхнего уровня, на котором совершен щелчок, и не предоставляет информацию о других объектах, расположенных под ним (т.е. перекрытых в Z-порядке).

В результате внесенных модификаций должна появиться возможность добавления фигуры на

Canvas
щелчком
левой кнопкой мыши и ее удаления щелчком правой кнопкой мыши.

До настоящего момента вы применяли объекты типов, производных от

Shape
, для визуализации содержимого элементов
RadioButton
с использованием разметки XAML и заполняли
Canvas
в коде С#. Во время исследования роли кистей и графических трансформаций в данный пример будет добавлена дополнительная функциональность. К слову, в другом примере главы будут иллюстрироваться приемы перетаскивания на объектах
UIElement
. А пока давайте рассмотрим оставшиеся члены пространства имен
System.Windows.Shapes
.

Работа с элементами Polyline и Polygon

В текущем примере используются только три класса, производных от

Shape
. Остальные дочерние классы (
Polyline
,
Polygon
и
Path
) чрезвычайно трудно корректно визуализировать без инструментальной поддержки (такой как инструмент Blend для Visual Studio или другие инструменты, которые могут создавать векторную графику) — просто потому, что они требуют определения большого количества точек для своего выходного представления. Ниже представлен краткий обзор остальных типов
Shapes
.

Тип

Polyline
позволяет определить коллекцию координат (
х
,
у
) (через свойство
Points
) для рисования последовательности линейных сегментов, не требующих замыкания. Тип
Polygon
похож, но запрограммирован так, что всегда замыкает контур, соединяя начальную точку с конечной, и заполняет внутреннюю область с помощью указанной кисти. Предположим, что в редакторе Kaxaml создан следующий элемент
StackPanel
:

<!-- Элемент Polyline не замыкает автоматически конечные точки -->

<Polyline Stroke ="Red" StrokeThickness ="20" StrokeLineJoin ="Round"

Points ="10,10 40,40

10,90 300,50"/>

<!-- Элемент Polygon всегда замыкает конечные точки -->

<Polygon Fill ="AliceBlue" StrokeThickness ="5" Stroke ="Green"

Points ="40,10 70,80 10,50" />

На рис. 26.2 показан визуализированный вывод в Kaxaml.

Работа с элементом Path

Применяя только типы

Rectangle
,
Ellipse
,
Polygon
,
Polyline
и
Line
, нарисовать детализированное двумерное векторное изображение было бы исключительно трудно, т.к. упомянутые примитивы не позволяют легко фиксировать графические данные, подобные кривым, объединениям перекрывающихся данных и т.д. Последний производный от
Shape
класс,
Path
, предоставляет возможность определения сложных двумерных графических данных в виде коллекции независимых геометрических объектов. После того, как коллекция таких геометрических объектов определена, ее можно присвоить свойству
Data
класса
Path
, где она будет использоваться для визуализации сложного двумерного изображения.

Свойство

Data
получает объект класса, производного от
System.Windows.Media.Geometry
, который содержит ключевые члены, кратко описанные в табл. 26.2.

Классы, которые расширяют класс

Geometry
(табл. 26.3), выглядят очень похожими на свои аналоги, производные от
Shape
. Например, класс
EllipseGeometry
имеет члены, подобные членам класса
Ellipse
. Крупное отличие связано с тем, что производные от
Geometry
классы не знают, каким образом визуализировать себя напрямую, поскольку они не являются
UIElement
. Взамен классы, производные от
Geometry
, представляют всего лишь коллекцию данных о точках, которая указывает объекту
Path
, как их визуализировать.

Поделиться с друзьями: