Erstellen eines Freihandeingabesteuerelements

Sie können ein benutzerdefiniertes Steuerelement erstellen, das Freihandeingaben dynamisch und statisch rendert. Das heißt, dass die Freihandeingabe gerendert wird, wenn der Benutzer einen Strich zeichnet, sodass die Freihandeingabe aus dem Tabletstift zu „fließen“ scheint, und dass die Freihandeingabe angezeigt wird, nachdem sie dem Steuerelement hinzugefügt wurde, entweder über den Tabletstift, durch Einfügen aus der Zwischenablage oder durch Laden aus einer Datei. Zum dynamischen Rendern von Freihandeingaben muss das Steuerelement einen DynamicRenderer verwenden. Um Freihandeingaben statisch zu rendern, müssen Sie die Eingabestift-Ereignismethoden (OnStylusDown, OnStylusMoveund OnStylusUp) außer Kraft setzen, um StylusPoint-Daten zu erfassen, Striche zu erstellen und sie einem InkPresenter hinzufügen (wodurch die Freihandeingabe für das Steuerelement gerendert wird).

Dieses Thema enthält folgende Unterabschnitte:

Vorgehensweise: Erfassen von Eingabestift-Punktdaten und Erstellen von Freihandeingaben

Gehen Sie wie folgt vor, um ein Steuerelement zu erstellen, das Freihandeingaben erfasst und verwaltet:

  1. Leiten Sie eine Klasse von Control oder einer der Klassen ab, die von Control abgeleitet werden, z. B. Label.

    using System;
    using System.Windows.Ink;
    using System.Windows.Input;
    using System.Windows.Input.StylusPlugIns;
    using System.Windows.Controls;
    using System.Windows;
    
    class InkControl : Label
    {
    
    }
    
  2. Fügen Sie der Klasse einen InkPresenter hinzu, und legen Sie die Content-Eigenschaft auf den neuen InkPresenter fest.

    InkPresenter ip;
    
    public InkControl()
    {
        // Add an InkPresenter for drawing.
        ip = new InkPresenter();
        this.Content = ip;
    }
    
  3. Fügen Sie das RootVisual des DynamicRenderer an den InkPresenter durch Aufrufen der AttachVisuals-Methode an, und fügen Sie den DynamicRenderer der StylusPlugIns-Sammlung hinzu. Dadurch kann der InkPresenter die Freihandeingabe anzeigen, wenn die Eingabestift-Punktdaten von Ihrem Steuerelement erfasst werden.

    public InkControl()
    {
    
        // Add a dynamic renderer that
        // draws ink as it "flows" from the stylus.
        dr = new DynamicRenderer();
        ip.AttachVisuals(dr.RootVisual, dr.DrawingAttributes);
        this.StylusPlugIns.Add(dr);
    }
    
  4. Überschreiben Sie die OnStylusDown -Methode. Erfassen Sie in dieser Methode den Eingabestift mit einem Aufruf von Capture. Durch die Erfassung des Eingabestifts wird ihr Steuerelement weiterhin auch dann StylusMove- und StylusUp-Ereignisse empfangen, wenn der Eingabestift die Grenzen des Steuerelements verlässt. Dies ist nicht zwingend erforderlich, aber für eine gute Benutzererfahrung fast immer erwünscht. Erstellen Sie eine neue StylusPointCollection, um StylusPoint-Daten zu erfassen. Fügen Sie schließlich StylusPointCollection die erste StylusPoint-Datenmenge hinzu.

    protected override void OnStylusDown(StylusDownEventArgs e)
    {
        // Capture the stylus so all stylus input is routed to this control.
        Stylus.Capture(this);
    
        // Allocate memory for the StylusPointsCollection and
        // add the StylusPoints that have come in so far.
        stylusPoints = new StylusPointCollection();
        StylusPointCollection eventPoints =
            e.GetStylusPoints(this, stylusPoints.Description);
    
        stylusPoints.Add(eventPoints);
    }
    
  5. Überschreiben Sie die OnStylusMove-Methode, und fügen Sie die StylusPoint-Daten dem zuvor erstellten StylusPointCollection-Objekt hinzu.

    protected override void OnStylusMove(StylusEventArgs e)
    {
        if (stylusPoints == null)
        {
            return;
        }
    
        // Add the StylusPoints that have come in since the
        // last call to OnStylusMove.
        StylusPointCollection newStylusPoints =
            e.GetStylusPoints(this, stylusPoints.Description);
        stylusPoints.Add(newStylusPoints);
    }
    
  6. Überschreiben Sie die OnStylusUp-Methode, und erstellen Sie eine neue Stroke-Methode mit den StylusPointCollection-Daten. Fügen Sie das neue Stroke-Element, das Sie erstellt haben, der Strokes-Sammlung des InkPresenter hinzu, und geben Sie die Eingabestifterfassung frei.

    protected override void OnStylusUp(StylusEventArgs e)
    {
        if (stylusPoints == null)
        {
            return;
        }
    
        // Add the StylusPoints that have come in since the
        // last call to OnStylusMove.
        StylusPointCollection newStylusPoints =
            e.GetStylusPoints(this, stylusPoints.Description);
        stylusPoints.Add(newStylusPoints);
    
        // Create a new stroke from all the StylusPoints since OnStylusDown.
        Stroke stroke = new Stroke(stylusPoints);
    
        // Add the new stroke to the Strokes collection of the InkPresenter.
        ip.Strokes.Add(stroke);
    
        // Clear the StylusPointsCollection.
        stylusPoints = null;
    
        // Release stylus capture.
        Stylus.Capture(null);
    }
    

Vorgehensweise: Aktivieren des Steuerelements zum Annehmen von Eingaben von der Maus

Wenn Sie der Anwendung das vorherige Steuerelement hinzufügen, sie ausführen und die Maus als Eingabegerät verwenden, werden Sie feststellen, dass die Freihandeingabe nicht persistent gespeichert wird. Gehen Sie wie folgt vor, um die Freihandeingabe beizubehalten, wenn die Maus als Eingabegerät verwendet wird:

  1. Überschreiben Sie das OnMouseLeftButtonDown-Ereignis, und erstellen Sie eine neue StylusPointCollection. Rufen Sie die Position der Maus ab, wenn das Ereignis aufgetreten ist, und erstellen Sie unter Verwendung der Punktdaten einen StylusPoint. Fügen Sie dann der StylusPointCollection das StylusPoint-Element hinzu.

    protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
    {
    
        base.OnMouseLeftButtonDown(e);
    
        // If a stylus generated this event, return.
        if (e.StylusDevice != null)
        {
            return;
        }
    
        // Start collecting the points.
        stylusPoints = new StylusPointCollection();
        Point pt = e.GetPosition(this);
        stylusPoints.Add(new StylusPoint(pt.X, pt.Y));
    }
    
  2. Überschreiben Sie die OnMouseMove -Methode. Rufen Sie die Position der Maus ab, wenn das Ereignis aufgetreten ist, und erstellen Sie unter Verwendung der Punktdaten ein StylusPoint-Element. Fügen Sie das StylusPoint-Element dem zuvor erstellten StylusPointCollection-Objekt hinzu.

    protected override void OnMouseMove(MouseEventArgs e)
    {
    
        base.OnMouseMove(e);
    
        // If a stylus generated this event, return.
        if (e.StylusDevice != null)
        {
            return;
        }
    
        // Don't collect points unless the left mouse button
        // is down.
        if (e.LeftButton == MouseButtonState.Released ||
            stylusPoints == null)
        {
            return;
        }
    
        Point pt = e.GetPosition(this);
        stylusPoints.Add(new StylusPoint(pt.X, pt.Y));
    }
    
  3. Überschreiben Sie die OnMouseLeftButtonUp -Methode. Erstellen Sie ein neues Stroke-Element mit den StylusPointCollection-Daten, und fügen Sie das neu erstellte Stroke-Element der Strokes-Sammlung von InkPresenter hinzu.

    protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
    {
    
        base.OnMouseLeftButtonUp(e);
    
        // If a stylus generated this event, return.
        if (e.StylusDevice != null)
        {
            return;
        }
    
        if (stylusPoints == null)
        {
            return;
        }
    
        Point pt = e.GetPosition(this);
        stylusPoints.Add(new StylusPoint(pt.X, pt.Y));
    
        // Create a stroke and add it to the InkPresenter.
        Stroke stroke = new Stroke(stylusPoints);
        stroke.DrawingAttributes = dr.DrawingAttributes;
        ip.Strokes.Add(stroke);
    
        stylusPoints = null;
    }
    

Zusammenführung

Das folgende Beispiel zeigt ein benutzerdefiniertes Steuerelement, das Freihandeingaben erfasst, wenn der Benutzer die Maus oder den Stift verwendet.

using System;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Input.StylusPlugIns;
using System.Windows.Controls;
using System.Windows;
// A control for managing ink input
class InkControl : Label
{
    InkPresenter ip;
    DynamicRenderer dr;

    // The StylusPointsCollection that gathers points
    // before Stroke from is created.
    StylusPointCollection stylusPoints = null;

    public InkControl()
    {
        // Add an InkPresenter for drawing.
        ip = new InkPresenter();
        this.Content = ip;

        // Add a dynamic renderer that
        // draws ink as it "flows" from the stylus.
        dr = new DynamicRenderer();
        ip.AttachVisuals(dr.RootVisual, dr.DrawingAttributes);
        this.StylusPlugIns.Add(dr);
    }

    static InkControl()
    {
        // Allow ink to be drawn only within the bounds of the control.
        Type owner = typeof(InkControl);
        ClipToBoundsProperty.OverrideMetadata(owner,
            new FrameworkPropertyMetadata(true));
    }

    protected override void OnStylusDown(StylusDownEventArgs e)
    {
        // Capture the stylus so all stylus input is routed to this control.
        Stylus.Capture(this);

        // Allocate memory for the StylusPointsCollection and
        // add the StylusPoints that have come in so far.
        stylusPoints = new StylusPointCollection();
        StylusPointCollection eventPoints =
            e.GetStylusPoints(this, stylusPoints.Description);

        stylusPoints.Add(eventPoints);
    }

    protected override void OnStylusMove(StylusEventArgs e)
    {
        if (stylusPoints == null)
        {
            return;
        }

        // Add the StylusPoints that have come in since the
        // last call to OnStylusMove.
        StylusPointCollection newStylusPoints =
            e.GetStylusPoints(this, stylusPoints.Description);
        stylusPoints.Add(newStylusPoints);
    }

    protected override void OnStylusUp(StylusEventArgs e)
    {
        if (stylusPoints == null)
        {
            return;
        }

        // Add the StylusPoints that have come in since the
        // last call to OnStylusMove.
        StylusPointCollection newStylusPoints =
            e.GetStylusPoints(this, stylusPoints.Description);
        stylusPoints.Add(newStylusPoints);

        // Create a new stroke from all the StylusPoints since OnStylusDown.
        Stroke stroke = new Stroke(stylusPoints);

        // Add the new stroke to the Strokes collection of the InkPresenter.
        ip.Strokes.Add(stroke);

        // Clear the StylusPointsCollection.
        stylusPoints = null;

        // Release stylus capture.
        Stylus.Capture(null);
    }

    protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
    {

        base.OnMouseLeftButtonDown(e);

        // If a stylus generated this event, return.
        if (e.StylusDevice != null)
        {
            return;
        }

        // Start collecting the points.
        stylusPoints = new StylusPointCollection();
        Point pt = e.GetPosition(this);
        stylusPoints.Add(new StylusPoint(pt.X, pt.Y));
    }

    protected override void OnMouseMove(MouseEventArgs e)
    {

        base.OnMouseMove(e);

        // If a stylus generated this event, return.
        if (e.StylusDevice != null)
        {
            return;
        }

        // Don't collect points unless the left mouse button
        // is down.
        if (e.LeftButton == MouseButtonState.Released ||
            stylusPoints == null)
        {
            return;
        }

        Point pt = e.GetPosition(this);
        stylusPoints.Add(new StylusPoint(pt.X, pt.Y));
    }

    protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
    {

        base.OnMouseLeftButtonUp(e);

        // If a stylus generated this event, return.
        if (e.StylusDevice != null)
        {
            return;
        }

        if (stylusPoints == null)
        {
            return;
        }

        Point pt = e.GetPosition(this);
        stylusPoints.Add(new StylusPoint(pt.X, pt.Y));

        // Create a stroke and add it to the InkPresenter.
        Stroke stroke = new Stroke(stylusPoints);
        stroke.DrawingAttributes = dr.DrawingAttributes;
        ip.Strokes.Add(stroke);

        stylusPoints = null;
    }
}

Verwenden zusätzlicher Plug-Ins und DynamicRenderers

Wie inkCanvas kann Ihr benutzerdefiniertes Steuerelement benutzerdefinierte StylusPlugIn-Elemente und zusätzliche DynamicRenderer-Objekte verwenden. Fügen Sie diese der StylusPlugIns-Sammlung hinzu. Die Reihenfolge der StylusPlugIn-Objekte in StylusPlugInCollection wirkt sich auf das Aussehen der Freihandeingabe aus, wenn sie gerendert wird. Angenommen, Sie verwenden einen DynamicRenderer namens dynamicRenderer und ein benutzerdefiniertes StylusPlugIn namens translatePlugin, das die Freihandeingabe vom Tabletstift umsetzt. Wenn translatePlugin das erste StylusPlugIn in StylusPlugInCollection und dynamicRenderer das zweite ist, wird die Freihandeingabe, die „fließt“, umgesetzt, wenn der Benutzer den Stift bewegt. Wenn dynamicRenderer das erste und translatePlugin das zweite Element ist, wird die Freihandeingabe erst umgesetzt, wenn der Benutzer den Stift anhebt.

Zusammenfassung

Sie können ein Steuerelement erstellen, das Tinte erfasst und rendert, indem die Stiftereignismethoden überschrieben werden. Indem Sie Ihr eigenes Steuerelement erstellen, Ihre eigenen StylusPlugIn-Klassen ableiten und sie in StylusPlugInCollection einfügen, können Sie praktisch jedes erdenkliche Verhalten mit digitalen Freihandeingaben implementieren. Sie haben Zugriff auf die StylusPoint-Daten, während sie generiert werden, wodurch Sie die Möglichkeit erhalten, die Stylus-Eingaben anzupassen und sie auf dem Bildschirm so darzustellen, wie es für Ihre Anwendung angemessen ist. Da Sie einen solchen niederschwelligen Zugriff auf die StylusPoint-Daten besitzen, können Sie die Freihandeingabensammlung implementieren und sie mit optimaler Leistung für Ihre Anwendung rendern.

Weitere Informationen