简介
如果您没有机会使用 Microsoft Windows XP Tablet PC Edition 2005 的最新版本,您将不知道您会错过什么内容。使用新的操作系统和 Microsoft OneNote™ 2003 Service Pack 1 预览版,我再也不用笔和纸进行书写了。对于那些需要写下所有东西才能最好地工作的人来说(我估计他们每年要使用 5 到 6 本 500 页的笔记本),转而完全进行电子书写的梦想变成了现实。它也使我对编写针对 Tablet PC 优化的应用程序非常感兴趣。
因为便携式计算机和 Tablet PC 间的界线变得越发模糊,所以进行该优化尤为重要。便携式计算机的应用程序可以很好地运行在 Tablet PC 上,这是因为 Windows XP Tablet PC Edition 2005 是 Windows XP Professional 的超集;然而,只要通过少量的工作,您就可以使 Tablet PC 用户的生活变得更加轻松。在本文中,我讨论了为确保应用程序能够利用 Windows XP Tablet PC Edition 中的功能所采取的步骤。本文用大量篇幅为您介绍了一个很好的控件,它可以使您非常容易地改变 Tablet PC 输入面板以协助用户输入数据。该控件的好处在于它可以轻松地合并到应用程序中。只需通过一天左右的努力,您将可以创建一个对于我们这些使用 Tablet PC 的人来说非常用户友好的应用程序。
在本文中,我假设您已经阅读了 Tablet PC SDK 1.7 帮助文件中的介绍性材料。该帮助文件中包含了很多重要信息,在您准备进行 Tablet PC 开发时,应该首先从学习该文件入手。
基础知识
当您的应用程序第一次在 Tablet PC 上运行时,您可能会发现自己已经使用横向模式太久了。Microsoft 发现大多数 Tablet PC 用户会立即将他们的计算机调转到纵向模式,并一直保持该模式。因为我也是其中一员,我也总是使用横向方向运行应用程序。图 1 显示了没有考虑纵向模式的应用程序的真实示例。尽管我不会明确反对具有图 1 中对话框的公司,但我还是希望他们能尽快停止强制我切换到横向来使用他们的应用程序。
图 1. 不考虑纵向模式
关于对话框和窗口大小问题,一个简单的解决方法就是在 768 x 768 的分辨率下保存所有内容。这是因为 Tablet PC 的最小纵向分辨率为 768 x 1024,而最小横向分辨率为 1024 x 768。但是,您还可以做得更好。在本文的源代码中,我包括了一个带有 LandscapeScreen 和 PortraitScreen 属性的 Utilities 类。使用这两个属性来检查方向,然后使您的应用程序相应地做出响应。
如果您已经从事 .NET Windows 窗体开发有一段时间了,您可能会疑惑为什么我不简单地使用 Screen.PrimaryScreen 属性来确定方向。问题在于 PrimaryScreen 中的值从启动时就进行了缓存,并且当用户更改 Tablet PC 上的屏幕方向时该值不会进行更新。 因此,需要自己调用 Win32 EnumDisplaySettings 函数进行查看方能才能确定屏幕方向。
如果您希望在屏幕分辨率发生更改时进行高级的窗体布局或其他特定于 Tablet PC 的工作,则只要屏幕分辨率或模式发生更改,SystemEvents.DisplaySettingsChanged 事件就会触发。在处理专为 Tablet PC 设计的应用程序时,我发现要使应用程序中的每个窗体都请求该事件需要非常大的工作量。我想,如果窗体处理了该事件并将 DisplaySettingChanged 提供为脱离窗体本身的一个事件,那么事情就会变得更加简单了。源代码中的 TabletPCAwareForm 就是所有问题的关键。
因为我要论及一些小的、但非常有用的方法,所以我将指出 Utilities 类还包括一个用来确定应用程序当前是否运行在 Tablet PC 上的方法,我们适当地称其为 IsTabletPC 方法。其他的方法包括通知您用户是选择了右手用法还是选择了左手用法以及菜单显示的方法。Utilities 类中的所有内容都非常琐碎,并不值得一一描述,但令我高兴的是,这些内容都在该类中。
Context Tagging
SDK 文档的 What's New in the Tablet PC SDK Version 1.7 提到了 Context Tagging 工具,它附带于该 SDK 的 1.7 版,但所提及的内容甚至并没有开始讨论该工具的有用性。在应用程序中的所有编辑字段中,您可能已经获取了某种适当的限制文本或预期文本。默认情况下,只要用户将笔移动到输入区,您就可以获得完美的新的输入面板,用户可以在手写区域输入任何值。这对于您要让用户在其中输入销售联系人的讨论的多行编辑控件来说很好用,但是如果期望他们输入诸如 SMTP 格式的电子邮件地址之类的内容时,就不好用了。如果您通过 Regex 类将键盘的输入限制到特定的输入集,情况会更糟,但是用户可以在输入面板中书写任何所需内容。理想情况下,您希望以同样的方式限制通过键盘和输入面板进行的输入。
Context Tagging 工具使您能够非常容易地提供针对输入面板的限制输入,甚至无需更改应用程序代码。使用 Context Tagging 工具时,您可以将其指向应用程序中的各种编辑字段,然后为字段指定输入类型。您可以将输入限制到预定义的限制,例如 SMTP 电子邮件地址或特定的数字值。如果有特定的值,您还可以指定任意长度的词组列表。对于唯一的项,您还可以指定正则表达式。但是,输入面板所使用的正则表达式语言并不匹配 .NET Framework 中的 Regex 类。稍后我会讨论在为输入面板以编程方式设置正则表达式时的这些差异。当使用 Context Tagging 工具限制输入值时,这也称为设置输入范围。
Context Tagging 工具的输出是 .ctm 文件,该文件创建于与应用程序相同的目录中。当应用程序运行时,输入面板读取该文件。当用户使输入面板处于特殊的编辑控件中时,输入面板会对书写区域应用适当的输入限制。总的来说,它是一个相当完美的系统,可以非常轻松地使应用程序更有效地运行在 Tablet PC 上。
有关 Context Tagging 工具的详细信息,请参考 Tablet PC SDK 文档。我想指出关于 Context Tagging 工具的两个关键问题。第一,该文档对以下内容强调得不够:对于 Windows 窗体应用程序,您必须在窗体的控件上填写 AccessibleName 属性,以便 Context Tagging 工具和输入面板能准确判断输入限制应该应用到哪个控件。这可能需要您在窗体中仔细检查并设置这些值;但是您务必要设置它们,以便使屏幕读者和其他辅助功能软件可以更容易地使用您的应用程序。
第二个问题是,利用 Context Tagging 工具创建的 .ctm 文件是硬编码的。这对于那些不发生更改的字段(例如,电子邮件地址输入字段)来说非常好,但是如果您创建了一个词组列表,这可能会成为一个非常大的问题。目前,没有办法将值更改到或动态地添加到 .ctm 文件。虽然 .ctm 文件只是 XML 文件,但 Microsoft 已经非常明确地表示在以后的版本中更改该语法。这意味着,为了最好地支持 Tablet PC,您将需要考虑将对输入面板的少量编程支持添加到应用程序中。这正是 SetInputScope API 的用武之地。
使用 SetInputScope API
可能您已从其名称中获知了一些信息,SetInputScope 函数指出输入面板要在手写区域中接受的输入。实际上,SetInputScope 族是 Windows XP Tablet PC Edition 2005 在处理应用程序的 .ctm 文件时在内部使用的。如果您阅读过 Context Tagging 工具的文档,我讨论的某些术语听起来可能会非常熟悉。
SetInputScope API 的唯一的小问题就是它不是托管的 API。该 API 只是提供为从 MSCTF.DLL 导出的 Win32 API。因为以编程方式设置输入面板对于 Tablet PC 应用程序来说非常重要,所以我必须尽快纠正 Microsoft 的疏忽。我为 TipInputScope 类所创建的代码完全包装该 API。通过检查代码就可以发现,它用在所有操作系统上都是绝对安全的。这是因为代码如果没有运行在 Windows XP Tablet PC Edition 2005 上,就会变成一个简单的无操作指令。
在 TipInputScope 类中有四个入口点:两个重载 SetInputScope 和两个 SetInputScopes 函数。在所有函数中,第一个参数是您要在其中限制输入面板输入的窗口的句柄。第一个 SetInputScope 方法使用了一个附加参数,该参数是在文件的开头定义的 InputScopeType 枚举。该枚举中的值与 Win32 API 定义的值完全匹配。如果您快速浏览 InputScopeType 枚举的 XML 文档注释,就会发现很多您可能需要的常用项都已被定义。
利用将正则表达式字符串作为第二个参数的第二个 SetInputScope 方法,TipInputScope 类会变得更有意义一些。正如我前面所提到的那样,正则表达式与 Regex 类中的表达式有所不同。尽管基本思想是相同的,但实际的模式略有不同。当您试图提供正确的表达式时,某些差异可能是您的最爱,而某些则可能会让您觉得有点困难。不论您设置的正则表达式是什么类型,请牢记您在输入面板中设置的类型就是您希望输入面板所识别的准确字符串,只是输入面板,并不包括实际的编辑控件。最后,正则表达式字符串只能包含来自代码页 1252(拉丁语 1 西欧)的字符。
尽管包括所有这些 InputScopeType 枚举非常好,但是如果您希望合并它们以生成正则表达式的话,靠您自己重新创建它们将会非常烦琐。例如,如果您要在一个编辑控件中将输入面板输入限制为 SMTP 电子邮件地址和您要输入到 Exchange Server 2003 中的电子邮件名称,那么您要构建这样的表达式。幸好,可以传递到 SetInputScope API 的正则表达式使您可以包括这些 InputScopeType 枚举,让您的生活更轻松。如果您在括号内指定了 InputScopeType,并且在枚举值前使用了感叹号,就一切就绪了。因此,如果您设置了 (! IS URL) 的正则表达式,它与直接使用第一个 SetInputScope 过重载方法将 IS_URL 输入范围指定为 InputScopeType 是相同的。
扩展正则表达式以包括多个 InputSc