Vlastní vykreslovací inkoust
Vlastnost DrawingAttributes tahu umožňuje určit vzhled tahu, například jeho velikost, barvu a tvar, ale někdy můžete chtít přizpůsobit vzhled nad rámec toho, co DrawingAttributes povolují. Vzhled rukopisu můžete přizpůsobit vykreslením ve vzhledu vzduchového štětce, olejové barvy a mnoha dalších efektů. Windows Presentation Foundation (WPF) umožňuje vlastní vykreslení rukopisu implementací vlastního DynamicRenderer a Stroke objektu.
Toto téma obsahuje následující pododdíly:
implementace dynamického rendereru
implementace vlastního InkCanvas
Architektura
Vykreslování inkoustu probíhá dvakrát; kdy uživatel píše inkoustem na inkoustovou plochu a znovu po přidání tahu na plochu s podporou inkoustu. DynamicRenderer vykreslí rukopis, když uživatel přesune pero tabletu na digitizátoru a Stroke se vykresluje po přidání do prvku.
Existují tři třídy, které je třeba implementovat při dynamickém vykreslování rukopisu.
DynamicRenderer: Implementujte třídu odvozenou z DynamicRenderer. Tato třída je specializovaná StylusPlugIn, která zobrazuje tah během kreslení. DynamicRenderer provádí vykreslování na samostatném vlákně, takže plocha pro kreslení vypadá, že shromažďuje rukopis i tehdy, když je blokované vlákno uživatelského rozhraní aplikace (UI). Další informace o modelu vláken naleznete v tématu Model vláken rukopisu. Pokud chcete přizpůsobit dynamické vykreslování tahu, přepište metodu OnDraw.
Stroke: Implementujte třídu, která je odvozena z Stroke. Tato třída zodpovídá za statické vykreslování dat StylusPoint po převodu na objekt Stroke. Přepište metodu DrawCore, aby bylo zajištěno, že statické vykreslování tahu je konzistentní s dynamickým vykreslováním.
InkCanvas: Implementujte třídu odvozenou z InkCanvas. Přiřaďte DynamicRenderer k vlastnosti DynamicRenderer. Přepište metodu OnStrokeCollected a přidejte vlastní tah k vlastnosti Strokes. Tím zajistíte, že vzhled rukopisu je konzistentní.
Implementace dynamického rendereru
Ačkoli třída DynamicRenderer je standardní součástí WPF, abyste mohli provádět specializovanější vykreslování, musíte vytvořit přizpůsobený dynamický renderer, který je odvozen od DynamicRenderer a přepsat metodu OnDraw.
Následující příklad ukazuje upravenou DynamicRenderer, která vytváří inkoustový efekt s použitím efektu lineárního gradientního štětce.
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
Implementace vlastních tahů
Implementujte třídu, která je odvozena z Stroke. Tato třída zodpovídá za vykreslení StylusPoint dat po převodu na objekt Stroke. Přepište třídu DrawCore, aby prováděla skutečné kreslení.
Třída Stroke může také ukládat vlastní data pomocí metody AddPropertyData. Tato data jsou uložena s daty tahů při zachování.
Třída Stroke může také provádět testování kolizí. Můžete také implementovat vlastní algoritmus testování hitů přepsáním metody HitTest v aktuální třídě.
Následující kód jazyka C# demonstruje vlastní třídu Stroke, která zobrazuje StylusPoint data jako 3D vykreslení.
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
Implementace vlastního InkCanvasu
Nejjednodušší způsob, jak použít vlastní DynamicRenderer a tah, je implementovat třídu, která je odvozena z InkCanvas a používá tyto třídy. InkCanvas má vlastnost DynamicRenderer, která určuje, jak se tah vykresluje při kreslení uživatelem.
Pro vykreslování vlastních tahů na InkCanvas postupujte takto:
Vytvořte třídu, která je odvozena z InkCanvas.
Přiřaďte DynamicRenderer k vlastnosti InkCanvas.DynamicRenderer.
Přepište metodu OnStrokeCollected. V této metodě odeberte původní tah, který byl přidán do InkCanvas. Pak vytvořte vlastní tah, přidejte ho do vlastnosti Strokes a zavolejte základní třídu s novým InkCanvasStrokeCollectedEventArgs, který obsahuje vlastní tah.
Následující kód v jazyce C# demonstruje vlastní třídu InkCanvas, která používá přizpůsobenou DynamicRenderer a shromažďuje vlastní tahy.
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);
}
}
InkCanvas může mít více DynamicRenderer. Do InkCanvas můžete přidat více DynamicRenderer objektů tak, že je přidáte do vlastnosti StylusPlugIns.
Závěr
Vzhled rukopisu můžete přizpůsobit odvozením vlastních tříd DynamicRenderer, Strokea InkCanvas. Tyto třídy společně zajišťují, že vzhled čáry je konzistentní, když uživatel kreslí čáru a po jejím dokončení.
Viz také
.NET Desktop feedback