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.
Die DrawingAttributes-Eigenschaft eines Strichs gestattet Ihnen, die Darstellung des Strichs anzugeben, z. B. die Größe, Farbe und Form. Es gibt jedoch möglicherweise Situationen, in denen Sie die Darstellung über die Möglichkeiten von DrawingAttributes hinausgehend anpassen möchten. Möglicherweise möchten Sie die Darstellung der Freihandzeichnung so anpassen, dass sie wie ein Airbrush- oder Ölfarbebild aussieht oder mit einem von vielen anderen Effekten gerendert wird. Windows Presentation Foundation (WPF) ermöglicht Ihnen, Freihandzeichnungen benutzerdefiniert zu rendern, indem Sie ein benutzerdefiniertes DynamicRenderer-Objekt und Stroke-Objekt implementieren.
Dieses Thema enthält folgende Unterabschnitte:
Architektur
Implementieren eines dynamischen Renderers
Implementing a Custom Stroke
Implementieren eines benutzerdefinierten InkCanvas
Schlussfolgerung
Architektur
Freihandrendering erfolgt zweimal: einmal, wenn ein Benutzer auf einer Freihandoberfläche schreibt, und ein zweites Mal, nachdem der Strich der freihandfähigen Oberfläche hinzugefügt wurde. Der DynamicRenderer rendert die Freihandeingabe, wenn der Benutzer den Tablettstift auf dem Digitizer verschiebt, und der Stroke rendert sich selbst, sobald er einem Element hinzugefügt wird.
Beim dynamischen Rendern von Freihandeingaben sind drei Klassen zu implementieren.
DynamicRenderer: Implementieren Sie eine Klasse, die sich von DynamicRenderer ableitet. Diese Klasse ist ein spezielles StylusPlugIn, das den Strich beim Zeichnen rendert. Der DynamicRenderer führt das Rendern in einem separaten Thread aus, sodass die Freihandoberfläche scheinbar Eingaben entgegennimmt, auch wenn der Thread der Anwendungsbenutzeroberfläche blockiert ist. Weitere Informationen zum Threadmodell finden Sie unter Das Threadmodell für Freihandeingaben. Um das dynamische Rendern eines Strichs anzupassen, überschreiben Sie die OnDraw-Methode.
Stroke: Implementieren Sie eine Klasse, die sich von Stroke ableitet. Diese Klasse ist für das statische Rendern der StylusPoint-Daten verantwortlich, nachdem sie in ein Stroke-Objekt konvertiert wurden. Überschreiben Sie die DrawCore-Methode, um sicherzustellen, dass das statische Rendern des Strichs und das dynamische Rendern konsistent sind.
InkCanvas: Implementieren Sie eine Klasse, die sich von InkCanvas ableitet. Weisen Sie den benutzerdefinierten DynamicRenderer der DynamicRenderer-Eigenschaft zu. Überschreiben Sie die OnStrokeCollected-Methode, und fügen Sie der Strokes-Eigenschaft einen benutzerdefinierten Strich hinzu. Dadurch wird sichergestellt, dass die Darstellung der Freihandeingaben konsistent ist.
Implementieren eines dynamischen Renderers
Zwar gehört die DynamicRenderer-Klasse zum Standardumfang von WPF, um spezialisierteres Rendern auszuführen, müssen Sie jedoch einen benutzerdefinierten dynamischen Renderer erstellen, der vom DynamicRenderer abgeleitet ist, und die OnDraw-Methode überschreiben.
Im folgenden Beispiel wird ein angepasster DynamicRenderer veranschaulicht, der Freihandeingaben mit einem Pinsel mit linearem Farbverlaufeffekt zeichnet.
Imports System
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
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 'OnStylusDown
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 'OnDraw
End Class 'CustomDynamicRenderer
using System;
using System.Windows.Media;
using System.Windows;
using System.Windows.Input.StylusPlugIns;
using System.Windows.Input;
using 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.
if (brush == null)
{
brush = new LinearGradientBrush(Colors.Red, Colors.Blue, 20d);
}
// Create a new Pen, if necessary.
if (pen == null)
{
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;
}
}
}
}
Implementieren von benutzerdefinierten Strichen
Implementieren Sie eine Klasse, die von Stroke abgeleitet ist. Diese Klasse ist für das Rendern von StylusPoint-Daten verantwortlich, nachdem sie in ein Stroke-Objekt konvertiert wurden. Überschreiben Sie die DrawCore-Klasse, um den eigentlichen Zeichnungsvorgang auszuführen.
Die Stroke-Klasse kann mithilfe der AddPropertyData-Methode auch benutzerdefinierte Daten speichern. Diese Daten werden beim dauerhaften Speichern zusammen mit den Strichdaten gespeichert.
Die Stroke-Klasse kann auch Trefferüberprüfungen ausführen. Sie können auch eigene Trefferüberprüfungsalgorithmen implementieren, indem Sie die HitTest-Methode in der aktuellen Klasse überschreiben.
Im folgenden C#-Code wird eine benutzerdefinierte Stroke-Klasse veranschaulicht, die StylusPoint-Daten als 3D-Strich rendert.
Imports System
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
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 'New
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 'DrawCore
End Class 'CustomStroke
using System;
using System.Windows.Media;
using System.Windows;
using System.Windows.Input.StylusPlugIns;
using System.Windows.Input;
using 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;
}
}
}
}
Implementieren eines benutzerdefinierten InkCanvas
Die einfachste Art, einen benutzerdefinierten DynamicRenderer und Strich zu verwenden, besteht im Implementieren einer Klasse, die von InkCanvas abgeleitetet ist und diese Klassen verwendet. InkCanvas verfügt über eine DynamicRenderer-Eigenschaft, die angibt, wie der Strich beim Zeichnen durch den Benutzer gerendert wird.
Gehen Sie wie folgt vor, um das Rendern von Strichen auf einem InkCanvas anzupassen:
Erstellen Sie eine von der InkCanvas-Klasse abgeleitete Klasse.
Weisen Sie den benutzerdefinierten DynamicRenderer der InkCanvas.DynamicRenderer-Eigenschaft zu.
Überschreiben Sie die OnStrokeCollected-Methode. Entfernen Sie in dieser Methode den ursprünglichen Strich, der dem 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 neuen InkCanvasStrokeCollectedEventArgs-Klasse auf, die den benutzerdefinierten Strich enthält.
Im folgenden C#-Code wird eine benutzerdefinierte InkCanvas-Klasse veranschaulicht, die einen angepassten DynamicRenderer verwendet und benutzerdefinierte Striche erfasst.
Public Class CustomRenderingInkCanvas
Inherits InkCanvas
Private customRenderer As New CustomDynamicRenderer()
Public Sub New()
' Use the custom dynamic renderer on the
' custom InkCanvas.
Me.DynamicRenderer = customRenderer
End Sub 'New
Protected Overrides Sub OnStrokeCollected(ByVal e As InkCanvasStrokeCollectedEventArgs)
' Remove the original stroke and add a custom stroke.
Me.Strokes.Remove(e.Stroke)
Dim customStroke As New CustomStroke(e.Stroke.StylusPoints)
Me.Strokes.Add(customStroke)
' Pass the custom stroke to base class' OnStrokeCollected method.
Dim args As New InkCanvasStrokeCollectedEventArgs(customStroke)
MyBase.OnStrokeCollected(args)
End Sub 'OnStrokeCollected
End Class 'CustomRenderingInkCanvas
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 mehrere DynamicRenderer-Objekte dem InkCanvas hinzufügen, indem Sie sie der StylusPlugIns-Eigenschaft hinzufügen.
Schlussfolgerung
Sie können die Darstellung von Freihandeingaben anpassen, indem Sie eigene DynamicRenderer-, Stroke- und InkCanvas-Klassen ableiten. Zusammen stellen diese Klassen sicher, dass die Darstellung des Strichs beim Zeichnen durch den Benutzer und nach dem Erfassen konsistent ist.