创建墨迹输入控件

可以创建动态和静态呈现墨迹的自定义控件。 也就是说,当用户绘制笔划时立即显示墨迹,使得墨迹仿佛从平板电脑笔中“流出”。而且,无论是通过平板电脑笔、从剪贴板粘贴,还是从文件中加载,墨迹在添加到控件后都会被显示。 若要动态呈现墨迹,控件必须使用 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方法,将RootVisual附加到InkPresenterDynamicRenderer,并将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 方法。 创建一个新的StrokeStylusPointCollection数据,并将新创建的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;
    }
}

使用附加插件和动态渲染器

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

结论

可以通过重写触笔事件方法创建一个控件来收集和呈现墨迹。 通过创建自己的控件、派生自己的 StylusPlugIn 类并将其插入 StylusPlugInCollection,可以实现几乎任何您能用数字墨迹想象的行为。 可以在数据生成的同时访问 StylusPoint 数据,使您能够定制 Stylus 输入,并在适合应用程序的情况下在屏幕上呈现它。 由于对数据具有如此低级别的访问权限 StylusPoint ,因此可以实现墨迹收集,并为应用程序呈现最佳性能。

另请参阅