创建墨迹输入控件

可以创建动态和静态呈现墨迹的自定义控件。 也就是说,当用户绘制笔划时呈现墨迹,使墨迹看起来从平板电脑笔中“流出”,并在墨迹被添加到控件后显示,无论是通过平板电脑笔、从剪贴板粘贴还是从文件加载。 若要动态呈现墨迹,控件必须使用 DynamicRenderer标识符。 若要静态渲染墨迹,必须重写触笔事件方法(OnStylusDownOnStylusMoveOnStylusUp),以收集 StylusPoint 数据、创建笔划,并将其添加到 InkPresenter(将墨迹呈现在控件上)。

本主题包含以下小节:

如何:收集触笔点数据并创建墨迹笔划

若要创建收集和管理墨迹笔划的控件,请执行以下操作:

  1. Control 派生一个类或从 Control派生的类之一,例如 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. 向类添加 InkPresenter,并将 Content 属性设置为新的 InkPresenter

    InkPresenter ip;
    
    public InkControl()
    {
        // Add an InkPresenter for drawing.
        ip = new InkPresenter();
        this.Content = ip;
    }
    
  3. 通过调用 AttachVisuals 方法将 DynamicRendererRootVisual 附加到 InkPresenter,并将 DynamicRenderer 添加到 StylusPlugIns 集合。 这样,InkPresenter 就可以在控件收集触笔点数据时显示墨迹。

    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. 重写 OnStylusDown 方法。 在此方法中,通过调用 Capture来捕获触笔。 通过捕获触笔,你的控件将继续接收 StylusMoveStylusUp 事件,即使触笔离开控件的边界。 这不是严格强制性的,但几乎总是需要良好的用户体验。 创建新的 StylusPointCollection 以收集 StylusPoint 数据。 最后,将初始 StylusPoint 数据集添加到 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. 重写 OnStylusMove 方法,并将 StylusPoint 数据添加到之前创建的 StylusPointCollection 对象。

    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. 重写 OnStylusUp 方法,并使用 StylusPointCollection 数据创建新的 Stroke。 将您创建的新 Stroke 添加到 InkPresenterStrokes 集合中,并释放触控笔捕获。

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

如何:使控件能够接受鼠标输入

如果将上述控件添加到应用程序,请运行它并使用鼠标作为输入设备,你会注意到笔划不会持久保存。 若要在鼠标用作输入设备时保留笔划,请执行以下操作:

  1. 重写 OnMouseLeftButtonDown 并创建新的 StylusPointCollection 获取事件发生时鼠标的位置,并使用点数据创建 StylusPoint,并将 StylusPoint 添加到 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. 重写 OnMouseMove 方法。 获取事件发生时鼠标的位置,并使用点数据创建 StylusPoint。 将 StylusPoint 添加到之前创建的 StylusPointCollection 对象。

    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. 重写 OnMouseLeftButtonUp 方法。 使用 StylusPointCollection 数据创建新的 Stroke,并将创建的新 Stroke 添加到 InkPresenterStrokes 集合中。

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

把它放在一起

以下示例是当用户使用鼠标或笔时收集墨迹的自定义控件。

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

使用其他插件和“DynamicRenderers”

与 InkCanvas 一样,您的自定义控件可以具备自定义的 StylusPlugIn 和其他 DynamicRenderer 对象。 将这些内容添加到 StylusPlugIns 集合。 StylusPlugInCollectionStylusPlugIn 对象的顺序会影响墨迹显示时的外观。 假设你有一个名为 dynamicRendererDynamicRenderer 和一个名为 translatePlugin 的自定义 StylusPlugIn,其中 translatePlugin 用于调整从平板电脑笔中释放的墨迹。 如果 translatePluginStylusPlugInCollection中的第一个 StylusPlugIndynamicRenderer 是第二个,那么当用户移动笔时,“流”的墨迹将会偏移。 如果 dynamicRenderer 是第一个,并且 translatePlugin 为第二个,则在用户抬起笔之前,墨迹不会偏移。

结论

您可以通过重写触笔事件方法来创建一个控件,以收集和显示墨迹。 通过创建自定义控件、派生自己的 StylusPlugIn 类并将其插入到 StylusPlugInCollection中,您几乎可以实现各种可以用数字墨水设想的行为。 你可以访问生成的 StylusPoint 数据,从而有机会根据应用程序自定义 Stylus 输入并将其呈现在屏幕上。 由于对 StylusPoint 数据具有如此低级别的访问权限,因此你可以实现墨迹收集,并为应用程序呈现其最佳性能。

另请参阅