Niestandardowy atrament renderowania

DrawingAttributes Właściwość pociągnięcia umożliwia określenie wyglądu pociągnięcia, takiego jak jego rozmiar, kolor i kształt, ale może istnieć czas, w którym chcesz dostosować wygląd poza tym, co DrawingAttributes pozwala. Możesz dostosować wygląd atramentu, renderując w wyglądzie pędzla powietrza, farby olejnej i wielu innych efektów. Program Windows Presentation Foundation (WPF) umożliwia niestandardowe renderowanie pisma odręcznego przez zaimplementowanie niestandardowego DynamicRenderer obiektu i Stroke .

Ten temat zawiera następujące podsekcje:

Architektura

Renderowanie pisma oddyskowego odbywa się dwa razy; gdy użytkownik zapisuje atrament na powierzchni pisma odręcznego i ponownie po dodaniu pociągnięcia do powierzchni obsługującej pisma odręcznego. Funkcja DynamicRenderer renderuje atrament, gdy użytkownik przenosi pióro tabletu na digitizer, a Stroke sam renderuje się po dodaniu go do elementu.

Istnieją trzy klasy do zaimplementowania podczas dynamicznego renderowania pisma oddyskowego.

  1. DynamicRenderer: zaimplementuj klasę pochodzącą z klasy DynamicRenderer. Ta klasa jest wyspecjalizowana StylusPlugIn , która renderuje pociągnięcie podczas rysowania. Wykonuje DynamicRenderer renderowanie w osobnym wątku, więc powierzchnia pisma odręcznego wydaje się zbierać pisma odręcznego nawet wtedy, gdy wątek interfejsu użytkownika aplikacji jest zablokowany. Aby uzyskać więcej informacji na temat modelu wątkowania, zobacz Model wątkowania pisma odwlekającego. Aby dostosować dynamiczne renderowanie pociągnięcia, zastąpij metodę OnDraw .

  2. Pociągnięcie: zaimplementuj klasę pochodzącą z klasy Stroke. Ta klasa jest odpowiedzialna za statyczne renderowanie StylusPoint danych po jego przekonwertowaniu na Stroke obiekt. Zastąpij metodę DrawCore , aby upewnić się, że renderowanie statyczne pociągnięcia jest spójne z renderowaniem dynamicznym.

  3. InkCanvas: Zaimplementuj klasę pochodzącą z klasy InkCanvas. Przypisz właściwość dostosowaną DynamicRendererDynamicRenderer do właściwości . Zastąpij metodę OnStrokeCollected i dodaj niestandardowy pociągnięcie do Strokes właściwości . Dzięki temu wygląd pisma odwłokowego jest spójny.

Implementowanie dynamicznego modułu renderowania

DynamicRenderer Mimo że klasa jest standardową częścią WPF, aby wykonać bardziej wyspecjalizowane renderowanie, należy utworzyć dostosowany dynamiczny moduł renderujący pochodzący z DynamicRenderer metody i zastąpić metodę OnDraw .

W poniższym przykładzie pokazano dostosowany DynamicRenderer atrament, który rysuje atrament z efektem pędzla gradientowego liniowego.

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

Implementowanie niestandardowych pociągnięć

Zaimplementuj klasę pochodzącą z klasy Stroke. Ta klasa jest odpowiedzialna za renderowanie StylusPoint danych po przekonwertowaniu ich na Stroke obiekt. Zastąpij klasę, DrawCore aby wykonać rzeczywisty rysunek.

Klasa Stroke może również przechowywać dane niestandardowe przy użyciu AddPropertyData metody . Te dane są przechowywane przy użyciu danych pociągnięcia po utrwieniu.

Klasa Stroke może również przeprowadzać testowanie trafień. Możesz również zaimplementować własny algorytm testowania trafień, przesłaniając metodę HitTest w bieżącej klasie.

Poniższy kod w języku C# przedstawia klasę niestandardową Stroke , która renderuje StylusPoint dane jako pociągnięcie 3D.

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

Implementowanie niestandardowego modułu InkCanvas

Najprostszym sposobem użycia dostosowanego DynamicRenderer i pociągnięcia jest zaimplementowanie klasy, która pochodzi z InkCanvas i używa tych klas. Właściwość InkCanvas ma właściwość określającą DynamicRenderer sposób renderowania pociągnięcia podczas rysowania go przez użytkownika.

Aby wykonać niestandardowe pociągnięcia renderowania na elementy InkCanvas , wykonaj następujące czynności:

Poniższy kod w języku C# demonstruje klasę niestandardową InkCanvas , która używa niestandardowego DynamicRenderer pociągnięcia i zbiera niestandardowe pociągnięcia.

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);
    }
}

Obiekt InkCanvas może mieć więcej niż jeden DynamicRendererelement . Do obiektu można dodać wiele DynamicRenderer obiektów InkCanvas , dodając je do StylusPlugIns właściwości .

Podsumowanie

Wygląd pisma odkowego można dostosować, wyprowadzając własne DynamicRendererklasy , Strokei InkCanvas . Razem te klasy zapewniają, że wygląd pociągnięcia jest spójny, gdy użytkownik rysuje pociągnięcie i po jego zebraniu.

Zobacz też