Das ist ziemlich einfach und es funktioniert so.
Generelle Vorgehensweise
In den Eregnissen MouseLeftButtonDown, MouseLeftButtonUp und MouseMove werden, dem Beispiel von Sharker Khaleed Mahmut folgend, zunächst die Daten für das Erzeugen der Strokes auf dem InkPresnter erzeugt. Zusätzlich wird eine private Variable "pointList" erstellt, die vom Typ "List(Of StylusPointCollection)" ist. Dieser Variablen werden die gleichen Positionsdaten hinzugefügt, die in den Ereignisbehandlern MouseLeftButtonDown, MouseLeftButtonUp und MouseMove zum Erzeugen der Strokes auf dem InkPresenter verwendet werden. In "pointList" wird jeweils der Rückgabewert vom Typ StylusPointCollection gesammelt. Eine StylusPointCollection hat unter anderem einen X-Koordinatenwert und einen Y-Koordinatenwert. Diese in "pointList" gesammelten Positionsdaten einzelner Punkte werden dann verwendet, um einen Pfad zu erzeugen.
Und so funktioniert es Schritt für Schritt.
Schritt für Schritt
Schritt 1 - Vorbereitungen
Es wird ein neues Silverlight 4-Projekt erzeugt. Der Oberfläche wird ein Canvas (x:Name="cvElements") mit dem "Z-Index" "0" hinzugefügt. Dann wird ein InkPresenter ("inkTest") hinzugefügt mit einem Wert für den "Z-Index" von "1". Der InkPresenter wird mit der gleichen Größe und den gleichen Margin-Werten wie das Canvas-Steuerelement erzeugt.
Schritt 2 - Positionsdaten sammeln
Es wird die private Variable "pointList" erzeugt:
Private pointList As New List(Of StylusPointCollection)
In den Ereignis-Behandlern von MouseLeftButtonDown (wenn der erste Punkte gezeichnet wird), MouseMove (wenn im Rythmus der MouseEventArgs fortwährend Punkte gezeichnet werden) von MouseLeftButtonUp (wenn der letzte Punkt gezeichnet wird), werden jeweils die einzelnen Punkte der "PointList" hinzugefügt.
Private Sub inkTest_MouseLeftButtonDown(ByVal sender As Object, ByVal e As System.Windows.Input.MouseButtonEventArgs) Handles inkTest.MouseLeftButtonDown
inkTest.CaptureMouse()
Dim points As StylusPointCollection = e.StylusDevice.GetStylusPoints(inkTest)
currentStroke = New Stroke(points)
currentStroke.DrawingAttributes.Color = Colors.Blue
currentStroke.DrawingAttributes.Width = 10
currentStroke.DrawingAttributes.Height = 10
inkTest.Strokes.Add(currentStroke)
' Grab the position to fill the List(Of StylusPointCollection)
pointList.Add(e.StylusDevice.GetStylusPoints(inkTest))
'-------------------------------------------------------------
End Sub
Private Sub inkTest_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Input.MouseEventArgs) Handles inkTest.MouseMove
If currentStroke IsNot Nothing Then
pointList.Add(e.StylusDevice.GetStylusPoints(inkTest))
currentStroke.StylusPoints.Add(points)
' Grab the position to fill the List(Of StylusPointCollection)
Dim points As StylusPointCollection = e.StylusDevice.GetStylusPoints(inkTest)
'-------------------------------------------------------------
End If
End Sub
Private Sub inkTest_MouseLeftButtonUp(ByVal sender As Object, ByVal e As System.Windows.Input.MouseButtonEventArgs) Handles inkTest.MouseLeftButtonUp
Dim points As StylusPointCollection = e.StylusDevice.GetStylusPoints(inkTest)
currentStroke = New Stroke(points)
inkTest.ReleaseMouseCapture()
currentStroke = Nothing
' Grab the position to fill the List(Of StylusPointCollection)
pointList.Add(e.StylusDevice.GetStylusPoints(inkTest))
' Create the path in the canvas
If pointList.Count > 0 Then
DrawPath()
End If
'---------------------------------------------------------
End Sub
Schritt 3 - Positionsdaten auswerten und den Pfad erzeugen
Im Ereignisbehandler des MouseLeftButtonUp events wird als letztes die Routine "DrawPath" aufgerufen. Hier ist der Quellcode dieser Routine:
Private Sub DrawPath()
Dim pathToDraw As Path = New Path()
Dim pathGeometry As PathGeometry = New PathGeometry()
Dim pathFigure As PathFigure = New PathFigure()
Dim pathSegments As PathSegmentCollection = New PathSegmentCollection()
pathFigure.StartPoint = New Point(CreatePointFromStylusPointCollection(pointList.ElementAt(0)).X,
CreatePointFromStylusPointCollection(pointList.ElementAt(0)).Y)
For Each member As StylusPointCollection In pointList
pathSegments.Add(CreateLineSegment(CreatePointFromStylusPointCollection(member)))
Next
pathFigure.Segments = pathSegments
pathGeometry.Figures.Add(pathFigure)
With pathToDraw
.Data = pathGeometry
.Stroke = New SolidColorBrush(Colors.Orange)
.StrokeThickness = 10
.StrokeLineJoin = PenLineJoin.Round
.StrokeStartLineCap = PenLineCap.Round
.StrokeEndLineCap = PenLineCap.Round
End With
Canvas.SetZIndex(pathToDraw, 0)
cvElements.Children.Add(pathToDraw)
pointList.Clear()
End Sub
Mit der Variablen "pathToDraw" wird ein neues Path-Element erzeugt. Außerdem wird die Variable "pathGeometry" vom Typ "PathGeometry" erzeugt. Diese wird später der Data-Eigenschaft des Pfads zugewiesen. Die Variable "pathFigure" vom Typ "PathFigure" wird später der "PathFigureCollection" von "pathGeometry" zugewiesen. Und der Eigenschaft "Segments" von "pathFigure" wird die "PathSegmentCollection", erzeugt in der Variablen "pathSegments", zugewiesen.
Das eigentliche Arbeitstier der Routine ist der folgende Quellcode-Ausschnitt:
pathFigure.StartPoint =
New Point(CreatePointFromStylusPointCollection(pointList.ElementAt(0)).X,
CreatePointFromStylusPointCollection(pointList.ElementAt(0)).Y)
For Each member As StylusPointCollection In pointList
pathSegments.Add(CreateLineSegment(CreatePointFromStylusPointCollection(member)))
Next
Es wird zunächst der Startpunkt des Pfads ermittelt und zugewiesen. Dazu wird die Funktion "CreatePointFromStylusPointCollection" verwendet. Der Quellcode dieser Funktion sieht so aus:
Private Function CreatePointFromStylusPointCollection(ByVal value As StylusPointCollection) As Point
Return New Point With {.X = value.Item(0).X, .Y = value.Item(0).Y}
End Function
Die Funktion erhält als Parameter eine "StylusPointCollection" und gibt einen "Point" zurück. Um den Startpunkt des Pfads zu ermitteln, wird das erste Item aus der "List(Of SytlusPointCollection)", also von "pointList" genommen, und mit dem X-Wert sowie dem Y-Wert dieses Items der Rückgabewert der Funktion erzeugt.
In der "For Each"-Schleife wird dann mithilfe der Funktionen "CreateLineSegment" und "CreatePointFromStylusPointCollection" die einzelnen "LineSegments" erzeugt und der "PathSegmentsCollection" "pathSegments" hinzugefügt. Die Funktion "CreateLineSegment" sieht so aus:
Private Function CreateLineSegment(ByVal value As Point) As LineSegment
Return New LineSegment With {.Point = value}
End Function
Um ein LineSegment zu definieren, benötigt man nur einen Punkt. Der eigentliche Pfad, der erzeugt wird, bewegt sich vom Startpunkt zum nächsten Punkt und von dort aus zum nächsten Punkt, usw. Jeder Punkt eines LineSegment steht für einen Punkt des Pfades. Das letzte LineSegment stellt den Endpunkt des Pfades dar. Und so hangelt sich die "For Each"-Schleife durch die "pointList" und erzeugt, nachdem der Startpunkt gesetzt wurde, die einzelnen Punkte (LineSegment.Point), die den endgültigen Pfad definieren.
Um die perfekte visuelle Übereinstimmung mit den Strokes, die auf den InkPresenter gezeichnet werden, zu bewirken, ist die der folgende Quellcode-Ausschnitt wichtig:
With pathToDraw
.Data = pathGeometry
.Stroke = New SolidColorBrush(Colors.Orange)
'!!!!!!!!!!!!!!!!!!!!!
.StrokeThickness = 10
.StrokeLineJoin = PenLineJoin.Round
.StrokeStartLineCap = PenLineCap.Round
.StrokeEndLineCap = PenLineCap.Round
'!!!!!!!!!!!!!!!!!!!!!
End With
Im obigen Quellcode-Ausschnitt werden alle Ecken sowie der Startpunkt und der Endpunkt des Pfads abgerundet. Das ist wichtig, weil auch die Strokes rund sind. Außerdem wird die "StrokeThickness" des Pfads auf den Wert "10" gesetzt. Das entspricht der Höhe und der Breite der Strokes:
Private Sub inkTest_MouseLeftButtonDown(ByVal sender As Object, ByVal e As System.Windows.Input.MouseButtonEventArgs) Handles inkTest.MouseLeftButtonDown
inkTest.CaptureMouse()
Dim points As StylusPointCollection = e.StylusDevice.GetStylusPoints(inkTest)
currentStroke = New Stroke(points)
currentStroke.DrawingAttributes.Color = Colors.Blue
'!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
currentStroke.DrawingAttributes.Width = 10
currentStroke.DrawingAttributes.Height = 10
'!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
inkTest.Strokes.Add(currentStroke)
' Grab the position for the List(Of StylusPointCollection)
pointList.Add(e.StylusDevice.GetStylusPoints(inkTest))
'---------------------------------------------------------
End Sub
Abschließend wird der erzeugte Pfad dem Canvas hinzugefügt und mit pointList.Clear() die "pointList" geleert. Das ist wichtig, weil bei der nächsten Pfad-Zeichnung andernfalls auch die alten Werte noch verarbeitet werden würden.
Das war's!
Das Beispielprojekt, das hier heruntergeladen werden kann, enthält noch deutlich mehr Quellcode. Der dient aber nur dazu, die Anwendung einigermaßen ansehnlich zu gestalten. Die grundlegende Technik hat dieser Artikel aufgezeigt.
Viel Spass damit.
Keine Kommentare:
Kommentar veröffentlichen