Creación de un control de entrada manuscrita

Puede crear un control personalizado que represente una entrada de lápiz de forma dinámica y estática; esto es, representar la entrada de lápiz a medida que el usuario va dibujando el trazo, lo que hace que la entrada parezca "fluir" desde el lápiz de tableta, y mostrar la entrada de lápiz después de agregarla al control, ya sea a través del lápiz de tableta, pegándola desde el Portapapeles o cargándola desde un archivo. Para representar una entrada de lápiz de forma dinámica, el control debe usar una clase DynamicRenderer. Para representar la entrada de lápiz estáticamente, debe invalidar los métodos de evento de lápiz (OnStylusDown, OnStylusMove y OnStylusUp) para recopilar datos de StylusPoint, crear trazos y agregarlos a un elemento InkPresenter (que representa la entrada de lápiz en el control).

Este tema contiene las siguientes subsecciones:

Procedimiento para recopilar datos de punto de lápiz y crear trazos de lápiz

Haga lo siguiente para crear un control que recopile y administre trazos de lápiz:

  1. Derive una clase de Control o una de las clases derivadas de Control, como 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. Agregue un objeto InkPresenter a la clase y establezca la propiedad Content en ese nuevo objeto InkPresenter.

    InkPresenter ip;
    
    public InkControl()
    {
        // Add an InkPresenter for drawing.
        ip = new InkPresenter();
        this.Content = ip;
    }
    
  3. Asocie el elemento RootVisual de DynamicRenderer a InkPresenter mediante una llamada al método AttachVisuals, y agregue el objeto DynamicRenderer a la colección StylusPlugIns. Esto permite a InkPresenter mostrar la entrada de lápiz a medida que el control recopila los datos del punto de lápiz.

    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, el control seguirá recibiendo los eventos StylusMove y StylusUp incluso cuando el lápiz no esté dentro de los límites del control. Esto no es estrictamente obligatorio, pero casi siempre es muy aconsejable para obtener una buena experiencia de usuario. Cree una colección StylusPointCollection para recopilar datos de StylusPoint. Por último, agregue el conjunto inicial de datos de StylusPoint a StylusPointCollection.

    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 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 una clase Stroke con los datos de StylusPointCollection. Agregue la nueva clase Stroke que ha creado a la colección Strokes de InkPresenter y libere la captura del lápiz.

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

Procedimiento para habilitar el control para aceptar entradas del mouse

Si agrega el control anterior a la aplicación, lo ejecuta y usa el mouse como dispositivo de entrada, verá que los trazos no se conservan. Haga lo siguiente para conservar los trazos cuando se use el mouse como dispositivo de entrada:

  1. Invalide OnMouseLeftButtonDown y cree una colección StylusPointCollection. Obtenga la posición del mouse cuando se produjo el evento, cree un objeto StylusPoint mediante los datos de punto y agregue StylusPoint a StylusPointCollection.

    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 mediante los datos de punto. Agregue StylusPoint al objeto StylusPointCollection que creó anteriormente.

    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 objeto Stroke con los datos de StylusPointCollection y agregue ese nuevo objeto Stroke que ha creado a la colección Strokes de InkPresenter.

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

Reunión de todo

El ejemplo siguiente es un control personalizado que recopila entradas de lápiz cuando el usuario usa el mouse o el lápiz.

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 otros complementos y objetos DynamicRenderer

Al igual que inkCanvas, el control personalizado puede tener objetos StylusPlugIn personalizados y más objetos DynamicRenderer. Agréguelos a la colección StylusPlugIns. El orden de los objetos StylusPlugIn en StylusPlugInCollection afecta a la apariencia de la entrada de lápiz cuando se representa. Supongamos que tiene un objeto DynamicRenderer llamado dynamicRenderer y un objeto StylusPlugIn personalizado denominado translatePlugin que desplaza la entrada de lápiz de la tableta. Si translatePlugin es el primer objeto StylusPlugIn en StylusPlugInCollection y dynamicRenderer es el segundo, la entrada de lápiz que "fluye" aparecerá desplazada mientras el usuario mueve el lápiz. Si dynamicRenderer va primero y translatePlugin después, la entrada de lápiz no se desplazará hasta que el usuario levante el lápiz.

Conclusión

Puede crear un control que recopile y represente las entradas de lápiz mediante la invalidación de los métodos de evento de lápiz. Al crear su propio control, derivar sus propias clases StylusPlugIn e insertarlas en StylusPlugInCollection, podrá implementar prácticamente cualquier comportamiento imaginable con una entrada de lápiz digital. Podrá acceder a los datos de StylusPoint a medida que se generan, lo que ofrece la oportunidad de personalizar la entrada de Stylus y representarla en la pantalla según corresponda para la aplicación. Dado el acceso de bajo nivel a los datos de StylusPoint, podrá implementar la colección de entrada de lápiz y representarla con un rendimiento óptimo para la aplicación.

Vea también