Sdílet prostřednictvím


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:

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.

  1. 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.

  2. 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.

  3. 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:

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é