墨迹线程处理模型
平板电脑上的墨迹的好处之一是,它感觉很像用常规笔和纸张书写。 为此,平板电脑笔以比鼠标写入的速率高得多收集输入数据,并在用户写入时呈现墨迹。 应用程序的用户界面(UI)线程不足以收集笔数据和呈现墨迹,因为它可能会被阻止。 为了解决此问题,WPF 应用程序在用户写入墨迹时使用另外两个线程。
以下列表描述了参与收集和呈现数字墨迹的线程:
笔线程 - 从触笔获取输入的线程。 (实际上,这是一个线程池,但本主题将其称为笔线程。)
应用程序用户界面线程 - 控制应用程序的用户界面的线程。
动态呈现线程 - 当用户绘制笔划时呈现墨迹的线程。 动态呈现线程不同于呈现应用程序的其他 UI 元素的线程,如 Window Presentation Foundation 线程处理模型中所述。
无论应用程序使用 InkCanvas 还是自定义控件(类似于创建墨迹输入控件中的控件),墨迹模型都是相同的。 尽管本主题讨论的是关于 InkCanvas的线程概念,但在创建自定义控件时,也适用相同的概念。
线程概述
下图演示了用户绘制笔划时的线程模型:
用户绘制笔划时发生的操作
当用户绘制笔划时,触笔点进入笔线程。 包括 DynamicRenderer 在内的触笔插件接受笔线程上的触笔点,并有机会在 InkCanvas 接收到它们之前对其进行修改。
DynamicRenderer 在动态呈现线程上呈现触笔点。 这与上一步同时发生。
InkCanvas 接收 UI 线程上的触笔点。
用户结束笔划后发生的操作
当用户完成绘制笔划时,InkCanvas 创建一个 Stroke 对象并将其添加到 InkPresenter 中,后者静态地呈现它。
UI 线程提醒 DynamicRenderer 笔画是静态呈现的,因此 DynamicRenderer 会删除笔划的可视表示形式。
墨迹集合和触笔插件
每个 UIElement 都有一个 StylusPlugInCollection。 StylusPlugInCollection 中的 StylusPlugIn 对象接收并可以修改笔线程上的触笔点。 StylusPlugIn 对象根据触笔点在 StylusPlugInCollection 中的顺序接收触笔点。
下图说明了 UIElement 的 StylusPlugIns 集合按此顺序包含 stylusPlugin1
、DynamicRenderer和 stylusPlugin2
的假设情况。
在上图中,发生以下行为:
StylusPlugin1
修改 x 和 y 的值。DynamicRenderer 接收修改后的触笔点,并在动态呈现线程上呈现它们。
StylusPlugin2
接收修改后的触笔点,并进一步修改 x 和 y 的值。应用程序收集触笔点,当用户完成笔划时,静态呈现笔划。
假设 stylusPlugin1
将触笔点限制在一个矩形范围内,stylusPlugin2
将触笔点平移到右侧。 在前面的场景中,DynamicRenderer 接收受限制的触笔点,但不接收经过转换的触笔点。 当用户绘制笔划时,笔划将呈现在矩形的边界内,但在用户拿起笔之前,笔划似乎不会转换。
在 UI 线程上使用触笔插件执行操作
由于无法对笔线程执行准确的命中测试,因此某些元素有时可能会接收适用于其他元素的触笔输入。 如果需要确保在执行操作之前正确路由输入,请订阅 OnStylusDownProcessed、OnStylusMoveProcessed 或 OnStylusUpProcessed 方法并执行操作。 执行准确的命中测试后,应用程序线程会调用这些方法。 若要订阅这些方法,请调用发生在笔线程上的方法中的 NotifyWhenProcessed 方法。
下图说明了在 StylusPlugIn 的触笔事件方面笔线程和 UI 线程之间的关系。
呈现墨迹
当用户绘制笔划时,DynamicRenderer 将墨迹呈现在单独的线程上,这样即使在 UI 线程繁忙时,墨迹也会看上去从笔中“流”出来。 当 DynamicRenderer 收集触笔点时,它在动态呈现线程上构建一个可视化树。 当用户完成笔划时,DynamicRenderer 要求在应用程序执行下一个呈现通道时收到通知。 应用程序完成下一个渲染过程后,DynamicRenderer 将清理其可视化树。 下图说明了此过程。
用户开始笔划。
- DynamicRenderer 创建可视化树。
用户正在绘制笔划。
- DynamicRenderer 生成可视化树。
用户结束笔划。
InkPresenter 将笔划添加到其可视化树中。
媒体集成层 (MIL) 静态呈现笔划。
DynamicRenderer 清除视觉对象。