Compartilhar via


Criando um controle de entrada de tinta

Você pode criar um controle personalizado que renderiza dinamicamente e estaticamente a tinta. Ou seja, é possível renderizar a tinta conforme um usuário desenha um traço, fazendo com que a tinta apareça "fluindo" da caneta eletrônica e exibi-la depois de adicionada ao controle, tanto pela caneta eletrônica, colada da área de transferência ou carregada de um arquivo. Para renderizar dinamicamente a tinta, seu controle precisa usar um DynamicRenderer. Para renderizar tinta de maneira estática, você precisa sobrescrever os métodos de eventos de caneta (OnStylusDown, OnStylusMove e OnStylusUp) para coletar dados de StylusPoint, criar traços e adicioná-los a um InkPresenter (que renderiza a tinta no controle).

Este tópico contém as seguintes subseções:

Como coletar dados de ponto de caneta e criar traços de tinta

Para criar um controle que coleta e gerencia traços de tinta, faça o seguinte:

  1. Derivar uma classe de Control ou uma das classes 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. Adicione um InkPresenter à classe e defina a propriedade Content como o novo InkPresenter.

    InkPresenter ip;
    
    public InkControl()
    {
        // Add an InkPresenter for drawing.
        ip = new InkPresenter();
        this.Content = ip;
    }
    
  3. Anexe o RootVisual do DynamicRenderer ao InkPresenter chamando o método AttachVisuals e adicione o DynamicRenderer à coleção StylusPlugIns. Isso permite que o InkPresenter exiba a tinta à medida que os dados do ponto de caneta são coletados pelo controle.

    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. Substituir o método OnStylusDown. Neste método, capture a caneta com uma chamada para Capture. Ao capturar a caneta, o controle continuará a receber eventos StylusMove e StylusUp, mesmo que a caneta saia dos limites do controle. Isso não é estritamente obrigatório, mas quase sempre desejado para uma boa experiência do usuário. Crie um novo StylusPointCollection para coletar dados StylusPoint. Por fim, adicione o conjunto inicial de dados StylusPoint ao 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. Substitua o método OnStylusMove e adicione os dados StylusPoint ao objeto StylusPointCollection criado 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. Substitua o método OnStylusUp e crie um novo Stroke com os dados StylusPointCollection. Adicione o novo Stroke que você criou à coleção Strokes do InkPresenter e libere a captura da caneta.

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

Como habilitar seu controle para aceitar a entrada do mouse

Se você adicionar o controle anterior ao seu aplicativo, executá-lo e usar o mouse como um dispositivo de entrada, você notará que os traços não serão preservados. Para manter os traços quando o mouse for usado como o dispositivo de entrada, faça o seguinte:

  1. Substitua o OnMouseLeftButtonDown e crie um novo StylusPointCollection Obtenha a posição do mouse quando o evento ocorreu e crie um StylusPoint usando os dados de ponto e adicione o StylusPoint ao 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. Substituir o método OnMouseMove. Obtenha a posição do mouse quando o evento ocorreu e crie um StylusPoint usando os dados de ponto. Adicione o StylusPoint ao objeto StylusPointCollection que você criou 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. Substituir o método OnMouseLeftButtonUp. Crie um novo Stroke com os dados de StylusPointCollection e adicione o novo Stroke que você criou à coleção Strokes do 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;
    }
    

Juntando as peças

O exemplo a seguir é um controle personalizado que coleta tinta quando o usuário usa o mouse ou a caneta.

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

Usando plug-ins adicionais e o DynamicRenderers

Assim como o InkCanvas, seu controle personalizado pode ter StylusPlugIn personalizados e objetos DynamicRenderer adicionais. Adicione-os à coleção StylusPlugIns. A ordem dos objetos StylusPlugIn no StylusPlugInCollection afeta a aparência da tinta quando ela é renderizada. Suponha que você tenha um DynamicRenderer chamado dynamicRenderer e um StylusPlugIn personalizado chamado translatePlugin que ajusta a tinta da caneta eletrônica. Se translatePlugin for o primeiro StylusPlugIn no StylusPlugInCollection e dynamicRenderer for o segundo, a tinta que "flui" será desviada enquanto o usuário move a caneta. Se dynamicRenderer for o primeiro e translatePlugin for o segundo, a tinta não será deslocada até que o usuário levante a caneta.

Conclusão

Você pode criar um controle que coleta e renderiza a tinta, substituindo os métodos de evento da caneta. Criando seu próprio controle, derivando suas próprias classes de StylusPlugIn e inserindo-as no StylusPlugInCollection, você pode implementar praticamente qualquer comportamento imaginável com tinta digital. Você tem acesso aos dados StylusPoint conforme eles são gerados, permitindo que você personalize as entradas Stylus e as renderize na tela de forma apropriada para o seu aplicativo. Como você tem acesso de nível tão baixo aos dados de StylusPoint, você pode implementar a coleta de tinta e renderizá-la com o desempenho ideal para seu aplicativo.

Consulte também