RealTimeStylus 插件示例

此应用程序演示如何使用 RealTimeStylus 类。 有关 StylusInput API(包括 RealTimeStylus 类)的详细概述,请参阅 访问和操作触笔输入。 有关同步和异步插件的信息,请参阅 插件和 RealTimeStylus 类

示例概述

插件、实现 IStylusSyncPluginIStylusAsyncPlugin 接口的对象可以添加到 RealTimeStylus 对象。 此示例应用程序使用多种类型的插件:

  • 数据包筛选器插件:修改数据包。 此示例中的数据包筛选器插件通过约束矩形区域内的所有 (x,y) 数据包数据来修改数据包信息。
  • 自定义动态呈现器插件:修改动态呈现质量。 此示例中的自定义动态呈现插件通过在笔划上的每个 (x,y) 点周围绘制一个小圆来修改墨迹的呈现方式。
  • 动态呈现器插件:修改动态呈现质量。 此示例演示如何使用 DynamicRenderer 对象作为插件来处理墨迹的动态呈现。
  • 手势识别器插件:识别应用程序手势。 此示例演示如何使用 GestureRecognizer 对象作为插件来识别应用程序手势, (在具有) Microsoft 手势识别器的系统上运行。

此外,此示例提供了一个用户界面,使用户能够添加、删除和更改集合中每个插件的顺序。 示例解决方案包含两个项目:RealTimeStylusPluginApp 和 RealTimeStylusPlugins。 RealTimeStylusPluginApp 包含示例的用户界面。 RealTimeStylusPlugins 包含插件的实现。RealTimeStylusPlugins 项目定义 RealTimeStylusPlugins 命名空间,其中包含数据包筛选器和自定义动态呈现器插件。此命名空间由 RealTimeStylusPluginApp 项目引用。 RealTimeStylusPlugins 项目使用 Microsoft.InkMicrosoft.StylusInputMicrosoft.StylusInput.PluginData 命名空间。

有关 Microsoft.StylusInputMicrosoft.StylusInput.PluginData 命名空间的概述,请参阅 StylusInput API 的体系结构

数据包筛选器插件

数据包筛选器插件是演示数据包修改的同步插件。 具体而言,它定义窗体上的矩形。 在区域外部绘制的任何数据包都呈现在该区域内。 插件类 PacketFilterPlugin注册 、 StylusUpPackets 笔输入事件的通知StylusDown。 类实现在 IStylusSyncPlugin 类上定义的 StylusDownStylusUpPackets 方法。

的公共构造函数 PacketFilterPlugin 需要 Rectangle 结构。 此矩形定义矩形区域,以墨迹空间坐标 (.01mm = 1 HIMETRIC 单位) ,其中将包含数据包。 矩形保存在私有字段中。 rectangle

public class PacketFilterPlugin:IStylusSyncPlugin  
{
    private System.Drawing.Rectangle rectangle = System.Drawing.Rectangle.Empty;
    public PacketFilterPlugin(Rectangle r)
    {
        rectangle = r;
    }
    // ...

PacketFilterPlugin 通过实现 DataInterest 属性的 get 访问器来注册事件通知。 在这种情况下,插件对响应 StylusDown、、 PacketsStylusUpError 通知感兴趣。 此示例返回 DataInterestMask 枚举中定义的这些值。 当笔尖接触数字化器表面时,将调用 StylusDown 方法。 笔尖离开数字化器表面时,将调用 StylusUp 方法。 当 RealTimeStylus 对象接收数据包时,将调用 Packets 方法。 当当前插件或上一个插件引发异常时,将调用 Error 方法。

public DataInterestMask DataInterest
{
    get
    {
        return DataInterestMask.StylusDown | 
               DataInterestMask.Packets | 
               DataInterestMask.StylusUp | 
               DataInterestMask.Error;
    }
}           
    //...

PacketFilterPlugin 在帮助程序方法 ModifyPacketData中处理大多数通知。 方法 ModifyPacketDataPacketsData 类中获取每个新数据包的 x 和 y 值。 如果任一值在矩形之外,该方法会将该值替换为仍位于矩形内的最近点。 这是插件在从笔输入流接收数据包数据时如何替换数据包数据的示例。

private void ModifyPacketData(StylusDataBase data)
{
    for (int i = 0; i < data.Count ; i += data.PacketPropertyCount)
    {
        // packet data always has x followed by y followed by the rest
        int x = data[i];
        int y = data[i+1];

        // Constrain points to the input rectangle
        x = Math.Max(x, rectangle.Left);
        x = Math.Min(x, rectangle.Right);
        y = Math.Max(y, rectangle.Top);
        y = Math.Min(y, rectangle.Bottom);

        // If necessary, modify the x,y packet data
        if (x != data[i])
        {
            data[i] = x;
        }
        if (y != data[i+1])
        {
            data[i+1] = y;
        } 
    }
}

自定义动态呈现器插件

CustomDynamicRenderer 还实现 IStylusSyncPlugin 类来接收笔输入通知。 然后,它会处理通知, Packets 在每个新数据包点周围绘制一个小圆圈。

类包含一个 Graphics 变量,该变量保存对传递到类构造函数中的图形对象的引用。 这是用于动态呈现的图形对象。

private Graphics myGraphics;

public CustomDynamicRendererPlugin(Graphics g)
{
    myGraphics = g;
}
        //...
            

当自定义动态呈现器插件收到数据包通知时,它会提取 (x,y) 数据,并在点周围绘制一个小的绿色圆圈。 这是基于笔输入流的自定义呈现示例。

public void Packets(RealTimeStylus sender,  PacketsData data)
{           
    for (int i = 0; i < data.Count; i += data.PacketPropertyCount)
    {
        // Packet data always has x followed by y followed by the rest
        Point point = new Point(data[i], data[i+1]);

        // Since the packet data is in Ink Space coordinates, we need to convert to Pixels...
        point.X = (int)Math.Round((float)point.X * (float)myGraphics.DpiX/2540.0F);
        point.Y = (int)Math.Round((float)point.Y * (float)myGraphics.DpiY/2540.0F);

        // Draw a circle corresponding to the packet
        myGraphics.DrawEllipse(Pens.Green, point.X - 2, point.Y - 2, 4, 4);
    }
}

RealTimeStylusPluginApp 项目

RealTimeStylusPluginApp 项目演示前面所述的插件,以及 GestureRecognizerDynamicRenderer 插件。项目的用户界面包括:

  • 包含用于定义墨迹输入区域的 GroupBox 控件的窗体。
  • 用于列出和选择可用插件的 CheckedListBox 控件。
  • 一对 Button 对象 ,用于启用插件的重新排序。

项目定义结构 PlugInListItem,以便更轻松地管理项目中使用的插件。 结构 PlugInListItem 包含插件和说明。

RealTimeStylusPluginApp 本身实现 IStylusAsyncPlugin 类。 这是必需的, RealTimeStylusPluginApp 以便 当 GestureRecognizer 插件将手势数据添加到输出队列时,类可以收到通知。 应用程序注册 CustomStylusDataAdded 通知。 收到手势数据时, RealTimeStylusPluginApp 在窗体底部的状态栏上放置其说明。

public void CustomStylusDataAdded(RealTimeStylus sender, CustomStylusData data)
{
    if (data.CustomDataId == GestureRecognizer.GestureRecognitionDataGuid)
    {
        GestureRecognitionData grd = data.Data as GestureRecognitionData;
        if (grd != null)
        {
            if (grd.Count > 0)
            {
                GestureAlternate ga = grd[0];
                sbGesture.Text = "Gesture=" + ga.Id + ", Confidence=" + ga.Confidence;
            }
        }
    }
}

注意

CustomStylusDataAdded 实现中,有趣的是,可以通过 GUID (通过使用 GestureRecognitionDataGuid 字段) 或通过使用 as 语句) 的结果按类型 (标识输出队列中的自定义手势数据。 该示例使用这两种标识技术进行演示。 这两种方法本身也是有效的。

 

在窗体的 Load 事件处理程序中,应用程序创建 和 CustomDynamicRenderer 类的PacketFilter实例,并将其添加到列表框中。 然后,应用程序尝试创建 GestureRecognizer 类的实例,如果成功,则将其添加到列表框中。 如果系统上不存在手势识别器,则此操作将失败。 接下来,应用程序实例化 DynamicRenderer 对象并将其添加到列表框中。 最后,应用程序启用每个插件和 RealTimeStylus 对象本身。

关于该示例的另一个重要注意事项是,在帮助程序方法中,在添加或删除插件之前先禁用 RealTimeStylus 对象,然后在添加或删除完成后重新启用。

private void RemoveFromPluginCollection(int index)
{
    IStylusSyncPlugin plugin = ((PluginListItem)chklbPlugins.Items[index]).Plugin;

    bool rtsEnabled = myRealTimeStylus.Enabled;
    myRealTimeStylus.Enabled = false;
    myRealTimeStylus.SyncPluginCollection.Remove(plugin);
    myRealTimeStylus.Enabled = rtsEnabled;
}

Microsoft.StylusInput.DynamicRenderer

Microsoft.StylusInput.GestureRecognizer

Microsoft.StylusInput.RealTimeStylus

Microsoft.StylusInput.DataInterestMask

Microsoft.StylusInput.IStylusSyncPlugin

Microsoft.StylusInput.IStylusAsyncPlugin

Microsoft.StylusInput.PluginData.PacketsData

访问和操作触笔输入

插件和 RealTimeStylus 类

RealTimeStylus Ink 集合示例