创建墨迹输入控件
可以创建动态和静态呈现墨迹的自定义控件。 也就是说,在用户绘制笔划时呈现墨迹,使墨迹看起来像是从触笔中“流出”的一样,并在将墨迹添加到控件(通过触笔、从剪贴板粘贴或从文件加载)后显示墨迹。 若要动态呈现墨迹,控件必须使用 DynamicRenderer。 要静态呈现墨迹,必须重写触笔事件方法(OnStylusDown、OnStylusMove 和 OnStylusUp)以收集 StylusPoint 数据、创建笔触并将它们添加到 InkPresenter(用于在控件上呈现墨迹)。
本主题包含以下小节:
如何:收集触笔点数据并创建墨迹笔划
若要创建收集和管理墨迹笔划的控件,请执行以下操作:
从 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 {
}
向类添加 InkPresenter,并将 Content 属性设置为新的 InkPresenter。
InkPresenter ip; public InkControl() { // Add an InkPresenter for drawing. ip = new InkPresenter(); this.Content = ip; }
通过调用 AttachVisuals 方法将 DynamicRenderer 的 RootVisual 附加到 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); }
重写 OnStylusDown 方法。 在此方法中,通过调用 Capture 来捕获触笔。 通过捕获触笔,即使触笔离开控件的边界,控件也会继续接收 StylusMove 和 StylusUp 事件。 这不是严格强制的,但若要获得良好的用户体验,则始终需要这样做。 创建新的 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); }
重写 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); }
重写 OnStylusUp 方法并使用 StylusPointCollection 数据创建一个新 Stroke。 将创建的新 Stroke 添加到 InkPresenter 的 Strokes 集合,并释放触笔捕获。
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); }
如何:使控件能够接受鼠标输入
如果将上述控件添加到应用程序,运行它并使用鼠标作为输入设备,你会注意到笔划不会持久保留。 要在将鼠标用作输入设备时保留笔画,请执行以下操作:
重写 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)); }
重写 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)); }
重写 OnMouseLeftButtonUp 方法。 使用 StylusPointCollection 数据创建一个新 Stroke,并将创建的新 Stroke 添加到 InkPresenter 的 Strokes 集合。
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 集合。 StylusPlugInCollection 中 StylusPlugIn 对象的顺序会影响墨迹的呈现外观。 假设你有一个名为 dynamicRenderer
的 DynamicRenderer 和一个名为 translatePlugin
的自定义 StylusPlugIn,用于偏移来自触笔的墨迹。 如果 translatePlugin
是 StylusPlugInCollection 中的第一个 StylusPlugIn,而 dynamicRenderer
是第二个,则“流动”的墨迹将随着用户移动笔而偏移。 如果 dynamicRenderer
是第一个,而 translatePlugin
是第二个,则在用户抬起笔之前,墨迹不会偏移。
结论
可以通过重写触笔事件方法来创建一个收集和呈现墨迹的控件。 通过创建自己的控件、派生自己的 StylusPlugIn 类并将其插入 StylusPlugInCollection 中,可以实现几乎任何可使用数字墨迹想象的行为。 你可以在 StylusPoint 数据生成时访问该数据,借此机会自定义 Stylus 输入并按照适合应用程序的方式在屏幕上呈现该输入。 由于你对 StylusPoint 数据的访问权限非常低,因此可以使用应用程序的最佳性能实现墨迹收集和呈现。