(Achtung - es fängt einfach an, und wird dann immer komplizierter )
Linien, Figuren, Formen werden in Wpf als
Die
Während nun ein Shape ein im Xaml direkt sichtbares FrameworkElement ist, ist eine
Auch von
Um es erwähnt und geklärt zu haben:
In eine
Eine
Eine
Das Xaml-Path-Markup
Damit kann man wie gesagt eine StreamGeometry definieren, aber auch eine PathFigureCollection.
Also bei den folgenden eiglich gleichen Path-Shapes enthält das erste eine StreamGeometry, und der zweite eine PathGeometry mit einer PathFigureCollection (der Unterschied wird aber nur in fortgeschrittenen Szenarien relevant):
PathGeometry, PathFigure, PathSegment
Ein Path-Markup definiert eine PathGeometry, mit mehrere PathFigures darinnen. Eine PathFigure wiederum besteht aus mehreren PathSegments. Unter "Segment" ist etwas zu verstehen, was den vorherigen Punkt mit einem weiteren Punkt verbindet.
Ob nun gradlienig oder gekrümt: Ein Segment verbindet immer den vorherigen Punkt mit seinem eigenen - und deshalb kann man Segmente verketten - quasi: "malen, ohne den Stift abzusetzen".
Oder anders: Eine PathGeometry enthält viele Linien, und jede Linie (PathFigure) verbindet zwei Punkte über viele PathSegment-Zwischenstationen.
Also eine PathFigure hat einen Startpunkt und daran hängen viele Segmente.
Beachte aber auch: ein Kreis, oder ein Rechteck, kann niemals ein Segment sein - denn als geschlossene Figur gibts da ja keinen Start- und End-Punkt, durch eine Linie zu verbinden.
Der Startpunkt einer PathFigure wird durch
Insgesamt ermöglicht das Markup folgende Anweisungen:
Die Segmente unter diesen Anweisungen, also
Das gilt für alle anderen Segmente entsprechend - allerdings werden PolySegmente schnell unübersichtlich - denn es sind reine Zahlen-Wüsten.
Segmente verketten
PolySegmente sind nur eine Sonderform der Verkettung (nämlich desselben Segment-Typs). Bei Verkettung unterschiedlicher Segmente gibt man natürlich für jedes Glied den Key mit an:
(Beachte, dass Bezier-Control-Punkte nicht auf der Linie liegen.)
Relative Koordinaten
Alle Anweisungen können auch mit relativen Koordinaten vereinbart werden - dazu den Key in Kleinschrift notieren. Insbesondere Segment-Verkettungen sind in relativer Notation leichter lesbar. Ich setze am liebsten den Figur-Startpunkt absolut, und hänge die Segmente dann relativ notiert dran, zB die PolyLine-PathFigure von vorhin:
Das ArcSegment
ist wohl am schwierigsten zu verstehen. Seine Argumente sind:
Betrachten wir folgendes Ellipsen-Segment:
Die Radien sind
Den großen Bogen
Der kleine Bogen
Und gegen den Uhrzeigersinn gedreht sieht der große Bogen so aus -
Ich kann auch beides verketten -
genau hingucken: Die erste Ellipse dreht rechtsrum und verbindet nach 20,20. Die zweite Ellipse setzt dort fort, dreht aber linksrum. Insgesamt ist also die Strecke 40,40 verbunden durch eine komische Schleife.
Also es ist eine extrem gewöhnungsbedürftige Art, Ellipsen-Segmente zu definieren, aber sehr kompakt, und Schräglagen sind unterstützt, wofür man zB beim
Eine Unmöglichkeit/Besonderheit entsteht übrigens, wenn die Ellipsen-Radien zu klein definiert sind, als dass ein Bogen überhaupt zum Zielpunkt gespannt werden könnte: WPFs Lösung besteht darin, dass die Ellipse dann proportional vergrößert wird, bis passt:Nicht wahr: Dieselbe Ellipse (
Bei vergrößerten Ellipsen ist die Angabe von largeArc auch irrelevant, denn übergroße ArcSegmente sind immer genau Halb-Ellipsen und beide Bögen somit gleichlang.
Die Sample-App
Ist eine Art Multi-Spielwiese. Auf einem Tab kann man Figuren eintippen, und gucken, wie's rauskommt. Und man kann auch verschiedene Experimente abspeichern.
Auf dem anderen Tab hab ich was aufwändiges programmiert, dass man die Punkte der Figuren mit der Maus herumdraggen kann:
Der dritte Tab ist eine Art runde Progressbar, die zeigt ein KreisSegment zwischen 0° und 359.999° (denn ein Vollkreis ist mit ArcSegment nicht möglich)
Skalierbaren Zeichenbereich abstecken
Beim Round-Progress-Tab verwende ich einen Trick. Nämlich beim Path-Shape kann man
Daher stecke ich zuvor mit zwei
Also das Markup obigen Bildles lautet:
Die ersten beiden
Vollkreis bei ArcSegment
Ist nicht möglich. Denn dabei wäre die Länge der verbundenen Strecke 0, und eine Ellipse durch nur einen Punkt zu legen ist nicht eindeutig definierbar. Stattdessen assoziiere ich beim RoundProgress
Further Readings
Bei meinen Recherchen stieß ich auf eine wahre Tutorial-Monster-Site, deren etwa 30 kurze und ausgezeichnet strukturierte Kapitel zum Thema "Shapes und Pathes" nur einen Winz-Bruchteil des Gesamt-Fundus' ausmachen:
Black Wasp
Schließlich fund ich auch bei Microsoft eine Dokumentation, eigenartigerweise eiglich zu Silverlight: Path Markup Syntax
Dort wird die "Mini-Language" so erklärt: Move (M) setzt den Stift. Das Draw-Command kann aus mehreren Formen (Shapes) bestehen, verschiedener Art: Line, Q-Bezier, Bezier, Arc
Linien, Figuren, Formen werden in Wpf als
Shape
dargestellt, und deren gibt es mehrere, die von der Shape
-Basisklasse erben:Ellipse
, Line
, Polygon
, PolyLine
, Rectangle
, Path
Die
Path
-Klasse ist am interessantesten, denn ihre Data
-Property enthält eine beliebige Geometry
.Während nun ein Shape ein im Xaml direkt sichtbares FrameworkElement ist, ist eine
Geometry
nur die abstrakte Beschreibung einer (Multi-)Linie / Form.Auch von
Geometry
wird geerbt, nämlich die folgenden nützlichen Klassen:EllipseGeometry
, LineGeometry
, RectangleGeometry
, CombinedGeometry
, GeometryGroup
, StreamGeometry
und PathGeometry
.Um es erwähnt und geklärt zu haben:
In eine
GeometryGroup
schmeisst man mehrere andere Geometries, die dabei nicht miteinander interagieren.Eine
CombinedGeometry
kombiniert zwei Geometries mit den Optionen Exclude
, Intersect
, Union
oder Xor
.Eine
StreamGeometry
ist eine optimierte PathGeometry
, die ggfs. durch besonderes Xaml-Markup generiert wird - letzteres ist das Haupt-Thema dieses TutsDas Xaml-Path-Markup
Damit kann man wie gesagt eine StreamGeometry definieren, aber auch eine PathFigureCollection.
Also bei den folgenden eiglich gleichen Path-Shapes enthält das erste eine StreamGeometry, und der zweite eine PathGeometry mit einer PathFigureCollection (der Unterschied wird aber nur in fortgeschrittenen Szenarien relevant):
PathGeometry, PathFigure, PathSegment
Ein Path-Markup definiert eine PathGeometry, mit mehrere PathFigures darinnen. Eine PathFigure wiederum besteht aus mehreren PathSegments. Unter "Segment" ist etwas zu verstehen, was den vorherigen Punkt mit einem weiteren Punkt verbindet.
Ob nun gradlienig oder gekrümt: Ein Segment verbindet immer den vorherigen Punkt mit seinem eigenen - und deshalb kann man Segmente verketten - quasi: "malen, ohne den Stift abzusetzen".
Oder anders: Eine PathGeometry enthält viele Linien, und jede Linie (PathFigure) verbindet zwei Punkte über viele PathSegment-Zwischenstationen.
Also eine PathFigure hat einen Startpunkt und daran hängen viele Segmente.
Beachte aber auch: ein Kreis, oder ein Rechteck, kann niemals ein Segment sein - denn als geschlossene Figur gibts da ja keinen Start- und End-Punkt, durch eine Linie zu verbinden.
Der Startpunkt einer PathFigure wird durch
M
gemarkupt - und dann folgen die Segmente.Insgesamt ermöglicht das Markup folgende Anweisungen:
Key | Bedeutung | Argumente | Bemerkung |
M | Startpunkt | {ptStart X,Y} | kein Segment, sondern ptStart ist Eigenschaft der die Segmente beinhaltenden PathFigure |
L | Line | {ptTarget X,Y} | LineSegment |
H | Horizontal Line | {target-X} | LineSegment |
V | Vertical Line | {target-Y} | LineSegment |
Z | CloseFigure | LineSegment zurück zum Startpunkt Neue Figur beginnen, mit demselben Startpunkt | |
A | Arc-Segment | {radius X,Y} {rotation-Deg} {large 0/1} {clockwise 0/1} {ptTarget} | Ellipse-Abschnitt (Erläuterungen folgen) |
Q | Quadratic Bezier | {ptCtrl} {ptTarget} | der Control-Punkt erzeugt eine Ausbauchung |
C | Cubic Bezier | {ptCtrl1} {ptCtrl2} {ptTarget} | ptCtrl1 + ptCtrl2 erzeugen 2 Ausbauchungen |
S | Smooth Cubic Bezier | {ptCtrl2} {ptTarget} | Cubic Bezier (s.o.) mit knickfreiem Anschluss, da ptCtrl1 autom. berechnet wird |
F | Fill-Rule | {fillRule 0/1} | füllt die durch die figur aufgespannte Fläche 0: even-odd-rule, 1: non-zero-rule 0: innere Figuren gelten als ausserhalb (unausgefüllt) 1: innere Figuren sind immer ausgefüllt |
Die Segmente unter diesen Anweisungen, also
L H V A Q C S
kann man auch als PolySegmente notieren - dabei muss man den Key nicht wiederholen. ZB sowas wäre eine PathFigure mit zweigliedriger PolyLine: M10,10 L60,10 35,35
.Das gilt für alle anderen Segmente entsprechend - allerdings werden PolySegmente schnell unübersichtlich - denn es sind reine Zahlen-Wüsten.
Segmente verketten
PolySegmente sind nur eine Sonderform der Verkettung (nämlich desselben Segment-Typs). Bei Verkettung unterschiedlicher Segmente gibt man natürlich für jedes Glied den Key mit an:
M10,10 L60,10 35,35 Q65,95,100,35
- das wäre etwa obige PolyLine mit angehängtem quadratischen Bezier-Bogen:(Beachte, dass Bezier-Control-Punkte nicht auf der Linie liegen.)
Relative Koordinaten
Alle Anweisungen können auch mit relativen Koordinaten vereinbart werden - dazu den Key in Kleinschrift notieren. Insbesondere Segment-Verkettungen sind in relativer Notation leichter lesbar. Ich setze am liebsten den Figur-Startpunkt absolut, und hänge die Segmente dann relativ notiert dran, zB die PolyLine-PathFigure von vorhin:
M10,10 l50,0 -25,25
Man sieht sofort: zunächst eine Horizontale der Länge 50 nach rechts, dann eine 45° Diagonale nach links unten, X-Komponente des Zielpunktes liegt genau auf der Hälfte der ersten Linie, also insgesamt ist ein gleichseitiges rechtwinkliges Dreieck aufgespannt - naja - sieht man ja auch im Bildle oben, wenn man sich den Bezier wegdenkt .Das ArcSegment
ist wohl am schwierigsten zu verstehen. Seine Argumente sind:
{radius X,Y} {rotation-Deg} {large 0/1} {clockwise 0/1} {ptTarget}
Betrachten wir folgendes Ellipsen-Segment:
M100,100 a80,40,45,1,1, 20,20
Die Radien sind
80
,40
, um 45°
rotiert, gezeichnet wird 1
der große Bogen, und zwar 1
im Uhrzeigersinn, und verbunden wird die relative Strecke 20,20
. Das ist recht kurz, und ebenfalls 45° geneigt, denn die X/Y - Komponenten der verbundenen Strecke sind ja gleich.Den großen Bogen
1
zeichnen stellt die Ellipse fast komplett dar, mit einer Lücke:Der kleine Bogen
0
ist entsprechend winzig - M100,100 a80,40,45,0,1, 20,20
: Und gegen den Uhrzeigersinn gedreht sieht der große Bogen so aus -
M100,100 a80,40,45,1,0, 20,20
:Ich kann auch beides verketten -
M100,100 a80,40,45,1,1, 20,20 a80,40,45,1,0, 20,20
oder als PolyArc:M100,100 a80,40,45,1,1, 20,20 80,40,45,1,0, 20,20
:genau hingucken: Die erste Ellipse dreht rechtsrum und verbindet nach 20,20. Die zweite Ellipse setzt dort fort, dreht aber linksrum. Insgesamt ist also die Strecke 40,40 verbunden durch eine komische Schleife.
Also es ist eine extrem gewöhnungsbedürftige Art, Ellipsen-Segmente zu definieren, aber sehr kompakt, und Schräglagen sind unterstützt, wofür man zB beim
Ellipse
-Shape zusätzlich in aller Umständlichkeit eine Matrix-Transformation ansetzen müsste.Eine Unmöglichkeit/Besonderheit entsteht übrigens, wenn die Ellipsen-Radien zu klein definiert sind, als dass ein Bogen überhaupt zum Zielpunkt gespannt werden könnte: WPFs Lösung besteht darin, dass die Ellipse dann proportional vergrößert wird, bis passt:Nicht wahr: Dieselbe Ellipse (
80,40,45,1,1
), jedoch die Strecke 150,0
kann nur verbunden werden, wenn Ellipse#1 sich vergrößert.Bei vergrößerten Ellipsen ist die Angabe von largeArc auch irrelevant, denn übergroße ArcSegmente sind immer genau Halb-Ellipsen und beide Bögen somit gleichlang.
Die Sample-App
Ist eine Art Multi-Spielwiese. Auf einem Tab kann man Figuren eintippen, und gucken, wie's rauskommt. Und man kann auch verschiedene Experimente abspeichern.
Auf dem anderen Tab hab ich was aufwändiges programmiert, dass man die Punkte der Figuren mit der Maus herumdraggen kann:
Der dritte Tab ist eine Art runde Progressbar, die zeigt ein KreisSegment zwischen 0° und 359.999° (denn ein Vollkreis ist mit ArcSegment nicht möglich)
Skalierbaren Zeichenbereich abstecken
Beim Round-Progress-Tab verwende ich einen Trick. Nämlich beim Path-Shape kann man
Stretch="Fill"
einstellen, dann stretcht er die Figur auf maximale Größe im verfügbaren Layout-Bereich. Nur ein sich verkürzendes/verlängerndes ArcSegment ändert ja seine Gesamt-Größe. Und würde daher immer verschieden skaliert, sodass die Figur nie ordentlich auf einer Kreisbahn bleibt.Daher stecke ich zuvor mit zwei
M
-Anweisungen den Gesamt-Rahmen des Pathes ab, den mein Arc-Segment nicht überschreitet. Sodass die Gesamtfigur immer eine konstante Größe hat (die des Vollkreises)- egal wie groß oder klein das ArcSegment darinnen ist. Also das Markup obigen Bildles lautet:
M-50,-50 M50,50, M0,-50 A50,50,0,1,1 -48.9, -10.4
Die ersten beiden
M
-Anweisungen sind leere PathFigures, die den Bereich abstecken, übrigens so, dass die Bild-Mitte genau auf 0,0 liegt. Startpunkt des Arcs ist logischerweise M0,-50
, also Oben-Mitte, und Endpunkt ist etwas rechts oberhalb von -50,0 (Mitte-Links).Vollkreis bei ArcSegment
Ist nicht möglich. Denn dabei wäre die Länge der verbundenen Strecke 0, und eine Ellipse durch nur einen Punkt zu legen ist nicht eindeutig definierbar. Stattdessen assoziiere ich beim RoundProgress
100%
mit 359,999°
, sodass immer eine mikroskopisch kleine Strecke übrig bleibt: -0.000087,0.0000000077
. Diese Lücke im Vollkreis ist unsichtbar winzig, und daher ist das Ergebnis auffm Bildschirm perfekt Further Readings
Bei meinen Recherchen stieß ich auf eine wahre Tutorial-Monster-Site, deren etwa 30 kurze und ausgezeichnet strukturierte Kapitel zum Thema "Shapes und Pathes" nur einen Winz-Bruchteil des Gesamt-Fundus' ausmachen:
Black Wasp
Schließlich fund ich auch bei Microsoft eine Dokumentation, eigenartigerweise eiglich zu Silverlight: Path Markup Syntax
Dort wird die "Mini-Language" so erklärt: Move (M) setzt den Stift. Das Draw-Command kann aus mehreren Formen (Shapes) bestehen, verschiedener Art: Line, Q-Bezier, Bezier, Arc
Dieser Beitrag wurde bereits 12 mal editiert, zuletzt von „ErfinderDesRades“ ()