Polyline и Polygon

25

Класс Polyline позволяет рисовать последовательность связанных отрезков прямых. В этом случае просто поставляется список координат X и Y с использованием свойства Points. Формально свойство Points требует объекта PointCollection, но эта коллекция заполняется в XAML-разметке с помощью лаконичного строкового синтаксиса. Нужно просто задать список точек, описанных координатами с разделителями (пробелом или запятой) между ними.

Элемент Polyline может быть описан всего двумя точками. Например, ниже приведен пример фигуры Polyline, дублирующей линию, которая простирается от (5,100) до (15,200):

<Polyline Stroke="Blue" Points="5,100 15,200"></Polyline>

А вот более сложный объект Polyline, начинающийся в точке (10,150). Точки монотонно распределяются слева направо, колеблясь между наибольшим значением и наименьшим:

<Polyline Stroke="LightBlue" StrokeThickness="4" 
         Points="10,150 30,140 50,170 70,120 90,190 110,100 130,210 150,80 170,230 190,60 210,250 230,150 320,150"></Polyline>

Полученная в результате линия показана на рисунке:

Polyline

Здесь может показаться, что было бы проще наполнить коллекцию Points программно с использованием некоторого цикла, автоматически увеличивающего значения X и Y. Это верно, если требуется создавать динамическую графику — например, диаграмму, изменяющую свою форму в зависимости от информации, извлеченной из базы данных. Но если просто нужно построить фиксированный фрагмент графического содержимого, вообще незачем беспокоиться об определенных координатах фигур.

Вместо этого для рисования соответствующей графики применяется инструмент, подобный Expression Design, который позволяет экспортировать результирующую графику в XAML-разметку.

Класс Polygon — почти то же самое, что и Polyline. Как и Polyline, класс Polygon определяет коллекцию Points, принимающую список координат. Единственное отличие в том, что Polygon добавляет финальный сегмент, соединяющий начальную и конечную точки. (Если финальная точка уже совпадает с начальной, то Polygon ничем от Polyline не отличается.) Внутреннюю часть полученной фигуры можно заполнить с использованием кисти Fill:

<Polygon Stroke="Blue" StrokeThickness="4" 
      Points="10,150 30,140 50,168 70,120 90,185 110,100 130,200 150,80 170,215 190,60 210,250"
      Fill="LightGreen"></Polygon>
Polygon

Формально можно также устанавливать свойство Fill для объекта Polyline. В этой ситуации Polyline закрашивает себя так, как если бы это был Polygon. Другими словами, как если бы он имел невидимый сегмент, соединяющий конечную точку с начальной. Этот эффект находит относительно ограниченное применение.

В простой фигуре, где линии никогда не пересекаются, заполнить внутреннюю область легко. Однако иногда приходится иметь дело с более сложным Polygon, где не совсем очевидно, какие части находятся "внутри" фигуры (и должны быть закрашены), а какие — снаружи.

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

<Viewbox HorizontalAlignment="Left">
    <Canvas Width="200" Height="400" >
      <Polygon Stroke="Blue" StrokeThickness="1" Fill="Yellow" Canvas.Left="10"
        Points="15,200 68,70 110,200 0,125 135,125" >
      </Polygon>
      <Polygon Stroke="Blue" StrokeThickness="1" Fill="Yellow" Canvas.Left="10" Canvas.Top="175"
               FillRule="Nonzero"
         Points="15,200 68,70 110,200 0,125 135,125" >
      </Polygon>
    </Canvas>
  </Viewbox>
Установка разных свойств FillRule

Каждый элемент Polygon и Polyline имеет свойство FillRule, которое позволяет выбирать между двумя разными подходами в заполнении областей. По умолчанию FillRule установлено в EvenOdd. Для того чтобы решить, нужно ли заполнять область, WPF подсчитывает количество линий, которые требуется пересечь во время достижения внешней стороны фигуры. Если это число нечетно, область заполняется, если же четно — нет. Чтобы попасть в центральную область фигуры на рисунке, понадобится пересечь две линии, поэтому область не закрашивается.

В WPF также поддерживается правило заполнения Nonzero, которое немного сложнее. Следуя правилу Nonzero, WPF выполняет такой же подсчет линий, как и в EventOdd, но при этом принимает во внимание направление каждой из линий. Если количество линий, направленных в одну сторону (скажем, слева направо) равно количеству линий, направленных в противоположную сторону (справа налево), область не заполняется. Если разница между этими двумя количествами не равна нулю, область заполняется. Если установить свойство FillRule в Nonzero в фигуре из предыдущего примера, внутренняя область заполнится.

Если количество линий нечетное, то разница между счетчиками не может быть равна нулю. Поэтому правило Nonzero всегда приводит к заполнению, как минимум, такого же числа областей, что и правило EvenOdd, плюс, возможно, еще несколько.

Особенность правила Nonzero состоит в том, что его настройки заполнения зависят от того, каким образом рисуется фигура, а не от того, как она выглядит. Например, ту же самую фигуру можно нарисовать так, что ее центр не будет заполнен (хотя это и труднее — можно начать рисовать внутреннюю область, а затем нарисовать внешние лучи в обратном направлении).

Пройди тесты
Лучший чат для C# программистов