Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Mit der DrawingAttributes-Eigenschaft eines Strichs können Sie das Erscheinungsbild eines Strichs angeben, z. B. Größe, Farbe und Form, aber es kann vorkommen, dass Sie die Darstellung über das hinaus anpassen möchten, was DrawingAttributes zulässt. Sie können die Darstellung von Freihandschrift beispielsweise so anpassen, dass diese als Airbrush- oder Ölgemälde oder mit verschiedenen anderen Effekten gerendert werden. Mit Windows Presentation Foundation (WPF) können Sie benutzerdefinierte Freihandschrift erstellen, indem Sie ein benutzerdefiniertes DynamicRenderer- und benutzerdefiniertes Stroke-Objekt implementieren.
Dieses Thema enthält die folgenden Unterabschnitte:
Architektur
Freihandschrift wird zweimal gerendert: einmal wenn Benutzende auf eine Oberfläche für Freihandschrift schreiben und noch einmal, nachdem der Strich der freihandfähigen Oberfläche hinzugefügt wurde. DynamicRenderer rendert die Freihandschrift, wenn Benutzende den Tablettstift auf dem Digitalisierungsgerät bewegen, und Stroke rendert sich selbst, sobald es einem Element hinzugefügt wird.
Es gibt drei Klassen, die beim dynamischen Rendern von Tinte implementiert werden sollen.
DynamicRenderer: Implementieren Sie eine Klasse, die von DynamicRendererabgeleitet wird. Diese Klasse ist ein spezialisiertes StylusPlugIn-Element, das den Strich direkt beim Zeichnen rendert. DynamicRenderer rendert in einem separaten Thread, wodurch die Freihandeingaben auf der Freihandoberfläche scheinbar erfasst werden, obwohl der Benutzeroberflächenthread der Anwendung blockiert ist. Weitere Informationen zum Threadingmodell finden Sie unter Das Threadingmodell für Freihandeschriften. Um das dynamische Rendern eines Strichs anzupassen, überschreiben Sie die OnDraw-Methode.
Pinselstrich: Implementieren Sie eine Klasse, die von Stroke abgeleitet ist. Diese Klasse ist für das statische Rendern der StylusPoint Daten verantwortlich, nachdem sie in ein Stroke-Objekt konvertiert wurde. Überschreiben Sie die DrawCore-Methode, um sicherzustellen, dass das statische Rendern des Strichs mit dem dynamischen Rendern konsistent ist.
InkCanvas: Implementieren Sie eine Klasse, die von InkCanvasabgeleitet ist. Weisen Sie das angepasste DynamicRenderer-Element der DynamicRenderer-Eigenschaft zu. Überschreiben Sie die OnStrokeCollected-Methode, und fügen Sie der Strokes-Eigenschaft einen benutzerdefinierten Strich hinzu. Dadurch wird sichergestellt, dass das Erscheinungsbild der Tinte konsistent ist.
Implementieren eines dynamischen Renderers
Obwohl die DynamicRenderer-Klasse ein Standardbestandteil von WPF ist, müssen Sie, um ein spezielleres Rendering durchzuführen, einen angepassten dynamischen Renderer erstellen, der vom DynamicRenderer-Element abgeleitet ist und die OnDraw-Methode außer Kraft setzt.
Das folgende Beispiel veranschaulicht einen benutzerdefinierten DynamicRenderer, der Freihandeingaben mit einem linearen Farbverlaufspinsel zeichnet.
using System;
using System.Windows.Media;
using System.Windows;
using System.Windows.Input.StylusPlugIns;
using System.Windows.Input;
using System.Windows.Ink;
Imports System.Windows.Media
Imports System.Windows
Imports System.Windows.Input.StylusPlugIns
Imports System.Windows.Input
Imports System.Windows.Ink
// A StylusPlugin that renders ink with a linear gradient brush effect.
class CustomDynamicRenderer : DynamicRenderer
{
[ThreadStatic]
static private Brush brush = null;
[ThreadStatic]
static private Pen pen = null;
private Point prevPoint;
protected override void OnStylusDown(RawStylusInput rawStylusInput)
{
// Allocate memory to store the previous point to draw from.
prevPoint = new Point(double.NegativeInfinity, double.NegativeInfinity);
base.OnStylusDown(rawStylusInput);
}
protected override void OnDraw(DrawingContext drawingContext,
StylusPointCollection stylusPoints,
Geometry geometry, Brush fillBrush)
{
// Create a new Brush, if necessary.
brush ??= new LinearGradientBrush(Colors.Red, Colors.Blue, 20d);
// Create a new Pen, if necessary.
pen ??= new Pen(brush, 2d);
// Draw linear gradient ellipses between
// all the StylusPoints that have come in.
for (int i = 0; i < stylusPoints.Count; i++)
{
Point pt = (Point)stylusPoints[i];
Vector v = Point.Subtract(prevPoint, pt);
// Only draw if we are at least 4 units away
// from the end of the last ellipse. Otherwise,
// we're just redrawing and wasting cycles.
if (v.Length > 4)
{
// Set the thickness of the stroke based
// on how hard the user pressed.
double radius = stylusPoints[i].PressureFactor * 10d;
drawingContext.DrawEllipse(brush, pen, pt, radius, radius);
prevPoint = pt;
}
}
}
}
' A StylusPlugin that renders ink with a linear gradient brush effect.
Class CustomDynamicRenderer
Inherits DynamicRenderer
<ThreadStatic()> _
Private Shared brush As Brush = Nothing
<ThreadStatic()> _
Private Shared pen As Pen = Nothing
Private prevPoint As Point
Protected Overrides Sub OnStylusDown(ByVal rawStylusInput As RawStylusInput)
' Allocate memory to store the previous point to draw from.
prevPoint = New Point(Double.NegativeInfinity, Double.NegativeInfinity)
MyBase.OnStylusDown(rawStylusInput)
End Sub
Protected Overrides Sub OnDraw(ByVal drawingContext As DrawingContext, _
ByVal stylusPoints As StylusPointCollection, _
ByVal geometry As Geometry, _
ByVal fillBrush As Brush)
' Create a new Brush, if necessary.
If brush Is Nothing Then
brush = New LinearGradientBrush(Colors.Red, Colors.Blue, 20.0)
End If
' Create a new Pen, if necessary.
If pen Is Nothing Then
pen = New Pen(brush, 2.0)
End If
' Draw linear gradient ellipses between
' all the StylusPoints that have come in.
Dim i As Integer
For i = 0 To stylusPoints.Count - 1
Dim pt As Point = CType(stylusPoints(i), Point)
Dim v As Vector = Point.Subtract(prevPoint, pt)
' Only draw if we are at least 4 units away
' from the end of the last ellipse. Otherwise,
' we're just redrawing and wasting cycles.
If v.Length > 4 Then
' Set the thickness of the stroke based
' on how hard the user pressed.
Dim radius As Double = stylusPoints(i).PressureFactor * 10.0
drawingContext.DrawEllipse(brush, pen, pt, radius, radius)
prevPoint = pt
End If
Next i
End Sub
End Class
Implementieren von benutzerdefinierten Strichen
Implementieren Sie eine Klasse, die von Strokeabgeleitet wird. Diese Klasse ist verantwortlich für das Rendern der StylusPoint-Daten, nachdem sie in ein Stroke-Objekt konvertiert wurden. Überschreiben Sie die DrawCore-Klasse für das eigentliche Zeichnen.
Ihre Stroke-Klasse kann auch benutzerdefinierte Daten speichern, indem Sie die AddPropertyData-Methode verwenden. Diese Daten werden mit den Strichdaten gespeichert, wenn diese beibehalten werden.
Die Stroke-Klasse kann auch Treffertests durchführen. Durch Überschreiben der HitTest-Methode in der aktuellen Klasse können Sie auch einen eigenen Treffertestalgorithmus implementieren.
Der folgende C#-Code veranschaulicht eine benutzerdefinierte Stroke Klasse, die StylusPoint Daten als 3D-Strich rendert.
using System;
using System.Windows.Media;
using System.Windows;
using System.Windows.Input.StylusPlugIns;
using System.Windows.Input;
using System.Windows.Ink;
Imports System.Windows.Media
Imports System.Windows
Imports System.Windows.Input.StylusPlugIns
Imports System.Windows.Input
Imports System.Windows.Ink
// A class for rendering custom strokes
class CustomStroke : Stroke
{
Brush brush;
Pen pen;
public CustomStroke(StylusPointCollection stylusPoints)
: base(stylusPoints)
{
// Create the Brush and Pen used for drawing.
brush = new LinearGradientBrush(Colors.Red, Colors.Blue, 20d);
pen = new Pen(brush, 2d);
}
protected override void DrawCore(DrawingContext drawingContext,
DrawingAttributes drawingAttributes)
{
// Allocate memory to store the previous point to draw from.
Point prevPoint = new Point(double.NegativeInfinity,
double.NegativeInfinity);
// Draw linear gradient ellipses between
// all the StylusPoints in the Stroke.
for (int i = 0; i < this.StylusPoints.Count; i++)
{
Point pt = (Point)this.StylusPoints[i];
Vector v = Point.Subtract(prevPoint, pt);
// Only draw if we are at least 4 units away
// from the end of the last ellipse. Otherwise,
// we're just redrawing and wasting cycles.
if (v.Length > 4)
{
// Set the thickness of the stroke
// based on how hard the user pressed.
double radius = this.StylusPoints[i].PressureFactor * 10d;
drawingContext.DrawEllipse(brush, pen, pt, radius, radius);
prevPoint = pt;
}
}
}
}
' A class for rendering custom strokes
Class CustomStroke
Inherits Stroke
Private brush As Brush
Private pen As Pen
Public Sub New(ByVal stylusPoints As StylusPointCollection)
MyBase.New(stylusPoints)
' Create the Brush and Pen used for drawing.
brush = New LinearGradientBrush(Colors.Red, Colors.Blue, 20.0)
pen = New Pen(brush, 2.0)
End Sub
Protected Overrides Sub DrawCore(ByVal drawingContext As DrawingContext, _
ByVal drawingAttributes As DrawingAttributes)
' Allocate memory to store the previous point to draw from.
Dim prevPoint As New Point(Double.NegativeInfinity, Double.NegativeInfinity)
' Draw linear gradient ellipses between
' all the StylusPoints in the Stroke.
Dim i As Integer
For i = 0 To Me.StylusPoints.Count - 1
Dim pt As Point = CType(Me.StylusPoints(i), Point)
Dim v As Vector = Point.Subtract(prevPoint, pt)
' Only draw if we are at least 4 units away
' from the end of the last ellipse. Otherwise,
' we're just redrawing and wasting cycles.
If v.Length > 4 Then
' Set the thickness of the stroke
' based on how hard the user pressed.
Dim radius As Double = Me.StylusPoints(i).PressureFactor * 10.0
drawingContext.DrawEllipse(brush, pen, pt, radius, radius)
prevPoint = pt
End If
Next i
End Sub
End Class
Implementieren einer benutzerdefinierten InkCanvas-Klasse
Der einfachste Weg, Ihre benutzerdefinierten DynamicRenderer und Striche zu verwenden, besteht darin, eine Klasse zu implementieren, die von diesen InkCanvas ableitet und diese Klassen verwendet. Die InkCanvas hat eine DynamicRenderer-Eigenschaft, die angibt, wie der Strich gerendert wird, wenn der Benutzer ihn zeichnet.
Um Striche auf einem InkCanvas zu rendern, gehen Sie wie folgt vor:
Erstellen einer Klasse, die von InkCanvas abgeleitet ist.
Weisen Sie der DynamicRenderer-Eigenschaft Ihre angepassten InkCanvas.DynamicRenderer-Eigenschaften zu.
Überschreiben Sie die OnStrokeCollected-Methode. Entfernen Sie in dieser Methode den Originalstrich, der InkCanvas hinzugefügt wurde. Erstellen Sie dann einen benutzerdefinierten Strich, fügen Sie ihn der Strokes-Eigenschaft hinzu und rufen Sie die Basisklasse mit einer neuenInkCanvasStrokeCollectedEventArgs auf, das den benutzerdefinierten Strich enthält.
Der folgende C#-Code veranschaulicht eine benutzerdefinierte InkCanvas-Klasse, die eine angepasste DynamicRenderer verwendet und angepaste Pinselstriche sammelt.
public class CustomRenderingInkCanvas : InkCanvas
{
CustomDynamicRenderer customRenderer = new CustomDynamicRenderer();
public CustomRenderingInkCanvas() : base()
{
// Use the custom dynamic renderer on the
// custom InkCanvas.
this.DynamicRenderer = customRenderer;
}
protected override void OnStrokeCollected(InkCanvasStrokeCollectedEventArgs e)
{
// Remove the original stroke and add a custom stroke.
this.Strokes.Remove(e.Stroke);
CustomStroke customStroke = new CustomStroke(e.Stroke.StylusPoints);
this.Strokes.Add(customStroke);
// Pass the custom stroke to base class' OnStrokeCollected method.
InkCanvasStrokeCollectedEventArgs args =
new InkCanvasStrokeCollectedEventArgs(customStroke);
base.OnStrokeCollected(args);
}
}
Ein InkCanvas kann über mehrere DynamicRenderer verfügen. Sie können DynamicRenderer mehrere InkCanvas-Objekte hinzufügen, indem Sie sie der StylusPlugIns-Eigenschaft hinzufügen.
Schlussfolgerung
Sie können das Erscheinungsbild der Tinte anpassen, indem Sie Ihre eigenen DynamicRenderer-, Stroke- und InkCanvas-Klassen erstellen. Zusammen stellen diese Klassen sicher, dass die Darstellung des Strichs während des Zeichnens mit der Darstellung nach seiner Erfassung übereinstimmt.
Siehe auch
.NET Desktop feedback