可以创建动态和静态呈现墨迹的自定义控件。 也就是说,当用户绘制笔划时立即显示墨迹,使得墨迹仿佛从平板电脑笔中“流出”。而且,无论是通过平板电脑笔、从剪贴板粘贴,还是从文件中加载,墨迹在添加到控件后都会被显示。 若要动态呈现墨迹,控件必须使用 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方法,将RootVisual附加到InkPresenter的DynamicRenderer,并将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 方法。 创建一个新的Stroke与StylusPointCollection数据,并将新创建的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;
}
}
使用附加插件和动态渲染器
与 InkCanvas 一样,自定义控件可以具有自定义 StylusPlugIn 对象和其他 DynamicRenderer 对象。 将这些内容添加到 StylusPlugIns 集合。 这些对象在StylusPlugInStylusPlugInCollection中渲染时的顺序会影响墨迹的外观。 假设你有一个 DynamicRenderer 名为 dynamicRenderer
和一个自定义 StylusPlugIn 名为 translatePlugin
,它使平板电脑笔的墨水偏移。 如果 translatePlugin
是 StylusPlugInCollection 中的第一个 StylusPlugIn,并且 dynamicRenderer
是第二个,那么当用户移动笔时,“流动”的墨迹会发生偏移。 如果是 dynamicRenderer
第一个,并且 translatePlugin
是第二个,则在用户抬起笔之前,墨迹不会偏移。
结论
可以通过重写触笔事件方法创建一个控件来收集和呈现墨迹。 通过创建自己的控件、派生自己的 StylusPlugIn 类并将其插入 StylusPlugInCollection,可以实现几乎任何您能用数字墨迹想象的行为。 可以在数据生成的同时访问 StylusPoint 数据,使您能够定制 Stylus 输入,并在适合应用程序的情况下在屏幕上呈现它。 由于对数据具有如此低级别的访问权限 StylusPoint ,因此可以实现墨迹收集,并为应用程序呈现最佳性能。