Creación de un control de entrada manuscrita

Actualización: noviembre 2007

Puede crear un control personalizado que represente la entrada manuscrita de forma dinámica y estática. Es decir, es posible representar la entrada manuscrita mientras el usuario dibuja el trazo, con lo que parece "fluir" del lápiz de Tablet PC, y mostrar la entrada manuscrita una vez agregada al control, bien a través del lápiz de Tablet PC, pegada desde el Portapapeles o cargada desde un archivo. Para representar dinámicamente la entrada manuscrita, el control debe usar DynamicRenderer. Para representar estáticamente la entrada manuscrita, debe invalidar los métodos de evento de lápiz (OnStylusDown, OnStylusMove y OnStylusUp) para recopilar los datos de StylusPoint, crear los trazos y agregarlos a un objeto InkPresenter (que representa la entrada manuscrita en el control).

Este tema contiene las siguientes subsecciones:

  • Cómo: Recopilar datos de los puntos de lápiz y crear trazos de entrada manuscrita

  • Cómo: Habilitar el control para que acepte la entrada del mouse

  • Resultado final

  • Uso de complementos adicionales y representadores dinámicos

  • Conclusión

Cómo: Recopilar datos de los puntos de lápiz y crear trazos de entrada manuscrita

Para crear un control que recopile y administre los trazos de entrada manuscrita, realice estas acciones:

  1. Derive una clase de Control o una de las clases derivadas de Control, como Label.

    Imports System
    Imports System.Collections.Generic
    Imports System.Text
    Imports System.Windows.Ink
    Imports System.Windows.Input
    Imports System.Windows.Input.StylusPlugIns
    Imports System.Windows.Controls
    Imports System.Windows
    
    
    ...
    
    
    Class InkControl
        Inherits Label
    
    
    ...
    
    
    End Class 'StylusControl
    
    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. Agregue un objeto InkPresenter a la clase y establezca la propiedad Content en el nuevo objeto InkPresenter.

    Private ip As InkPresenter
    
    Public Sub New()
        ' Add an InkPresenter for drawing.
        ip = New InkPresenter()
        Me.Content = ip
    
    End Sub
    
    InkPresenter ip;
    
    public InkControl()
    {
        // Add an InkPresenter for drawing.
        ip = new InkPresenter();
        this.Content = ip;
    }
    
  3. Asocie la propiedad RootVisual de DynamicRenderer al objeto InkPresenter mediante una llamada al método AttachVisuals y agregue DynamicRenderer a la colección StylusPlugIns. Esto permite a InkPresenter mostrar la entrada manuscrita a medida que el control recopila los datos de los puntos de lápiz.

    Public Sub New()
    
    
    ...
    
    
        ' Add a dynamic renderer that 
        ' draws ink as it "flows" from the stylus.
        dr = New DynamicRenderer()
        ip.AttachVisuals(dr.RootVisual, dr.DrawingAttributes)
        Me.StylusPlugIns.Add(dr)
    
    End Sub
    
    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. Invalide el método OnStylusDown. En este método, capture el lápiz con una llamada a Capture. Al capturar el lápiz, su control seguirá recibiendo eventos StylusMove y StylusUp aun cuando el lápiz abandone los límites del control. Esto no es estrictamente obligatorio, pero es deseable en la mayoría de los casos, para mejorar la experiencia del usuario. Cree una nueva StylusPointCollection para recopilar los datos de StylusPoint. Finalmente, agregue el conjunto inicial de datos de StylusPoint a StylusPointCollection.

    Protected Overrides Sub OnStylusDown(ByVal e As StylusDownEventArgs)
        ' Capture the stylus so all stylus input is routed to this control.
        Stylus.Capture(Me)
    
        ' Allocate memory for the StylusPointsCollection and
        ' add the StylusPoints that have come in so far.
        stylusPoints = New StylusPointCollection()
        Dim eventPoints As StylusPointCollection = e.GetStylusPoints(Me, stylusPoints.Description)
    
        stylusPoints.Add(eventPoints)
    
    End Sub 'OnStylusDown
    
    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. Invalide el método OnStylusMove y agregue los datos de StylusPoint al objeto StylusPointCollection que creó anteriormente.

    Protected Overrides Sub OnStylusMove(ByVal e As StylusEventArgs)
    
        If stylusPoints Is Nothing Then
            Return
        End If
    
        ' Add the StylusPoints that have come in since the 
        ' last call to OnStylusMove.
        Dim newStylusPoints As StylusPointCollection = e.GetStylusPoints(Me, stylusPoints.Description)
        stylusPoints.Add(newStylusPoints)
    
    End Sub 'OnStylusMove
    
    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. Invalide el método OnStylusUp y cree un nuevo objeto Stroke con los datos de StylusPointCollection. Agregue el objeto Stroke que ha creado a la colección Strokes de InkPresenter y libere la captura del lápiz.

    Protected Overrides Sub OnStylusUp(ByVal e As StylusEventArgs)
        ' Allocate memory for the StylusPointsCollection, if necessary.
        If stylusPoints Is Nothing Then
            Return
        End If
    
        ' Add the StylusPoints that have come in since the 
        ' last call to OnStylusMove.
        Dim newStylusPoints As StylusPointCollection = e.GetStylusPoints(Me, stylusPoints.Description)
        stylusPoints.Add(newStylusPoints)
    
        ' Create a new stroke from all the StylusPoints since OnStylusDown.
        Dim stroke As New Stroke(stylusPoints)
    
        ' Add the new stroke to the Strokes collection of the InkPresenter.
        ip.Strokes.Add(stroke)
    
        ' Clear the StylusPointsCollection.
        stylusPoints = Nothing
    
        ' Release stylus capture.
        Stylus.Capture(Nothing)
    
    End Sub 'OnStylusUp
    
    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);
    }
    

Cómo: Permitir que el control acepte la entrada del mouse

Si agrega el control anterior a su aplicación, lo ejecuta y usa el mouse como dispositivo de entrada, observará que no se conservan los trazos. Para conservar los trazos cuando se use el mouse como dispositivo de entrada realice los pasos siguientes:

  1. Invalide el método OnMouseLeftButtonDown y cree un nuevo objeto StylusPointCollection. Obtenga la posición del mouse cuando se produjo el evento, cree un objeto StylusPoint a partir de los datos de los puntos y agregue el objeto StylusPoint a StylusPointCollection.

    Protected Overrides Sub OnMouseLeftButtonDown(ByVal e As MouseButtonEventArgs)
    
        MyBase.OnMouseLeftButtonDown(e)
    
        ' If a stylus generated this event, return.
        If Not (e.StylusDevice Is Nothing) Then
            Return
        End If
    
        ' Start collecting the points.
        stylusPoints = New StylusPointCollection()
        Dim pt As Point = e.GetPosition(Me)
        stylusPoints.Add(New StylusPoint(pt.X, pt.Y))
    
    End Sub 'OnMouseLeftButtonDown
    
    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. Invalide el método OnMouseMove. Obtenga la posición del mouse cuando se produjo el evento y cree un objeto StylusPoint con los datos de los puntos. Agregue StylusPoint al objeto StylusPointCollection que creó anteriormente.

    Protected Overrides Sub OnMouseMove(ByVal e As MouseEventArgs)
    
        MyBase.OnMouseMove(e)
    
        ' If a stylus generated this event, return.
        If Not (e.StylusDevice Is Nothing) Then
            Return
        End If
    
        ' Don't collect points unless the left mouse button
        ' is down.
        If e.LeftButton = MouseButtonState.Released Then
            Return
        End If
    
        If stylusPoints Is Nothing Then
            Return
        End If
    
        Dim pt As Point = e.GetPosition(Me)
        stylusPoints.Add(New StylusPoint(pt.X, pt.Y))
    
    End Sub 'OnMouseMove
    
    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. Invalide el método OnMouseLeftButtonUp. Cree un nuevo objeto Stroke con los datos de StylusPointCollection y agregue el nuevo Stroke que creó a la colección Strokes de InkPresenter.

    Protected Overrides Sub OnMouseLeftButtonUp(ByVal e As MouseButtonEventArgs)
    
        MyBase.OnMouseLeftButtonUp(e)
    
        ' If a stylus generated this event, return.
        If Not (e.StylusDevice Is Nothing) Then
            Return
        End If
    
        If stylusPoints Is Nothing Then
            stylusPoints = New StylusPointCollection()
        End If
    
        Dim pt As Point = e.GetPosition(Me)
        stylusPoints.Add(New StylusPoint(pt.X, pt.Y))
    
        ' Create a stroke and add it to the InkPresenter.
        Dim stroke As New Stroke(stylusPoints)
        stroke.DrawingAttributes = dr.DrawingAttributes
        ip.Strokes.Add(stroke)
    
        stylusPoints = Nothing
    
    End Sub 'OnMouseLeftButtonUp 
    
    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;
    
    }
    

Resultado final

En el ejemplo siguiente se ilustra un control personalizado que recopila la entrada manuscrita cuando el usuario emplea el mouse o el lápiz.

Imports System
Imports System.Collections.Generic
Imports System.Text
Imports System.Windows.Ink
Imports System.Windows.Input
Imports System.Windows.Input.StylusPlugIns
Imports System.Windows.Controls
Imports System.Windows


...


' A control for managing ink input
Class InkControl
    Inherits Label
    Private ip As InkPresenter
    Private dr As DynamicRenderer

    ' The StylusPointsCollection that gathers points 
    ' before Stroke from is created.
    Private stylusPoints As StylusPointCollection = Nothing


    Public Sub New()
        ' Add an InkPresenter for drawing.
        ip = New InkPresenter()
        Me.Content = ip

        ' Add a dynamic renderer that 
        ' draws ink as it "flows" from the stylus.
        dr = New DynamicRenderer()
        ip.AttachVisuals(dr.RootVisual, dr.DrawingAttributes)
        Me.StylusPlugIns.Add(dr)

        Dim cdr As New CustomDynamicRenderer()
        ip.AttachVisuals(cdr.RootVisual, cdr.DrawingAttributes)
        Me.StylusPlugIns.Add(cdr)

    End Sub 'New

    Shared Sub New()

        ' Allow ink to be drawn only within the bounds of the control.
        Dim owner As Type = GetType(InkControl)
        ClipToBoundsProperty.OverrideMetadata(owner, New FrameworkPropertyMetadata(True))

    End Sub 'New

    Protected Overrides Sub OnStylusDown(ByVal e As StylusDownEventArgs)
        ' Capture the stylus so all stylus input is routed to this control.
        Stylus.Capture(Me)

        ' Allocate memory for the StylusPointsCollection and
        ' add the StylusPoints that have come in so far.
        stylusPoints = New StylusPointCollection()
        Dim eventPoints As StylusPointCollection = e.GetStylusPoints(Me, stylusPoints.Description)

        stylusPoints.Add(eventPoints)

    End Sub 'OnStylusDown

    Protected Overrides Sub OnStylusMove(ByVal e As StylusEventArgs)

        If stylusPoints Is Nothing Then
            Return
        End If

        ' Add the StylusPoints that have come in since the 
        ' last call to OnStylusMove.
        Dim newStylusPoints As StylusPointCollection = e.GetStylusPoints(Me, stylusPoints.Description)
        stylusPoints.Add(newStylusPoints)

    End Sub 'OnStylusMove

    Protected Overrides Sub OnStylusUp(ByVal e As StylusEventArgs)
        ' Allocate memory for the StylusPointsCollection, if necessary.
        If stylusPoints Is Nothing Then
            Return
        End If

        ' Add the StylusPoints that have come in since the 
        ' last call to OnStylusMove.
        Dim newStylusPoints As StylusPointCollection = e.GetStylusPoints(Me, stylusPoints.Description)
        stylusPoints.Add(newStylusPoints)

        ' Create a new stroke from all the StylusPoints since OnStylusDown.
        Dim stroke As New Stroke(stylusPoints)

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

        ' Clear the StylusPointsCollection.
        stylusPoints = Nothing

        ' Release stylus capture.
        Stylus.Capture(Nothing)

    End Sub 'OnStylusUp

    Protected Overrides Sub OnMouseLeftButtonDown(ByVal e As MouseButtonEventArgs)

        MyBase.OnMouseLeftButtonDown(e)

        ' If a stylus generated this event, return.
        If Not (e.StylusDevice Is Nothing) Then
            Return
        End If

        ' Start collecting the points.
        stylusPoints = New StylusPointCollection()
        Dim pt As Point = e.GetPosition(Me)
        stylusPoints.Add(New StylusPoint(pt.X, pt.Y))

    End Sub 'OnMouseLeftButtonDown

    Protected Overrides Sub OnMouseMove(ByVal e As MouseEventArgs)

        MyBase.OnMouseMove(e)

        ' If a stylus generated this event, return.
        If Not (e.StylusDevice Is Nothing) Then
            Return
        End If

        ' Don't collect points unless the left mouse button
        ' is down.
        If e.LeftButton = MouseButtonState.Released Then
            Return
        End If

        If stylusPoints Is Nothing Then
            Return
        End If

        Dim pt As Point = e.GetPosition(Me)
        stylusPoints.Add(New StylusPoint(pt.X, pt.Y))

    End Sub 'OnMouseMove

    Protected Overrides Sub OnMouseLeftButtonUp(ByVal e As MouseButtonEventArgs)

        MyBase.OnMouseLeftButtonUp(e)

        ' If a stylus generated this event, return.
        If Not (e.StylusDevice Is Nothing) Then
            Return
        End If

        If stylusPoints Is Nothing Then
            stylusPoints = New StylusPointCollection()
        End If

        Dim pt As Point = e.GetPosition(Me)
        stylusPoints.Add(New StylusPoint(pt.X, pt.Y))

        ' Create a stroke and add it to the InkPresenter.
        Dim stroke As New Stroke(stylusPoints)
        stroke.DrawingAttributes = dr.DrawingAttributes
        ip.Strokes.Add(stroke)

        stylusPoints = Nothing

    End Sub 'OnMouseLeftButtonUp 
End Class 'StylusControl
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;

    }
}

Uso de complementos adicionales y representadores dinámicos

Como sucede con InkCanvas, su control personalizado puede tener elementos StylusPlugIn personalizados y objetos DynamicRenderer adicionales. Agréguelos a la colección StylusPlugIns. El orden de los objetos StylusPlugIn en StylusPlugInCollection afecta a la apariencia de la entrada manuscrita cuando se representa. Supongamos que tiene un objeto DynamicRenderer denominado dynamicRenderer y un objeto StylusPlugIn personalizado denominado translatePlugin que desplaza la entrada manuscrita del lápiz de Tablet PC. Si translatePlugin es el primer StylusPlugIn de StylusPlugInCollection y dynamicRenderer es el segundo, la entrada manuscrita que "fluye" se desplazará a medida que el usuario mueva el lápiz. Si dynamicRenderer está primero y translatePlugin es el segundo, no se desplazará la entrada manuscrita hasta que el usuario levante el lápiz.

Conclusión

Puede crear un control que recopile y represente la entrada manuscrita si invalida los métodos de evento de lápiz. Si crea un control propio, deriva sus propias clases StylusPlugIn y las inserta en StylusPlugInCollection, podrá implementar prácticamente cualquier comportamiento imaginable con la entrada manuscrita digital. Al tener acceso a los datos de StylusPoint a medida que se generan, puede personalizar la entrada de Stylus y representarla en pantalla según sea adecuado para su aplicación. Dado que tiene este acceso tan detallado a los datos de StylusPoint, puede implementar la recopilación y representación de la entrada manuscrita con un rendimiento óptimo para su aplicación.

Vea también

Otros recursos

Control avanzado de entrada manuscrita

Accessing and Manipulating Pen Input