• Nie Znaleziono Wyników

Budowa aplikacji w technologii .NET wykład 10 – Kształty, pędzle, transformacje, ścieżki

N/A
N/A
Protected

Academic year: 2021

Share "Budowa aplikacji w technologii .NET wykład 10 – Kształty, pędzle, transformacje, ścieżki"

Copied!
58
0
0

Pełen tekst

(1)

Budowa aplikacji w technologii .NET

wykład 10 – Kształty, pędzle, transformacje, ścieżki

Kształty

Najprostszy sposób rysowania własnej zawartości w WPF, to wykorzystanie kształtów.

Shapes – klasy reprezentujące linie, elipsy, prostokąty, wielokąty (prymitywy, kształty podstawowe).

Wszystkie one dziedziczą z FrameworkElement, a zatem:

• Odpowiadają za narysowanie samych siebie (również automatycznie reagują na zmianę właściwości).

• Są zorganizowane w ten sam sposób co inne elementy (umieszczane w kontenerze układu, przeważnie Canvas).

• Obsługują te same zdarzenia, co inne elementy (a także wspierają tooltipy, menu kontekstowe, operacje drag-and-drop).

(2)

Klasy kształtów

Każdy kształt dziedziczy z abstrakcyjnej klasy System.Windows.Shapes.Shape:

Rectangle – prostokąt

Ellipse – elipsa

Line – odcinek

Polyline – połączony ciąg odcinków

Polygon – zamknięty kształt z ciągu połączonych odcinków

Path – pozwala na łączenie w jednym elemencie wszystkich innych kształtów

2/58

(3)

Własności klasy Shape:

Fill – pędzel do namalowania zawartości (wnętrza) kształtu

Stroke – pędzel do namalowania krawędzi kształtu

StrokeThickness – grubość krawędzi. Jest rozdzielana po równo na obie strony.

StrokeStartLineCap, StrokeEndLineCap – określają kształt końca i początku odcinków

StrokeDashArray, StrokeDashOffset, StrokeDashCap – pozwalają na zdefiniowanie linii przerywanych.

StrokeLineJoin, StrokeMiterLimit – określają kształt narożników (wierzchołków)

Stretch – określa, jak kształt ma dopasować się do dostępnego miejsca (można też używać HorizontalAlignment i VerticalAlignment).

DefiningGeometry – dostarcza definicję geometrii kształtu (np. współrzędnych punktów).

GeometryTransform – pozwala zastosować transformację (np. przesunięcie, pochylenie, obrót, skalowanie).

RenderedGeometry – dostarcza geometrię ostatecznego ,renderowanego kształtu.

(4)

Rectangle and Ellipse

Wystarczy zdefiniować rozmiar kształtu (a także Fill lub/i Stroke, aby kształt stał się widoczny):

<StackPanel>

<Ellipse Fill="Yellow" Stroke="Blue" Height="50"

Width="100" Margin="5" HorizontalAlignment="Left"/>

<Rectangle Fill="Yellow" Stroke="Blue" Height="50"

Width="100" Margin="5" HorizontalAlignment="Left"/>

</StackPanel>

4/58

(5)

Rectangle dodaje dwie własności: RadiusX i RadiusY (pozwalają na rysowanie zaokrąglonych narożników).

(6)

Rozmieszczanie kształtów

Przeważnie rozmiar i położenie kształtów określa się ręcznie.

Możemy też pozwolić, by kształt dopasował się do dostępnego miejsca:

<Grid>

<Ellipse Fill="Yellow" Stroke="Blue"></Ellipse>

</Grid>

6/58

(7)

Określając wartość własności Stretch:

Fill – dopasowanie wysokości i szerokości do dostępnego miejsca.

None – bez dopasowania.

Uniform – dopasowanie proporcjonalne (tak, by kształt mieścił się w kontenerze).

Jeśli określimy wysokość i szerokość, będą traktowane jako górne ograniczenie.

UniformToFill – podobnie, ale każdy z wymiarów ma wypełnić dostępną przestrzeń (część kształtu może zostać ucięta).

(8)

Położenie kształtów jest określane na tych samych zasadach, co innych elementów: rządzi nim kontener. Największą kontrolę nad położeniem daje Canvas:

<Canvas>

<Ellipse Fill="Yellow" Stroke="Blue" Canvas.Left="100"

Canvas.Top="50" Width="100" Height="50"/>

<Rectangle Fill="Yellow" Stroke="Blue" Canvas.Left="30"

Canvas.Top="40" Width="100" Height="50"/>

</Canvas>

(uwaga: kolejność definicji ma znaczenie, gdy kształty nakładają się na siebie)

(uwaga: Cansa nie musi być elementem najwyższego poziomu – możemy umieścić go np.

w komórce Grida).

8/58

(9)

Viewbox

Jest sposobem na połączenie precyzyjnego rozmieszczania kształtów i skalowania do dostępnego rozmiaru.

Viewbox to Decorator, przyjmujący pojedyncze dziecko (może nim być kontener z dalszymi elementami), które skaluje, dopasowując do dostępnej przestrzeni. Najbardziej opłacalne w wypadku grupy kształtów:

<Grid Margin="5">

<Viewbox Stretch="UniformToFill">

<Canvas Width="50" Height="50">

<Ellipse Fill="Yellow" Stroke="Blue" Canvas.Left="5"

Canvas.Top="5" Width="40" Height="40"/>

</Canvas>

</Viewbox>

</Grid>

(10)

(Viebox skaluje całą zawartość: nie tylko rozmiar samego kształtu, ale i np. grubość krawędzi, a także kontrolki, które w nim umieścimy).

Własność Viewbox.Stretch domyślnie ustawiona jest na Uniform, ale można też użyć Fill i UniformToFill, aby zmienić sposób skalowania zawartości, a także StretchDirection (poza Both mamy UpOnly, aby tylko powiększać i DownOnly aby tylko zmniejszać).

Uwaga: element umieszczony w ViewBoxie musi mieć określony rozmiar, aby Viewbox wiedział jak go przeskalować (tzn. jaki jest jego rozmiar bazowy).

10/58

(11)

Line

Własności X1, Y1 oraz X2, Y2 określają współrzędne początkowego i końcowego punktu linii.

<Grid Margin="5">

<Line Stroke="Blue" X1="0" Y1="0" X2="100" Y2="10"/>

</Grid>

Dla linii określamy tylko właność Stroke (Fill nie jest brany pod uwagę).

Współrzędne są określane w odniesieniu do lewego, górnego narożnika kontenera (plus Margin), w którym umieszczono kształt.

W przeciwieństwie do Rectangle i Ellipse, możemy rysować linie poza kontenerem (np.

podając ujemne współrzędne). Nie możemy za to ustawić dla nich marginesu i alignmentu.

Nadal możemy określić położenie punktu początkowego linii w Canvasie przy pomocy Canvas.Top, Canvas.Left a rozmiar przy pomocy Width i Height.

(12)

Polyline

Pozwala narysować linię łamaną. Definiujemy ją przy pomocy listy współrzędnych X i Y.

Można podać ją jako kolekcję punktów:

<Polyline Stroke="MediumBlue" StrokeThickness="5">

<Polyline.Points>

<PointCollection>

<Point X="10" Y="110"/>

<Point X="30" Y="120"/>

<Point X="50" Y="100"/>

<Point X="70" Y="140"/>

<Point X="90" Y="80"/>

<Point X="110" Y="170"/>

<Point X="130" Y="50"/>

<Point X="150" Y="170"/>

<Point X="170" Y="80"/>

<Point X="190" Y="140"/>

</PointCollection>

</Polyline.Points>

</Polyline>

12/58

(13)

Albo w skróconej formie, jako wartości oddzielane spacjami (przecinek między X a Y jest opcjonalny).

<Polyline Stroke="MediumBlue" StrokeThickness="5"

Points="10,90 30,100 50,80 70,120 90,60 110,150 130,30 150,150 170,60 190,120"/>

(14)

Polygon

Działa niemal tak samo jak Polyline. Jedyna różnica: Polygon jest krzywą zamkniętą (sam dodaje ostatni segment, łączący punkt końcowy z początkowym). Może używać pędzla wypełnienia (Fill).

<Polygon Stroke="MediumBlue" Fill="LightBlue"

StrokeThickness="5" Points="20,85 30,100 50,80 70,120 90,60 110,150 130,30 150,150 170,60 180,130"/>

14/58

(15)

Są dwa sposoby wypełniania Polygonu.

Domyślny:

<Polygon Stroke="MediumBlue" Fill="LightBlue"

FillRule="EvenOdd" StrokeThickness="5" Points="95,20 160,150 20,70 180,60 50,160"/>

(16)

<Polygon Stroke="MediumBlue" Fill="LightBlue"

FillRule="Nonzero" StrokeThickness="5" Points="95,20 160,150 20,70 180,60 50,160"/>

16/58

(17)

Line Caps, Line Joins

W wypadku rysowania linii możemy określić kształt zakończeń – StrokeStartLineCap i StrokeEndLineCap: Flat (domyślne), Round, Square i Triangle (wszystkie zwiększają długość linii o ½ szerokości).

<Canvas>

<Polyline StrokeEndLineCap="Flat"

Stroke="MediumBlue"

StrokeThickness="20"

Points="20,40 40,60 80,20 100,40 200,40"/>

<Polyline StrokeEndLineCap="Round" .../>

<Polyline StrokeEndLineCap="Square" .../>

<Polyline StrokeEndLineCap="Triangle" .../>

</Canvas>

(18)

StrokeLineJoin pozwala określić kształt łączeń (wierzchołków łamanej) – Miter (domyślny), Bevel (ścięte narożniki), Round (zaokrąglone). Miter pozwala podać

StrokeMiterLimit, który ogranicza wydłużanie narożników (wartość 1 to maksymalnie ½ szerokości linii).

<Canvas>

<Polyline Stroke="MediumBlue"

StrokeThickness="20"

StrokeLineJoin="Miter"

Points="20,20 140,40 40,60 60,80"/>

<Polyline StrokeLineJoin="Bevel" .../>

<Polyline StrokeLineJoin="Round" .../>

<Polyline StrokeLineJoin="Miter"

StrokeMiterLimit="3".../>

</Canvas>

18/58

(19)

Dashes

<Canvas>

<Polyline StrokeDashArray="1" .../>

<Polyline StrokeDashArray="1 2" .../>

<Polyline StrokeDashArray="2 1" .../>

<Polyline StrokeDashArray="3 2 1 2" .../>

<Polyline StrokeDashArray="5 2 1" .../>

<Polyline StrokeDashArray="2 2" StrokeDashCap="Round" .../>

</Canvas>

StrokeDashArray pozwala zdefiniować dowolny wzór linii przerywanej – podajemy wartości, określające długość segmentu i przerwy między segmentami. Liczba tych wartości nie musi być parzysta. StrokeDashCap pozwala określić kształt zakończeń segmentów (uwaga na długość

zwiększoną o ½ szerokości).

StrokeDashOffset pozwala zacząć przerywaną od wybranej wartości wzorca.

(20)

Pixel Snapping

Uwaga: wymiary podajemy zawsze w jednostkach logicznych (1/96 cala). Mogą być to liczby całkowite. W zależności od urządzenia nie muszą się one tłumaczyć na faktyczne położenie pixeli. Domyślnie jest to niwelowane przez antyaliasing. Gdy nie jest to pożądane, można włączyć opcję SnapsToDevicePixels (można to ustawić osobno dla każdego kształtu) – powoduje ona zaokrąglenie wartości do faktycznych pikseli urządzenia.

Brushes

Pędzle używane są zarówno do rysowania tła (background), pierwszego planu (foreground), krawędzi (border) elementów, jak też wypełnienia (fill) oraz obwiedni (stroke) kształtów.

• Pędzle obsługują powiadamianie o zmianie (gdy zmodyfikujemy pędzel, wszystkie elementy, które go używają, powinny się odrysować).

• Obsługują półprzeźroczystość (własność Opacity).

• Klasa SystemBrushes udostępnia pędzle używające kolorów systemowych (zdefiniowanych w ustawieniach użytkownika).

20/58

(21)

Dostępne rodzaje pędzli:

SolidColorBrush – najprostszym rodzaj pędzla: wypełnia zawartość jednolitym kolorem. Jest on stosowany domyślnie, gdy podajemy sam kolor jako wartość własności w XAMLu.

LinearGradientBrush, RadialGradientBrush – wypełnienie gradientowe.

ImageBrush – wypełnienie przy pomocy obrazka.

DrawingBrush – wypełnienie przy użyciu własnej grafiki (np. kształtów).

VisualBrush – wypełnianie przy użyciu dowolnego obiektu typu Visual (a więc np.

również elementów interfejsu – przydatne do różnych efektów specjalnych).

LinearGradientBrush

Wymaga podania listy elementów GradientStop (każdy element to kolejny kolor).

<Rectangle>

<Rectangle.Fill>

<LinearGradientBrush>

<GradientStop Color="LightBlue" Offset="0" />

<GradientStop Color="MediumBlue" Offset="1" />

</LinearGradientBrush>

(22)

Offset określa położenie każdego koloru, można w ten sposób sterować szerokością przejścia:

<Rectangle Margin="3">

<Rectangle.Fill>

<LinearGradientBrush>

<GradientStop Color="LightBlue" Offset="0.3" />

<GradientStop Color="MediumBlue" Offset="0.7" />

</LinearGradientBrush>

</Rectangle.Fill>

</Rectangle>

StartPoint i EndPoint pozwalają sterować kierunkiem gradientu.

<Rectangle Margin="3">

<Rectangle.Fill>

<LinearGradientBrush StartPoint="0,0" EndPoint="1,0">

<GradientStop Color="LightBlue" Offset="0" />

<GradientStop Color="MediumBlue" Offset="1" />

</LinearGradientBrush>

</Rectangle.Fill>

</Rectangle>

22/58

(23)

<Rectangle Margin="3">

<Rectangle.Fill>

<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">

<GradientStop Color="LightBlue" Offset="0" />

<GradientStop Color="MediumBlue" Offset="1" />

</LinearGradientBrush>

</Rectangle.Fill>

</Rectangle>

<Rectangle Margin="3">

<Rectangle.Fill>

<LinearGradientBrush StartPoint="0,0"

EndPoint="0,0.2" SpreadMethod="Reflect">

<GradientStop Color="LightBlue" Offset="0" />

<GradientStop Color="MediumBlue" Offset="1" />

</LinearGradientBrush>

</Rectangle.Fill>

</Rectangle>

(24)

<Rectangle Margin="3">

<Rectangle.Fill>

<LinearGradientBrush StartPoint="0,0"

EndPoint="0,0.2" SpreadMethod="Repeat">

<GradientStop Color="LightBlue" Offset="0" />

<GradientStop Color="MediumBlue" Offset="1" />

</LinearGradientBrush>

</Rectangle.Fill>

</Rectangle>

Można określić więcej kolorów składowych:

<Rectangle Margin="3">

<Rectangle.Fill>

<LinearGradientBrush StartPoint="0,1" EndPoint="0,0">

<GradientStop Color="Red" Offset="0" />

<GradientStop Color="White" Offset="0.5" />

<GradientStop Color="Black" Offset="1" />

</LinearGradientBrush>

</Rectangle.Fill>

</Rectangle>

(Uwaga: oczywiście, takiego pędzla możemy używać nie tylko do rysowania kształtów, ale i np. tekstu, krawędzi, etc.)

24/58

(25)

RadialGradientBrush

Kolory definiuje się, jak w LinearGradientBrush. Różnica polega na określeniu pozycji.

<Ellipse Margin="3">

<Ellipse.Fill>

<RadialGradientBrush>

<GradientStop Color="Yellow" Offset="0" />

<GradientStop Color="Red" Offset="1" />

</RadialGradientBrush>

</Ellipse.Fill>

</Ellipse>

<Ellipse Margin="3">

<Ellipse.Fill>

<RadialGradientBrush Center="0.2,0.8">

<GradientStop Color="Yellow" Offset="0" />

<GradientStop Color="Red" Offset="1" />

</RadialGradientBrush>

</Ellipse.Fill>

</Ellipse>

(26)

<Ellipse Margin="3">

<Ellipse.Fill>

<RadialGradientBrush GradientOrigin="0.2,0.8">

<GradientStop Color="Yellow" Offset="0" />

<GradientStop Color="Red" Offset="1" />

</RadialGradientBrush>

</Ellipse.Fill>

</Ellipse>

<Ellipse Margin="3">

<Ellipse.Fill>

<RadialGradientBrush RadiusX="0.25" RadiusY="1">

<GradientStop Color="Yellow" Offset="0" />

<GradientStop Color="Red" Offset="1" />

</RadialGradientBrush>

</Ellipse.Fill>

</Ellipse>

26/58

(27)

ImageBrush

<Rectangle Margin="3">

<Rectangle.Fill>

<ImageBrush ImageSource="happy_face.png">

</ImageBrush>

</Rectangle.Fill>

</Rectangle>

Viewbox pozwala wskazać fragment obrazka do użycia w roli pędzla (koordynaty względne).

<Rectangle Margin="3">

<Rectangle.Fill>

<ImageBrush ImageSource="happy_face.png"

Viewbox="0.5 0.1 0.4 0.4">

</ImageBrush>

</Rectangle.Fill>

</Rectangle>

(28)

Tiled ImageBrush

Viewport określa względny rozmiar pojedynczego obrazka (a tym samym – ile powtórzeń znajdzie się w wypełnianym obszarze – niezależnie od jego rozmiaru).

<Ellipse Margin="3" Stroke="Black">

<Ellipse.Fill>

<ImageBrush ImageSource="happy_face.png"

TileMode="Tile" Viewport="0,0 0.4,0.5">

</ImageBrush>

</Ellipse.Fill>

</Ellipse>

<Ellipse Margin="3" Stroke="Black">

<Ellipse.Fill>

<ImageBrush ImageSource="happy_face.png"

TileMode="FlipXY" Viewport="0,0 0.4,0.5">

</ImageBrush>

</Ellipse.Fill>

</Ellipse>

28/58

(29)

Ustawienie ViewportUnits jako Absolute spowoduje, że obrazek nie będzie skalowany, a powtarzany ilość razy zależną od rozmiaru figury.

<Ellipse Margin="3" Stroke="Black">

<Ellipse.Fill>

<ImageBrush ImageSource="happy_face.png"

ViewportUnits="Absolute" TileMode="Tile"

Viewport="0,0 48,48">

</ImageBrush>

</Ellipse.Fill>

</Ellipse>

(30)

VisualBrush

Pozwala pobrać obraz dowolnego elementu i używać go jako pędzla. Uwaga: kopiuje to tylko wygląd elementu, skopiowane kontrolki nie dadzą się używać. Pędzel reaguje na zmianę wyglądu kontrolki (np. naciśnięcie przycisku, wpisanie wartości do pola tekstowego).

<StackPanel>

<Button Margin="3" Name="ok">OK</Button>

<Rectangle Margin="3" Height="{Binding ElementName=ok, Path=ActualHeight}">

<Rectangle.Fill>

<VisualBrush Visual="{Binding ElementName=ok}"/>

</Rectangle.Fill>

</Rectangle>

<Rectangle Margin="3" Height="50">

<Rectangle.Fill>

<VisualBrush Visual="{Binding ElementName=ok}"/>

</Rectangle.Fill>

</Rectangle>

</StackPanel>

Jest to używane np. aby pokazać podgląd zawartości okna, wygenerować miniaturę, albo do efektów specjalnych (odbicia, cienie, animacje).

30/58

(31)

Transforms

Transformacje afiniczne. Wszystkie klasy dziedziczą z System.Windows.Media.Transform.

TranslateTransform – translacja (własności X i Y) RotateTransform – obrót (Angle, CenterX, CenterY)

ScaleTransform – skalowanie (ScaleX, ScaleY, CenterX, CenterY) SkewTransform – pochylenie (AngleX, AngleY, CenterX, CenterX)

MatrixTransform – dowolne przekształcenie z wykorzystaniem własnej macierzy przekształcenia (Matrix)

TransformGroup – łączy kilka transformacji (ich kolejność jest ważna) Aby transformować kształt należy ustawić własność RenderTransform.

W obrocie określamy kąt:

<Canvas Margin="3">

<Rectangle Stroke="DarkBlue" Width="150" Height="20">

<Rectangle.RenderTransform>

<RotateTransform Angle="10"/>

</Rectangle.RenderTransform>

</Rectangle>

(32)

Można też wybrać środek obrotu:

<Canvas Margin="3">

<Rectangle Stroke="DarkBlue" Width="150" Height="20">

<Rectangle.RenderTransform>

<RotateTransform Angle="10"

CenterX="75" CenterY="10"/>

</Rectangle.RenderTransform>

</Rectangle>

...

</Canvas>

Środek można też wskazać jako wartość względną:

<Canvas Margin="3">

<Rectangle Stroke="DarkBlue" Width="150" Height="20"

RenderTransformOrigin="0.2,0.5">

<Rectangle.RenderTransform>

<RotateTransform Angle="10" />

</Rectangle.RenderTransform>

</Rectangle>

...

</Canvas>

32/58

(33)

Transforming Elements

Te same transformacje mogą być stosowane również w stosunku do elementów:

<Button Margin="3" Name="ok" RenderTransformOrigin="0.5,0.5">

<Button.RenderTransform>

<TransformGroup>

<SkewTransform AngleX="-45" />

<RotateTransform Angle="30"/>

<TranslateTransform Y="50"/>

</TransformGroup>

</Button.RenderTransform>

OK</Button>

Dostępne jest jeszcze LayoutTransform, które jest aplikowane przed ułożeniem zawartości.

<StackPanel>

<Button Margin="3" Name="ok" RenderTransformOrigin="0.5,0.5">

<Button.LayoutTransform>

<TransformGroup>

<RotateTransform Angle="45"/>

</TransformGroup>

</Button.LayoutTransform>

(34)

Transparency

Sposoby na efekt przeźroczystości:

• Własność Opacity elementu (od 1 – nieprzejrzysty do 0 – całkowicie przeźroczysty).

• Opacity pędzla – wówczas dotyczy tylko tego, co jest rysowane tym pędzlem.

• Użycie koloru o wartości alfa mniejszej niż 255 (często daje lepsze efekty niż ustawienie Opacity).

<StackPanel Background="LawnGreen">

<Button Margin="3">

Nieprzejrzysty </Button>

<Button Margin="3" Opacity="0.5" Background="Yellow">

Półprzeźroczysty </Button>

<Button Margin="3"

Background="#80FFFF00">

Półprzeźroczysty kolor </Button>

</StackPanel>

34/58

(35)

Opacity Masks

W odróżnieniu od Opacity, która ustawia jeden poziom przeźroczystości dla całego elementu, Opacity Mask pozwala na bardziej urozmaicone efekty. Akceptuje dowolny pędzel i używa jego kanału alfa (kolor nie jest ważny) by określić przezroczystość.

<StackPanel Background="LawnGreen">

<Button Margin="3" Padding="3">

<Button.OpacityMask>

<LinearGradientBrush StartPoint="0,0"

EndPoint="0,1">

<GradientStop Color="#00000000" Offset="0"/>

<GradientStop Color="#ff000000" Offset="1"/>

</LinearGradientBrush>

</Button.OpacityMask>

Opacity Mask </Button>

</StackPanel>

(36)

Wykorzystanie do efektu lustra:

<StackPanel>

<Button Margin="3" Name="ok">Naciśnij mnie</Button>

<Rectangle Margin="3" Height="{Binding ElementName=ok, Path=ActualHeight}" RenderTransformOrigin="0,0.5">

<Rectangle.Fill>

<VisualBrush Visual="{Binding ElementName=ok}"/>

</Rectangle.Fill>

<Rectangle.RenderTransform>

<ScaleTransform ScaleY="-1"/>

</Rectangle.RenderTransform>

<Rectangle.OpacityMask>

<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">

<GradientStop Color="Transparent" Offset="0.3"/>

<GradientStop Color="#80000000" Offset="1"/>

</LinearGradientBrush>

</Rectangle.OpacityMask>

</Rectangle>

</StackPanel>

36/58

(37)

Ścieżki i geometrie

Path to najbardziej złożony z dostępnych kształtów (shapes). Może zastąpić dowolne z innych kształtów, a także umożliwia posługiwanie się krzywymi.

Klasa Path ma własność Data, pobierającą obiekt Geometry (klasa abstrakcyjna). Definiuje on kształt lub kształty wchodzące w skład ścieżki. Używamy następujących klas

dziedziczących z Geometry:

LineGeometry – linia prosta.

RectangleGeometry – odpowiednik kształtu Rectangle.

EllipseGeometry – elipsa.

GeometryGroup – dodaje dowolną liczbę geometrii do jednej ścieżki.

CombinedGeometry – łączy dwie geometrie w jedne kształt. Pozwala wybrać sposób połączenia.

PathGeometry – reprezentuje złożoną figurę (otwartą lub zamkniętą), składającą się z sekwencji łuków, odcinków i krzywych.

StreamGeometry – oszczędza pamięć nie przechowując całej geometrii, ale po stworzeniu jest tylko do odczytu.

Geometry pozwala na opisanie geometrii dwuwymiarowych obiektów. Jest bardziej

(38)

Kształty można zapisać również za pomocą ścieżek:

<Rectangle Fill="Yellow"

Stroke="Blue" Width="100"

Height="50" />

<Path Fill="Yellow" Stroke="Blue">

<Path.Data>

<RectangleGeometry Rect="0,0 100,50"/>

</Path.Data>

</Path>

<Line Stroke="Blue" X1="0"

Y1="0" X2="10" Y2="100"/> <Path Fill="Yellow" Stroke="Blue">

<Path.Data>

<LineGeometry StartPoint="0,0"

EndPoint="10,100"/>

</Path.Data>

</Path>

<Ellipse Fill="Yellow"

Stroke="Blue"

Width="100" Height="50"

HorizontalAlignment="Left"/>

<Path Fill="Yellow" Stroke="Blue">

<Path.Data>

<EllipseGeometry RadiusX="50"

RadiusY="25" Center="50,25"/>

</Path.Data>

</Path>

W tej chwili jedyna widoczna korzyść to to, że nie musimy umieszczać ich w Canvas.

Prawdziwe możliwości pojawiają się gdy zechcemy połączyć kilka geometrii.

38/58

(39)

Łączenie kształtów przy pomocy GeometryGroup

To najprostszy sposób połączenie geometrii:

<Path Fill="Yellow" Stroke="Blue" Margin="5" Canvas.Top="10"

Canvas.Left="10" >

<Path.Data>

<GeometryGroup>

<RectangleGeometry Rect="0,0 100,100"/>

<EllipseGeometry Center="150,50" RadiusX="35"

RadiusY="25"/>

</GeometryGroup>

</Path.Data>

</Path>

Plusy:

• jeden obiekt zamiast dwóch – większa wydajność (jeden element złożony działa szybciej niż wiele elementów prostych)

• grupy kształtów można definiować w zasobach

(40)

W zasobach:

<Window.Resources>

<GeometryGroup x:Key="geometria">

<RectangleGeometry Rect="0 ,0 100 ,100"/>

<EllipseGeometry Center="150, 50" RadiusX="35"

RadiusY="25"/>

</GeometryGroup>

</Window.Resources>

<Canvas>

<Path Fill="Yellow"

Stroke="Blue" Margin="5"

Canvas.Top="10" Canvas.Left="10"

Data="{StaticResource geometria}">

</Path>

<Path Fill="Green"

Stroke="Blue" Margin="5"

Canvas.Top="50" Canvas.Left="50"

Data="{StaticResource geometria}">

</Path>

</Canvas>

40/58

(41)

Minusy geometrii:

• obsługa zdarzeń (np. myszy) możliwa tylko dla grupy elementów (dla Path) a nie dla każdego z nich. Istnieje jednak możliwość manipulacji pojedynczymi obiektami np. transformacja, pochylanie czy obrót

Ciekawsze rzeczy dzieją się, jeśli kształty się przecinają:

<GeometryGroup x:Key="geometria">

<RectangleGeometry Rect="0,0 100,100"/>

<EllipseGeometry Center="80, 10" RadiusX="35" RadiusY="25"/>

</GeometryGroup>

(42)

Bardziej urozmaicone sposoby łączenia kształtów oferuje CombinedGeometry Może ona łączyć tylko dwie geometrie (własności Geometry1 i Geometry2).

GeometryCombineMode określa wykonywaną na nich operację:

Union – suma

Intersect – część wspólna

Xor – alternatywa wykluczająca

Exclude – różnica

<Window.Resources>

<RectangleGeometry x:Key="kwadrat"

Rect="0 ,0 100 ,100"/>

<EllipseGeometry x:Key="koło"

Center="80, 10" RadiusX="35"

RadiusY="35"/>

<CombinedGeometry x:Key="geometria"

Geometry1="{StaticResource kwadrat}"

Geometry2="{StaticResource koło}"

GeometryCombineMode="Union" />

</Window.Resources>

42/58

(43)

<CombinedGeometry x:Key="geometria"

Geometry1="{StaticResource kwadrat}"

Geometry2="{StaticResource koło}"

GeometryCombineMode="Intersect" />

<CombinedGeometry x:Key="geometria"

Geometry1="{StaticResource kwadrat}"

Geometry2="{StaticResource koło}"

GeometryCombineMode="Xor" />

<CombinedGeometry x:Key="geometria"

Geometry1="{StaticResource kwadrat}"

Geometry2="{StaticResource koło}"

(44)

Bardziej złożone kształty należy składać z kilku CombinedGeometry.

<CombinedGeometry x:Key="geometria"

GeometryCombineMode="Union">

<CombinedGeometry.Geometry1>

<CombinedGeometry GeometryCombineMode="Exclude">

<CombinedGeometry.Geometry1>

<EllipseGeometry Center="50,50" RadiusX="50"

RadiusY="50"/>

</CombinedGeometry.Geometry1>

<CombinedGeometry.Geometry2>

<EllipseGeometry Center="50,50" RadiusX="40"

RadiusY="40"/>

</CombinedGeometry.Geometry2>

</CombinedGeometry>

</CombinedGeometry.Geometry1>

<CombinedGeometry.Geometry2>

<RectangleGeometry Rect="0,45,100,10">

<RectangleGeometry.Transform>

<RotateTransform Angle="-45" CenterX="50"

CenterY="50"/>

</RectangleGeometry.Transform>

</RectangleGeometry>

</CombinedGeometry.Geometry2>

</CombinedGeometry>

44/58

(45)

Efekt:

(46)

Krzywe i linie przy użyciu PathGeometry

PathGeometry to najbardziej zaawansowana z geometrii, może narysować wszystko to, co poprzednie (i wiele więcej). Minusem jest trochę dłuższa i czasami bardziej

skomplikowana składnia.

Każdy obiekt PathGeometry zbudowany jest z jednego lub wielu obiektów PathFigure (przechowywanych w kolekcji PathGeometry.Figures).

Każdy PathFigure jest zbiorem połączonych linii i krzywych mogących tworzyć zamknięte lub otwarte obszary.

Własności PathFigure:

StartPoint – punkt początkowy figury

Segments – kolekcja obiektów PathSegment

IsClosed – jeśli true, WPF dodaje linię łączącą punkt początkowy i końcowy )o ile nie są takie same)

IsFilled – jeśli true, wnętrze jest wypełniane przy użyciu pędzla Path.Fill PathFigure to kształt który narysowany jest linią składającą się z wielu segmentów

(PathSegment). Kolejny segment zaczyna się w punkcie, w którym skończył się poprzedni.

(Uwaga: jedna PathGeometry może zawierać wiele osobnych PathFigure, czyli może składać się z wielu osobnych figur.)

46/58

(47)

Istnieje kilka typów segmentów, dzięki czemu możemy narysować najróżniejsze kształty:

LineSegment – linia prosta łącząca dwa punkty

ArcSegment – łuk

BezierSegment – krzywa Beziera

QuadraticBezierSegment – prostsza forma krzywej Beziera, z jednym punktem kontrolnym (szybsza)

PolyLineSegment – ciąg odcinków

PolyBezierSegment, PolyQuadraticBezierSegment – ciąg krzywych Beziera LineSegment

<Path Stroke="Blue" StrokeThickness="5">

<Path.Data>

<PathGeometry>

<PathFigure IsClosed="True"

StartPoint="20,20">

<LineSegment Point="90,50" />

<LineSegment Point="50,90"/>

</PathFigure>

</PathGeometry>

(48)

ArcSegment

Łuk to część elipsy o rozmiarze Size.

Dodatkowo możemy wybrać dłuższą lub krótszą część łuku, a także położenie krzywej.

<Path Stroke="Blue" StrokeThickness="3">

<Path.Data><PathGeometry>

<PathFigure IsClosed="False" StartPoint="20,20" >

<ArcSegment Point="150,150" Size="100,100"

IsLargeArc="False" SweepDirection="Clockwise" />

</PathFigure>

</PathGeometry></Path.Data>

</Path>

<Path Stroke="Green" StrokeThickness="3">

...<ArcSegment Point="150,150"

Size="190,110" IsLargeArc="False"

SweepDirection="Clockwise" /> ...

</Path>

<Path Stroke="Red" StrokeThickness="3">

...<ArcSegment Point="150,150"

Size="200,200" IsLargeArc="False"

SweepDirection="Counterclockwise" />

...

</Path>

48/58

(49)

Krzywe Beziera

<Path Stroke="Blue" StrokeThickness="5">

<Path.Data><PathGeometry>

<PathFigure StartPoint="10,10">

<BezierSegment Point1="130,30" Point2="40,140"

Point3="150,150" />

</PathFigure>

</PathGeometry></Path.Data>

</Path>

Pierwsze dwa punkty, to punkty kontrolne, trzeci – koniec segmentu.

(punkty kontrolne i linie przerywane zostały dodane w celu poglądowym i oczywiście nie pojawiają się samoistnie na obrazie)

(50)

Geometry Mini-Language

Pozwala zdefiniować geometrię w skróconej formie: napisu zawierającego ciąg poleceń.

Każde polecenie to litera i wartości liczbowe oddzielane spacjami. Np. zamiast takiej definicji:

<Path Stroke="Blue" StrokeThickness="5">

<Path.Data>

<PathGeometry>

<PathFigure IsClosed="True" StartPoint="20,20">

<LineSegment Point="90,50" />

<LineSegment Point="50,90"/>

</PathFigure>

</PathGeometry>

</Path.Data>

</Path>

Mamy to:

<Path Stroke="Blue" StrokeThickness="5"

Data="M 20,20 L 90,50 L 50,90 Z"/>

Przecinki są opcjonalne, ale poprawiają czytelność.

Tworzony jest (niemodyfikowalny) obiekt StreamGeometry, a nie PathGeometry.

50/58

(51)

Dostępne polecenia:

F value – ustawia własność Geometry.FillRule (0 oznacza EvenOdd, 1 – Nonzero).

Może pojawić się tylko na początku stringu.

M x,y – tworzy nową ścieżkę i ustawia punkt startowy. Musi pojawić się przed jakąkolwiek inną (poza F), ale może pojawić się też później, by przemieścić punkt startowy (M znaczy Move).

L x,y 0 – tworzy LineSegment

H x – tworzy poziomy LineSegment

V y – tworzy pionowy LineSegment

A radiusX, radiusY degrees isLargeArc, isClockwise x,y – tworzy ArcSegment do wskazanego punktu

C x1,y1 x2,y2 x,y – tworzy krzywą Beziera z punktami kontrolnymi x1,y1 i x2,y2

Q x1, y1 x,y – QuadraticBezierSegment z jednym punktem kontrolnym

S x2,y2 x,y – tworzy kolejny segment Beziera, wykorzystując ostatni punkt kontrolny z poprzedniej krzywej (smooth BezierSegment)

Z – kończy PathFigure i ustawia IsClosed na true (nieobowiązkowe) Polecenie zapisane małymi znakami oznacza, że parametry są podane względem

(52)

Geometrie są używane nie tylko do definiowania ścieżek. Można ich użyć np. jako własności Clip (określa obszar obcinania).

Przeważnie stosuje się to do obcinania obrazów i grafiki, ale może być też użyte do elementów:

<StackPanel Margin="5">

<StackPanel.Clip>

<EllipseGeometry Center="100,100" RadiusX="80"

RadiusY="120" />

</StackPanel.Clip>

<Button>Jeden</Button>

<Button>Dwa</Button>

<Button>Trzy</Button>

<Button>Cztery</Button>

</StackPanel>

Minus - clipping nie bierze pod uwagę rozmiaru elementu więc przy zmianie wielkości okna element się nie zmieni.

52/58

(53)

Drawings

Uzupełnieniem Geometry jest klasa Drawing. Dodaje do geometrii informacje o tym, jak ją wyświetlić (np. kolor pędzla, grubość linii).

Drawing, podobnie jak geometria, nie jest elementem – nie można umieścić jej w oknie.

GeometryDrawing – uzupełnia geometrię (Geometry) o dane pędzla wypełnienia (Brush) i „pióra” (Pen – określa ono wszystko, co dotyczy krawędzi: jej pędzel, grubość, a także kształty końców, narożników, etc.)

DrawingGroup – kolekcja obiektów Drawing, pozwala stosować efekty na całej grupie obiektów

ImageDrawing – obraz (ImageSource) wraz z prostokątem określającym rozmiar (Rect)

VideoDrawing – własności Player i Rect

GlyphRunDrawing – elementy tekstowe niskiego poziomu wraz z pędzlem

Wyświetlanie obiektów Drawing – muszą być umieszczone w jednym z dostępnych elementów:

DrawingImage – pozwala umieścić Drawing wewnątrz elementu Image

DrawingBrush – pozwala wykorzystać Drawing jako Brush

(54)

Tak może wyglądać proste rozwiązanie umieszczające grafikę na przycisku:

<Button ... >

<Canvas ... >

<Polyline ... >

<Polyline ... >

<Rectangle ... >

<Ellipse ... >

<Polygon ... >

...

</Canvas>

</Button>

Można też zrobić to przy pomocy ścieżek (każda z własną geometrią) – bardziej wydajne:

<Button ... >

<Canvas ... >

<Path ... >

<Path ... >

...

</Canvas>

</Button>

54/58

(55)

Wykorzystując Drawing – geometria umieszczona jest w GeometryDrawing:

<GeometryDrawing Brush="...">

<GeometryDrawing.Geometry>

<EllipseGeometry .../>

<PathGeometry .../>

<RectangleGeometry .../>

</GeometryDrawing.Geometry>

</GeometryDrawing>

Drawing można umieścić w DrawingImage, który może służyć jako źródło obrazka Image:

<Image ...>

<Image.Source>

<DrawingImage>

<DrawingImage.Drawing>

<GeometryDrawing ...>

...

</GeometryDrawing>

</DrawingImage.Drawing>

</DrawingImage>

(56)

Taki obrazek można umieścić w przycisku. Wiele GeometryDrawing można łączyć przy pomocy DrawingGroup.

<Button ... >

<Image ... >

<Image.Source>

<DrawingImage>

<DrawingImage.Drawing>

<DrawingGroup>

<GeometryDrawing ... >

<GeometryDrawing ... >

<GeometryDrawing ... >

...

</DrawingGroup>

</DrawingImage.Drawing>

</DrawingImage>

<Image.Source>

</Image>

</Button>

Jeśli zamiast DrawingImage użyjemy DrawingBrush, możemy użyć go jako pędzla.

56/58

(57)

DrawingVisual – „najlżejszy” sposób rysowania, bez zdarzeń i układów, pozwala

renderować własny tekst i rysunki; nadaje się do wymagających zastosowań (np. aplikacje graficzne)

hit test dostępny jako statyczna metoda HitTest() z klasy VisualTreeHelper public partial class MainWindow : Window

{

// lista na DrawingVisual

private List<Visual> visuals = new List<Visual>();

public MainWindow() {

InitializeComponent();

// tworzymy obiekt

DrawingVisual dv = new DrawingVisual();

// rozpoczynamy rysowanie

using (DrawingContext dc = dv.RenderOpen()) {

// i rysujemy

dc.DrawRectangle(Brushes.Red,

(58)

// ...

dc.DrawEllipse(Brushes.Green,

new Pen(Brushes.Yellow, 2.0), new Point(200,100), 50, 100);

}

// dodajemy obiekt do drzewa logicznego i wizualnego visuals.Add(dv);

AddVisualChild(dv);

AddLogicalChild(dv);

}

// wymagane nadpisanie – kontener udostępnia swoje dzieci protected override int VisualChildrenCount

{

get { return visuals.Count; } }

protected override Visual GetVisualChild(int index) {

return visuals[index];

} }

58/58

Cytaty

Powiązane dokumenty

SPŁYW - szybkie przemieszczanie się masy gruntowej bez wytworzenia wyraźnej powierzchni poślizgu przy współudziale wody np.. spływy

Stan ten jest naturalnie bardzo przykry, bo nikt nie wie, jakich ma się trzymać przepisów.. czynają się pojawiać uzgodnienia tych przepisów, tworzą, się zbiory

Jeśli żadna orbita nie jest jednoelementowa, to rozmiar każdej jest podzielny przez p, zatem i |M| jest podzielna przez p.. Zamiast grafów można podobnie analizować

Jeśli żadna orbita nie jest jednoelementowa, to rozmiar każdej jest podzielny przez p, zatem i |M| jest podzielna przez p. Zamiast grafów można podobnie analizować

Zatem w modelu geometrii hiperbolicznej istnieją dwie proste przechodzące przez punkt x (tak naprawdę nieskończenie wiele), które nie przecinają prostej L.. Pokazaliśmy w ten

też inne parametry algorytmu, często zamiast liczby wykonywanych operacji rozważa się rozmiar pamięci, której używa dany algorytm. Wówczas mówimy o złożoności pamięciowej;

Gdy pojazd się do nas zbliża, ton syreny jest wysoki (krótsza fala), po czym zmienia się na niższy (dłuższa fala), gdy pojazd zaczyna się

Jest to program mający pokazać dany produkt na okres paru dni lub na liczbę uruchomień.. Ma trzy ograniczenia: niemożna drukować, zapisywać i innych