创建墨迹输入控件
更新:2007 年 11 月
您可以创建一个既能动态呈现墨迹又能静态呈现墨迹的自定义控件。更确切地说,动态呈现是指用户边画笔画边呈现墨迹(这样,墨迹看上去好像是从 Tablet 笔中“画出”的),静态呈现是指在通过 Tablet 笔、从剪贴板中粘贴或从文件中加载的方式将墨迹添加到控件之后显示墨迹。若要动态呈现墨迹,您的控件必须使用 DynamicRenderer。若要静态呈现墨迹,必须重写手写笔事件方法(OnStylusDown、OnStylusMove 和 OnStylusUp)以收集 StylusPoint 数据、创建笔画以及将笔画添加到 InkPresenter(在控件上呈现墨迹)。
本主题包含以下小节:
如何:收集手写笔接触点数据和创建墨迹笔画
如何:使控件能够接受通过鼠标输入的内容
将内容结合起来
使用附加的插件和 DynamicRenderers
结束语
如何:收集手写笔接触点数据和创建墨迹笔画
若要创建收集和管理墨迹笔画的控件,请执行下列操作:
从 Control 或派生自 Control 的一个类中派生一个类,例如 Label。
Imports System Imports System.Collections.Generic Imports System.Text Imports System.Windows.Ink Imports System.Windows.Input Imports System.Windows.Input.StylusPlugIns Imports System.Windows.Controls Imports System.Windows ... Class InkControl Inherits Label ... End Class 'StylusControl
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。
Private ip As InkPresenter Public Sub New() ' Add an InkPresenter for drawing. ip = New InkPresenter() Me.Content = ip End Sub
InkPresenter ip; public InkControl() { // Add an InkPresenter for drawing. ip = new InkPresenter(); this.Content = ip; }
通过调用 AttachVisuals 方法将 DynamicRenderer 的 RootVisual 附加至 InkPresenter,并将 DynamicRenderer 添加至 StylusPlugIns 集合。这样,在控件收集手写笔接触点数据时,InkPresenter 可以显示墨迹。
Public Sub New() ... ' Add a dynamic renderer that ' draws ink as it "flows" from the stylus. dr = New DynamicRenderer() ip.AttachVisuals(dr.RootVisual, dr.DrawingAttributes) Me.StylusPlugIns.Add(dr) End Sub
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 Overrides Sub OnStylusDown(ByVal e As StylusDownEventArgs) ' Capture the stylus so all stylus input is routed to this control. Stylus.Capture(Me) ' Allocate memory for the StylusPointsCollection and ' add the StylusPoints that have come in so far. stylusPoints = New StylusPointCollection() Dim eventPoints As StylusPointCollection = e.GetStylusPoints(Me, stylusPoints.Description) stylusPoints.Add(eventPoints) End Sub 'OnStylusDown
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 Overrides Sub OnStylusMove(ByVal e As StylusEventArgs) If stylusPoints Is Nothing Then Return End If ' Add the StylusPoints that have come in since the ' last call to OnStylusMove. Dim newStylusPoints As StylusPointCollection = e.GetStylusPoints(Me, stylusPoints.Description) stylusPoints.Add(newStylusPoints) End Sub 'OnStylusMove
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 Overrides Sub OnStylusUp(ByVal e As StylusEventArgs) ' Allocate memory for the StylusPointsCollection, if necessary. If stylusPoints Is Nothing Then Return End If ' Add the StylusPoints that have come in since the ' last call to OnStylusMove. Dim newStylusPoints As StylusPointCollection = e.GetStylusPoints(Me, stylusPoints.Description) stylusPoints.Add(newStylusPoints) ' Create a new stroke from all the StylusPoints since OnStylusDown. Dim stroke As New Stroke(stylusPoints) ' Add the new stroke to the Strokes collection of the InkPresenter. ip.Strokes.Add(stroke) ' Clear the StylusPointsCollection. stylusPoints = Nothing ' Release stylus capture. Stylus.Capture(Nothing) End Sub 'OnStylusUp
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 Overrides Sub OnMouseLeftButtonDown(ByVal e As MouseButtonEventArgs) MyBase.OnMouseLeftButtonDown(e) ' If a stylus generated this event, return. If Not (e.StylusDevice Is Nothing) Then Return End If ' Start collecting the points. stylusPoints = New StylusPointCollection() Dim pt As Point = e.GetPosition(Me) stylusPoints.Add(New StylusPoint(pt.X, pt.Y)) End Sub 'OnMouseLeftButtonDown
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 Overrides Sub OnMouseMove(ByVal e As MouseEventArgs) MyBase.OnMouseMove(e) ' If a stylus generated this event, return. If Not (e.StylusDevice Is Nothing) Then Return End If ' Don't collect points unless the left mouse button ' is down. If e.LeftButton = MouseButtonState.Released Then Return End If If stylusPoints Is Nothing Then Return End If Dim pt As Point = e.GetPosition(Me) stylusPoints.Add(New StylusPoint(pt.X, pt.Y)) End Sub 'OnMouseMove
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 Overrides Sub OnMouseLeftButtonUp(ByVal e As MouseButtonEventArgs) MyBase.OnMouseLeftButtonUp(e) ' If a stylus generated this event, return. If Not (e.StylusDevice Is Nothing) Then Return End If If stylusPoints Is Nothing Then stylusPoints = New StylusPointCollection() End If Dim pt As Point = e.GetPosition(Me) stylusPoints.Add(New StylusPoint(pt.X, pt.Y)) ' Create a stroke and add it to the InkPresenter. Dim stroke As New Stroke(stylusPoints) stroke.DrawingAttributes = dr.DrawingAttributes ip.Strokes.Add(stroke) stylusPoints = Nothing End Sub 'OnMouseLeftButtonUp
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; }
将内容结合起来
下面的示例是一个自定义控件,它在用户使用鼠标或笔时收集墨迹。
Imports System
Imports System.Collections.Generic
Imports System.Text
Imports System.Windows.Ink
Imports System.Windows.Input
Imports System.Windows.Input.StylusPlugIns
Imports System.Windows.Controls
Imports System.Windows
...
' A control for managing ink input
Class InkControl
Inherits Label
Private ip As InkPresenter
Private dr As DynamicRenderer
' The StylusPointsCollection that gathers points
' before Stroke from is created.
Private stylusPoints As StylusPointCollection = Nothing
Public Sub New()
' Add an InkPresenter for drawing.
ip = New InkPresenter()
Me.Content = ip
' Add a dynamic renderer that
' draws ink as it "flows" from the stylus.
dr = New DynamicRenderer()
ip.AttachVisuals(dr.RootVisual, dr.DrawingAttributes)
Me.StylusPlugIns.Add(dr)
Dim cdr As New CustomDynamicRenderer()
ip.AttachVisuals(cdr.RootVisual, cdr.DrawingAttributes)
Me.StylusPlugIns.Add(cdr)
End Sub 'New
Shared Sub New()
' Allow ink to be drawn only within the bounds of the control.
Dim owner As Type = GetType(InkControl)
ClipToBoundsProperty.OverrideMetadata(owner, New FrameworkPropertyMetadata(True))
End Sub 'New
Protected Overrides Sub OnStylusDown(ByVal e As StylusDownEventArgs)
' Capture the stylus so all stylus input is routed to this control.
Stylus.Capture(Me)
' Allocate memory for the StylusPointsCollection and
' add the StylusPoints that have come in so far.
stylusPoints = New StylusPointCollection()
Dim eventPoints As StylusPointCollection = e.GetStylusPoints(Me, stylusPoints.Description)
stylusPoints.Add(eventPoints)
End Sub 'OnStylusDown
Protected Overrides Sub OnStylusMove(ByVal e As StylusEventArgs)
If stylusPoints Is Nothing Then
Return
End If
' Add the StylusPoints that have come in since the
' last call to OnStylusMove.
Dim newStylusPoints As StylusPointCollection = e.GetStylusPoints(Me, stylusPoints.Description)
stylusPoints.Add(newStylusPoints)
End Sub 'OnStylusMove
Protected Overrides Sub OnStylusUp(ByVal e As StylusEventArgs)
' Allocate memory for the StylusPointsCollection, if necessary.
If stylusPoints Is Nothing Then
Return
End If
' Add the StylusPoints that have come in since the
' last call to OnStylusMove.
Dim newStylusPoints As StylusPointCollection = e.GetStylusPoints(Me, stylusPoints.Description)
stylusPoints.Add(newStylusPoints)
' Create a new stroke from all the StylusPoints since OnStylusDown.
Dim stroke As New Stroke(stylusPoints)
' Add the new stroke to the Strokes collection of the InkPresenter.
ip.Strokes.Add(stroke)
' Clear the StylusPointsCollection.
stylusPoints = Nothing
' Release stylus capture.
Stylus.Capture(Nothing)
End Sub 'OnStylusUp
Protected Overrides Sub OnMouseLeftButtonDown(ByVal e As MouseButtonEventArgs)
MyBase.OnMouseLeftButtonDown(e)
' If a stylus generated this event, return.
If Not (e.StylusDevice Is Nothing) Then
Return
End If
' Start collecting the points.
stylusPoints = New StylusPointCollection()
Dim pt As Point = e.GetPosition(Me)
stylusPoints.Add(New StylusPoint(pt.X, pt.Y))
End Sub 'OnMouseLeftButtonDown
Protected Overrides Sub OnMouseMove(ByVal e As MouseEventArgs)
MyBase.OnMouseMove(e)
' If a stylus generated this event, return.
If Not (e.StylusDevice Is Nothing) Then
Return
End If
' Don't collect points unless the left mouse button
' is down.
If e.LeftButton = MouseButtonState.Released Then
Return
End If
If stylusPoints Is Nothing Then
Return
End If
Dim pt As Point = e.GetPosition(Me)
stylusPoints.Add(New StylusPoint(pt.X, pt.Y))
End Sub 'OnMouseMove
Protected Overrides Sub OnMouseLeftButtonUp(ByVal e As MouseButtonEventArgs)
MyBase.OnMouseLeftButtonUp(e)
' If a stylus generated this event, return.
If Not (e.StylusDevice Is Nothing) Then
Return
End If
If stylusPoints Is Nothing Then
stylusPoints = New StylusPointCollection()
End If
Dim pt As Point = e.GetPosition(Me)
stylusPoints.Add(New StylusPoint(pt.X, pt.Y))
' Create a stroke and add it to the InkPresenter.
Dim stroke As New Stroke(stylusPoints)
stroke.DrawingAttributes = dr.DrawingAttributes
ip.Strokes.Add(stroke)
stylusPoints = Nothing
End Sub 'OnMouseLeftButtonUp
End Class 'StylusControl
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(能够使 Tablet 笔的墨迹偏移)。如果 translatePlugin 是 StylusPlugInCollection 中的第一个 StylusPlugIn,dynamicRenderer 是第二个,则当用户移动笔时所“画出”的墨迹将会偏移。如果 dynamicRenderer 是第一个,translatePlugin 是第二个,则在用户提起笔之前,墨迹将不会偏移。
结束语
通过重写手写笔事件方法,您可以创建一个能够收集和呈现墨迹的控件。通过创建自己的控件,派生自己的 StylusPlugIn 类并将这些类插入到 StylusPlugInCollection,您几乎可以实现可想像的任何数字墨迹行为。您可以在生成 StylusPoint 数据时访问该数据,从而有机会自定义 Stylus 输入并且适当时会在屏幕上为您的应用程序呈现它。因为能够对 StylusPoint 数据进行这种低级别访问,所以可以在确保应用程序最佳性能的情况下实现墨迹收集和呈现墨迹。