wykład 8

Transkrypt

wykład 8
Programowanie obiektowe i zdarzeniowe
wykład 8 – grafika i animacja
Grafika
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 traktowane są jak elementy:
 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, etc.).
 Odpowiadają za rysowanie samych siebie (i automatycznie reagują na zmianę
właściwości).
Marek Tabędzki
1/62
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 wielu innych kształtów
Najważniejsze właściwości:
 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.
Marek Tabędzki
2/62
Rectangle, 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>
Marek Tabędzki
3/62
Rectangle dodaje dwie własności: RadiusX i RadiusY (pozwalają na rysowanie
zaokrąglonych narożników).
<StackPanel>
<Rectangle 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"
RadiusX="15" RadiusY="15"/>
</StackPanel>
Marek Tabędzki
4/62
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>
Marek Tabędzki
5/62
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).
Marek Tabędzki
6/62
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).
Marek Tabędzki
7/62
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>
Marek Tabędzki
8/62
(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).
Marek Tabędzki
9/62
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.
Marek Tabędzki
10/62
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>
Marek Tabędzki
11/62
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"/>
Marek Tabędzki
12/62
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"/>
Marek Tabędzki
13/62
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"/>
Marek Tabędzki
14/62
<Polygon Stroke="MediumBlue" Fill="LightBlue" FillRule="Nonzero"
StrokeThickness="5" Points="95,20 160,150 20,70 180,60 50,160"/>
Marek Tabędzki
15/62
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>
Marek Tabędzki
16/62
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>
Marek Tabędzki
17/62
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.
Marek Tabędzki
18/62
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).
Marek Tabędzki
19/62
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>
</Rectangle.Fill>
</Rectangle>
Marek Tabędzki
20/62
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>
Marek Tabędzki
21/62
<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>
Marek Tabędzki
22/62
<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.)
Marek Tabędzki
23/62
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>
Marek Tabędzki
24/62
<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>
Marek Tabędzki
25/62
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>
Marek Tabędzki
26/62
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>
Marek Tabędzki
27/62
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>
Marek Tabędzki
28/62
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).
Marek Tabędzki
29/62
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>
...
</Canvas>
Marek Tabędzki
30/62
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>
Marek Tabędzki
31/62
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>
OK</Button>
</StackPanel>
Marek Tabędzki
32/62
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>
Marek Tabędzki
33/62
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>
Marek Tabędzki
34/62
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>
Marek Tabędzki
35/62
Ś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 łączenie kształtów oraz posługiwanie się krzywymi.
Klasa Path ma własność Data, pobierającą obiekt typu Geometry. Definiuje on kształt (lub
kształty) wchodzące w skład ścieżki.
Proste geometrie:
 LineGeometry – linia prosta.
 RectangleGeometry – odpowiednik kształtu Rectangle.
 EllipseGeometry – elipsa.
Geometrie złożone:
 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.
Marek Tabędzki
36/62
Ścieżka jako zamiennik kształtu:
<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.
Marek Tabędzki
37/62
Łą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 i wykorzystywać w innych
elementach ścieżkach
Marek Tabędzki
38/62
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
<Path Fill="Yellow" Stroke="Blue" Canvas.Top="40" Canvas.Left="20">
<Path.Data>
<CombinedGeometry GeometryCombineMode="Union">
<CombinedGeometry.Geometry1>
<RectangleGeometry Rect="0,0 100,100"/>
</CombinedGeometry.Geometry1>
<CombinedGeometry.Geometry2>
<EllipseGeometry Center="80,10"
RadiusX="35" RadiusY="35"/>
</CombinedGeometry.Geometry2>
</CombinedGeometry>
</Path.Data>
</Path>
Marek Tabędzki
39/62
GeometryCombineMode="Intersect"
GeometryCombineMode="Xor"
GeometryCombineMode="Exclude"
Marek Tabędzki
40/62
Bardziej złożone kształty należy składać z kilku CombinedGeometry.
<CombinedGeometry 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>
Marek Tabędzki
41/62
Efekt:
Marek Tabędzki
42/62
Geometry Mini-Language
Pozwala zdefiniować geometrię w formie tekstu zawierającego ciąg poleceń. Każde
polecenie to litera i wartości liczbowe oddzielane spacjami.
Ścieżka jest wówczas zbiorem połączonych linii i krzywych mogących tworzyć zamknięte
lub otwarte obszary.
Przykład:
<Path Stroke="Blue" StrokeThickness="5"
Data="M 20,20 L 90,50 L 50,90 Z"/>
Przecinki są opcjonalne, ale poprawiają czytelność.
Marek Tabędzki
43/62
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 – tworzy odcinek (linię) do wskazanego punktu
 H x – tworzy poziomy odcinek
 V y – tworzy pionowy odcinek
 A radiusX, radiusY degrees isLargeArc, isClockwise x,y – tworzy łuk elipsy do
wskazanego punktu, podajemy promienie elipsy i wybieramy która część nas
interesuje
 C x1,y1 x2,y2 x,y – tworzy krzywą Beziera z punktami kontrolnymi x1,y1 i x2,y2
 Q x1, y1 x,y – prostsza forma krzywej Beziera (Quadratic Bezier), z jednym
punktem kontrolnym
 S x2,y2 x,y – tworzy kolejny segment Beziera, wykorzystując ostatni punkt
kontrolny z poprzedniej krzywej (Smooth Bezier)
 Z – kończy PathFigure i ustawia powoduje jej zamknięcie (nie jest obowiązkowe,
jeśli chcemy mieć figurę otwartą)
Polecenie zapisane małymi znakami oznacza, że parametry są podane względem poprzedniego
punktu (a nie wartości absolutne).
Marek Tabędzki
44/62
Animacje





Animacje w WPF definiuje się przeważnie w sposób deklaratywny (w XAML).
Stworzenie animacji wymaga określenia jaka właściwość elementu w jaki sposób ma
się zmieniać w czasie.
Ograniczenie: animacje dotyczą tylko dependency properties (a co za tym idzie, nie da
się uzyskać w ten sposób efektów, których nie osiągniemy modyfikując własności, np.
nie można animacją dodawać elementów do okna). Mogą one służyć uatrakcyjnieniu
standardowej aplikacji, ale nie nadają się np. do gier.
Każda animacja operuje na pojedynczej własności.
Animowanie własności wymaga użycia klasy animacji właściwej dla typu tej
własności.
Klasy animacji:
 zmieniających pewną wartość na drodze liniowej interpolacji (z wartości From do
wartości To lub zwiększając wartość aktualną o By), np. DoubleAnimation
 zmieniających pewną wartość w określonych punktach czasu, np.
DoubleAnimationUsingKeyFrames
 animacje wykorzystujące ścieżkę (PathGeometry), np. DoubleAnimationUsingPath
Nie ma klas do animowania własności typu wyliczeniowego (np. HorizontalAlignment).
Zazwyczaj nie animuje się też typów referencyjnych (np. Brush – zamiast tego używa się np.
ColorAnimation, DoubleAnimation do animacji własności pędzla).
Marek Tabędzki
45/62
Przykład:
Animacja w XAMLu definiowana jest w obiekcie storyboard. Reprezentuje on przebieg
animacji, pozwala na grupowanie kilku animacji, kontrolowanie animacji. Do
uruchomienia animacji konieczny jest EventTrigger. Jego zadaniem jest odpalenie
animacji.
<Window.Triggers>
<EventTrigger RoutedEvent="Window.Loaded">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation From="0" To="300"
Duration="0:0:1" Storyboard.TargetName="pr"
Storyboard.TargetProperty="(Canvas.Left)"/>
<DoubleAnimation From="0" To="200"
Duration="0:0:1" Storyboard.TargetName="pr"
Storyboard.TargetProperty="(Canvas.Top)"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Window.Triggers>
Marek Tabędzki
46/62
<Canvas>
<Rectangle Name="pr" Width="20" Height="20" Fill="Orange"
Stroke="Black" StrokeThickness="2"/>
</Canvas>
Marek Tabędzki
47/62
Inne właściwości animacji:
<Storyboard>
<DoubleAnimation From="0" To="300" BeginTime="0:0:2"
AutoReverse="True" RepeatBehavior="Forever"
AccelerationRatio="0.2" DecelerationRatio="0.2"
Duration="0:0:1" Storyboard.TargetName="pr"
Storyboard.TargetProperty="(Canvas.Left)"/>
<DoubleAnimation From="0" To="200" BeginTime="0:0:2"
AutoReverse="True" RepeatBehavior="Forever"
AccelerationRatio="0.2" DecelerationRatio="0.2"
Duration="0:0:1" Storyboard.TargetName="pr"
Storyboard.TargetProperty="(Canvas.Top)"/>
</Storyboard>
Marek Tabędzki
48/62
Posługiwanie się animacjami
Co animować?
 Jeśli chcemy, by elementy pojawiały się i znikały: Opacity.
 Jeśli chcemy przemieszczać elementy: Canvas.Left, Canvas.Top (jeśli kontenerem
jest Canvas), ewentualnie Margin lub/i Padding (przy pomocy
ThicknessAnimation), ewentualnie MinWidth i MinHeight kolumny lub wiersza w
Gridzie.
 RenderTransform – aby przemieszczać (TranslateTransform), obracać
(RotateTransform), skalować (ScaleTransform) elementy. Działają szybciej niż
animacja własności określających rozmiar czy pozycję.
 ColorAnimation – aby modyfikować własności pędzla.
Marek Tabędzki
49/62
Animacja transformacji
Każdy element ma dwa rodzaje transformacji: RenderTransform (stosowana przed
wyświetleniem obiektu) i LayoutTransform (stosowana przed ułożeniem elementów przez
kontener). (Animacja nie może stworzyć transformacji, a jedynie animować własności
istniejącej.)
Przykład: obracający się przycisk.
<Button RenderTransformOrigin="0.5,0.5">
<Button.RenderTransform>
<RotateTransform/>
</Button.RenderTransform>
OK
</Button>
Marek Tabędzki
50/62
EventTrigger do uruchomienia animacji:
<EventTrigger RoutedEvent="Button.MouseEnter">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty=
"RenderTransform.Angle" To="360" Duration=
"0:0:1" RepeatBehavior="Forever"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
Marek Tabędzki
51/62
I do zakończenia (powrotu do stanu początkowego – nie określamy wartości docelowej):
<EventTrigger RoutedEvent="Button.MouseLeave">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty=
"RenderTransform.Angle" Duration="0:0:0.2"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
Marek Tabędzki
52/62
Parametry przycisku możemy ustawić w stylu:
<Style TargetType="Button">
<Setter Property="Margin" Value="3" />
<Setter Property="Padding" Value="20,3"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="RenderTransformOrigin"
Value="0.5,0.5"/>
<Setter Property="RenderTransform">
<Setter.Value>
<RotateTransform></RotateTransform>
</Setter.Value>
</Setter>
<Style.Triggers>
<EventTrigger ...
<EventTrigger ...
</Style.Triggers>
</Style>
Marek Tabędzki
53/62
Teraz każdy z przycisków będzie używał tej animacji:
<StackPanel>
<Button>jeden</Button>
<Button>dwa</Button>
<Button>trzy</Button>
<Button>cztery</Button>
<Button>pięć</Button>
</StackPanel>
Marek Tabędzki
54/62
Jeśli wymienimy RenderTransform na LayoutTransform:
Marek Tabędzki
55/62
Animowanie pędzli
Kolor pędzla animujemy przy pomocy klasy ColorAnimation. Wymaga stworzenia pędzla:
<Ellipse Stretch="Uniform" Margin="3" Name="elipsa">
<Ellipse.Fill>
<RadialGradientBrush>
<GradientStop Color="Yellow" Offset="0"/>
<GradientStop Color="LawnGreen" Offset="1"/>
</RadialGradientBrush>
</Ellipse.Fill>
</Ellipse>
Marek Tabędzki
56/62
Teraz możemy ustawić jego właściwości:
<BeginStoryboard>
<Storyboard>
<ColorAnimation Storyboard.TargetName="elipsa"
To="DeepSkyBlue" Storyboard.TargetProperty=
"Fill.GradientStops[1].Color" Duration="0:0:1"
AutoReverse="True" RepeatBehavior="Forever"/>
</Storyboard>
</BeginStoryboard>
Marek Tabędzki
57/62
PointAnimation pozwala przemieszczać punkt (działa to jak DoubleAnimation składowej
X i Y). Dzięki temu możemy np. modyfikować kształt figury zbudowanej z punktów. W
poniższym przykładzie animujemy centralny punkt gradientu radialnego.
<PointAnimation Storyboard.TargetName="elipsa"
Storyboard.TargetProperty="Fill.GradientOrigin"
From="0.7,0.3" To="0.3,0.7" Duration="0:0:1"
AutoReverse="True" RepeatBehavior="Forever" />
Marek Tabędzki
58/62
Animacja z wykorzystaniem klatek kluczowych
Pozwala zdefiniować większą liczbę etapów animacji. Każda „klatka” ma określoną
wartość docelową oraz czas. Można w ten sposób definiować bardziej złożony przebieg
animacji (zmian wartości). Przykład:
<Grid>
<Ellipse Width="20" Height="20" Stroke="Black"
Fill="Yellow" Name="elipsa" />
</Grid>
Marek Tabędzki
59/62
Animacja rozmiaru:
<Window.Triggers>
<EventTrigger RoutedEvent="Window.Loaded">
<BeginStoryboard>
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetName=
"elipsa" Storyboard.TargetProperty="Width"
RepeatBehavior="Forever">
<LinearDoubleKeyFrame Value="20" KeyTime="0:0:0"/>
<LinearDoubleKeyFrame Value="200" KeyTime="0:0:1"/>
<LinearDoubleKeyFrame Value="200" KeyTime="0:0:2"/>
<LinearDoubleKeyFrame Value="20" KeyTime="0:0:3"/>
<LinearDoubleKeyFrame Value="20" KeyTime="0:0:4"/>
</DoubleAnimationUsingKeyFrames>
...
Marek Tabędzki
60/62
...
<DoubleAnimationUsingKeyFrames Storyboard.TargetName=
"elipsa" Storyboard.TargetProperty="Height"
RepeatBehavior="Forever">
<LinearDoubleKeyFrame Value="20" KeyTime="0:0:0"/>
<LinearDoubleKeyFrame Value="20" KeyTime="0:0:1"/>
<LinearDoubleKeyFrame Value="200" KeyTime="0:0:2"/>
<LinearDoubleKeyFrame Value="200" KeyTime="0:0:3"/>
<LinearDoubleKeyFrame Value="20" KeyTime="0:0:4"/>
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</Window.Triggers>
Klatki określają węzły animacji – punkty, do jakich ma się udać animacja, wartości jakie
mamy przyjąć. Dzielą one animację na wykonywane po kolei segmenty.
Marek Tabędzki
61/62
Poza interpolacją liniową między klatkami kluczowymi, możemy wybrać wartości
dyskretne (nie dokonuje interpolacji – wartość jest przyjmowana w określonej chwili
czasu):
<ColorAnimationUsingKeyFrames Storyboard.TargetName="elipsa"
Storyboard.TargetProperty="Fill.Color"
RepeatBehavior="Forever">
<DiscreteColorKeyFrame Value="Yellow" KeyTime="0:0:0"/>
<DiscreteColorKeyFrame Value="Red" KeyTime="0:0:0.3"/>
<DiscreteColorKeyFrame Value="Green" KeyTime="0:0:0.6"/>
<DiscreteColorKeyFrame Value="Blue" KeyTime="0:0:0.9"/>
<DiscreteColorKeyFrame Value="Yellow" KeyTime="0:0:1.2"/>
</ColorAnimationUsingKeyFrames>
Wartość KeyTime (koniec segmentu animacji) może być również określana procentowo.
Możemy używać różnych interpolacji w obrębie jednej animacji.
Marek Tabędzki
62/62

Podobne dokumenty