墨迹对象模型:Windows 窗体和 COM 与 WPF 之比较

基本上有三个平台支持数字墨迹:平板电脑 Windows 窗体平台、平板电脑 COM 平台和 Windows Presentation Foundation (WPF) 平台。 Windows 窗体和 COM 平台共享类似的对象模型,但 WPF 平台的对象模型大相径庭。 本主题讨论高级别的差异,以便使用一个对象模型的开发人员可以更好地了解另一个对象模型。

在应用程序中启用墨迹

所有三个平台都提供对象和控制,使应用程序能够从平板电脑笔接收输入。 Windows 窗体和 COM 平台附带 Microsoft.Ink.InkPictureMicrosoft.Ink.InkEditMicrosoft.Ink.InkOverlayMicrosoft.Ink.InkCollector 类。 Microsoft.Ink.InkPictureMicrosoft.Ink.InkEdit 是可以添加到应用程序以收集墨迹的控件。 Microsoft.Ink.InkOverlayMicrosoft.Ink.InkCollector 可以附加到现有的窗口上,以使窗口和自定义控件支持墨迹功能。

WPF 平台包括 InkCanvas 控件。 可以向应用程序添加 InkCanvas,然后立即开始收集墨迹。 使用 InkCanvas,用户可以复制、选择和调整墨迹大小。 你可向 InkCanvas 添加其他控件,用户也可在这些控件上手写。 可以通过向其添加 InkPresenter 并收集其触笔点来创建启用墨迹的自定义控件。

下表列出了在应用程序中启用墨迹的详细信息:

要执行此操作 ... 在 WPF 平台上… 在 Windows 窗体/COM 平台上…
向应用程序添加启用墨迹的控件 请参阅墨迹入门 请参阅 自动声明表单示例
在自定义控件上启用墨迹 请参阅创建墨迹输入控件 请参阅 墨水剪贴板示例

墨水数据

在 Windows 窗体和 COM 平台上,Microsoft.Ink.InkCollectorMicrosoft.Ink.InkOverlayMicrosoft.Ink.InkEditMicrosoft.Ink.InkPicture 分别公开 Microsoft.Ink.Ink 对象。 Microsoft.Ink.Ink 对象包含一个或多个 Microsoft.Ink.Stroke 对象的数据,并公开用于管理和操作这些笔划的常用方法和属性。 Microsoft.Ink.Ink 对象管理其包含的笔划的生存期;Microsoft.Ink.Ink 对象创建并删除其拥有的笔划。 每个 Microsoft.Ink.Stroke 的标识符在其父 Microsoft.Ink.Ink 对象中是唯一的。

在 WPF 平台上,System.Windows.Ink.Stroke 类拥有和管理其自己的生存期。 一组 Stroke 对象可以一起收集在 StrokeCollection 中,它提供常见的墨迹数据管理操作方法,例如命中测试、擦除、转换和序列化墨迹。 在任何给定时间,Stroke 都可以属于零个、一个或多个 StrokeCollection 对象。 InkCanvas 包含的不是 InkPresenter 对象,而是 System.Windows.Ink.StrokeCollection

以下一对插图比较了墨迹数据对象模型。 在 Windows 窗体和 COM 平台上,Microsoft.Ink.Ink 对象约束 Microsoft.Ink.Stroke 对象的生存期,而触笔数据包属于各个笔划。 两个或更多笔划可以引用相同的 Microsoft.Ink.DrawingAttributes 对象,如下图所示。

COM/Winforms 墨迹对象模型示意图。

在 WPF 上,每个 System.Windows.Ink.Stroke 都是一个公共语言运行时对象,只要某些内容引用了该对象,该对象就会存在。 每个 Stroke 都引用一个 StylusPointCollectionSystem.Windows.Ink.DrawingAttributes 对象,该对象也是公共语言运行时对象。

WPF 墨迹对象模型图。

下表比较了如何在 WPF 平台和 Windows 窗体和 COM 平台上完成一些常见任务。

任务 Windows Presentation Foundation Windows 窗体和 COM
节约墨水 Save Microsoft.Ink.Ink.Save
加载墨迹 使用 StrokeCollection 构造函数创建一个 StrokeCollection Microsoft.Ink.Ink.Load
命中测试 HitTest Microsoft.Ink.Ink.HitTest
复制墨迹 CopySelection Microsoft.Ink.Ink.ClipboardCopy
粘贴墨迹 Paste Microsoft.Ink.Ink.ClipboardPaste
访问笔划集合的自定义属性 AddPropertyData(属性在内部通过 AddPropertyDataRemovePropertyDataContainsPropertyData进行存储和访问) 使用 Microsoft.Ink.Ink.ExtendedProperties

在平台之间共享墨迹

尽管平台具有不同的墨迹数据对象模型,但平台之间共享数据非常简单。 以下示例将墨迹从 Windows 窗体应用程序保存,并将墨迹加载到 Windows Presentation Foundation 应用程序中。

using Microsoft.Ink;
using System.Drawing;
Imports Microsoft.Ink
Imports System.Drawing
/// <summary>
/// Saves the digital ink from a Windows Forms application.
/// </summary>
/// <param name="inkToSave">An Ink object that contains the
/// digital ink.</param>
/// <returns>A MemoryStream containing the digital ink.</returns>
MemoryStream SaveInkInWinforms(Ink inkToSave)
{
    byte[] savedInk = inkToSave.Save();

    return (new MemoryStream(savedInk));
}
'/ <summary>
'/ Saves the digital ink from a Windows Forms application.
'/ </summary>
'/ <param name="inkToSave">An Ink object that contains the 
'/ digital ink.</param>
'/ <returns>A MemoryStream containing the digital ink.</returns>
Function SaveInkInWinforms(ByVal inkToSave As Ink) As MemoryStream 
    Dim savedInk As Byte() = inkToSave.Save()
    
    Return New MemoryStream(savedInk)

End Function 'SaveInkInWinforms
using System.Windows.Ink;
Imports System.Windows.Ink

/// <summary>
/// Loads digital ink into a StrokeCollection, which can be
/// used by a WPF application.
/// </summary>
/// <param name="savedInk">A MemoryStream containing the digital ink.</param>
public void LoadInkInWPF(MemoryStream inkStream)
{
    strokes = new StrokeCollection(inkStream);
}
'/ <summary>
'/ Loads digital ink into a StrokeCollection, which can be 
'/ used by a WPF application.
'/ </summary>
'/ <param name="savedInk">A MemoryStream containing the digital ink.</param>
Public Sub LoadInkInWPF(ByVal inkStream As MemoryStream) 
    strokes = New StrokeCollection(inkStream)

End Sub

以下示例将墨迹从 Windows Presentation Foundation 应用程序保存,并将墨迹加载到 Windows 窗体应用程序中。

using System.Windows.Ink;
Imports System.Windows.Ink

/// <summary>
/// Saves the digital ink from a WPF application.
/// </summary>
/// <param name="inkToSave">A StrokeCollection that contains the
/// digital ink.</param>
/// <returns>A MemoryStream containing the digital ink.</returns>
MemoryStream SaveInkInWPF(StrokeCollection strokesToSave)
{
    MemoryStream savedInk = new MemoryStream();

    strokesToSave.Save(savedInk);

    return savedInk;
}
'/ <summary>
'/ Saves the digital ink from a WPF application.
'/ </summary>
'/ <param name="inkToSave">A StrokeCollection that contains the 
'/ digital ink.</param>
'/ <returns>A MemoryStream containing the digital ink.</returns>
Function SaveInkInWPF(ByVal strokesToSave As StrokeCollection) As MemoryStream 
    Dim savedInk As New MemoryStream()
    
    strokesToSave.Save(savedInk)
    
    Return savedInk

End Function 'SaveInkInWPF

using Microsoft.Ink;
using System.Drawing;
Imports Microsoft.Ink
Imports System.Drawing
/// <summary>
/// Loads digital ink into a Windows Forms application.
/// </summary>
/// <param name="savedInk">A MemoryStream containing the digital ink.</param>
public void LoadInkInWinforms(MemoryStream savedInk)
{
    theInk = new Ink();
    theInk.Load(savedInk.ToArray());
}
'/ <summary>
'/ Loads digital ink into a Windows Forms application.
'/ </summary>
'/ <param name="savedInk">A MemoryStream containing the digital ink.</param>
Public Sub LoadInkInWinforms(ByVal savedInk As MemoryStream) 
    theInk = New Ink()
    theInk.Load(savedInk.ToArray())

End Sub

来自触笔的事件

Microsoft.Ink.InkOverlayMicrosoft.Ink.InkCollectorMicrosoft.Ink.InkPicture 在 Windows 窗体和 COM 平台上接收用户输入笔数据时的事件。 Microsoft.Ink.InkOverlayMicrosoft.Ink.InkCollector 附加到窗口或控件,并且可以订阅触笔输入数据引发的事件。 发生这些事件的线程取决于是用笔、鼠标还是以编程方式引发事件。 有关与这些事件相关的线程处理的详细信息,请参阅常规线程处理注意事项可以触发事件的线程

在 Windows Presentation Foundation 平台上,UIElement 类具有用于笔输入的事件。 这意味着每个控件都会公开完整的触笔事件集。 触笔事件具有隧道/浮升事件对,并且始终出现在应用程序线程上。 有关详细信息,请参阅 路由事件概述

下图显示了对引发触笔事件的类的对象模型进行比较。 Windows Presentation Foundation 对象模型仅显示浮泡事件,而不是隧道事件对应项。

WPF 与 Winforms 中的触笔事件对比示意图。

触笔数据

这三个平台都为你提供了截获和操作来自平板电脑笔的数据的方法。 在 Windows 窗体和 COM 平台上,这是通过创建 Microsoft.StylusInput.RealTimeStylus、将窗口或控件附加到它上面,并创建一个实现 Microsoft.StylusInput.IStylusSyncPluginMicrosoft.StylusInput.IStylusAsyncPlugin 接口的类来达成。 然后将自定义插件添加到 Microsoft.StylusInput.RealTimeStylus的插件集合中。 有关此对象模型的详细信息,请参阅 StylusInput API 的体系结构

在 WPF 平台上,UIElement 类公开插件集合,类似于 Microsoft.StylusInput.RealTimeStylus的设计。 若要截获笔数据,请创建一个从 StylusPlugIn 继承的类,并将对象添加到 StylusPlugInsUIElement 集合中。 有关此交互的详细信息,请参阅截获触笔的输入

在所有平台上,线程池通过触笔事件接收墨迹数据,并将其发送到应用程序线程。 有关 COM 和 Windows 平台上的线程处理的详细信息,请参阅 触笔输入 API 的线程处理注意事项。 有关 Windows 演示文稿软件上的线程的详细信息,请参阅 墨迹线程模型

下图比较了在触笔线程池上接收触笔数据的类的对象模型。

WPF 与 Winforms 中的 StylusPlugin 模型对比示意图。