Chris Lovett
Microsoft
摘要:本文介绍新版本的 XML Notepad。该工具于 1998 年首次推出,但多年来始终一成不变。这一版本利用了 System.Xml 的优势并增加了一些新功能。
1998 年,发布了 Murray Low 用 C++ 编写的 XML Notepad。后来,由于它在支持 XML 标准方面表现落后,而我们又没有时间加以解决,因而被 MSDN 所抛弃。但事实证明 Murray 所做的这项工作是出色的,要求恢复 XML Notepad 的请求如潮水般涌向 MSDN,因此 MSDN 责成我来完成这个工具的更新换代。
对于那些了解这个工具初始版本的人而言,这个界面看起来应该很熟悉:左边的树视图与右边的节点文本视图同步,手动微调工具栏按钮可以在节点树中快速上下移动各节点(参见图 1)。由于距第一版的时间太过久远,我不得不利用了 System.Xml 中的一些强大内容并增加了一些原来版本所没有的点缀性功能:
• | 在树视图和文本视图中可以增量搜索 (CTRL+I),这样视图会随着搜索内容的输入而导航到最匹配的节点。 |
• | 剪切/复制/粘贴功能支持简单、可交互 XML 格式的完整命名空间。 |
• | 支持拖放功能,从而可以轻松操控树视图,甚至能跨越 XML Notepad 的不同实例或者从文件系统进行操控。 |
• | 对所有编辑操作可以进行无限次撤消/重做。 |
• | 对大型文本节点值进行在位编辑、弹出编辑和多行编辑,IntelliSense 以期望的元素和属性为基础。 |
• | 可通过选项对话框配置字体和颜色。 |
• | 完整查找/替换对话框支持 regex 和 XPath 表达式。 |
• | 在处理大型 XML 文档方面性能良好(在约一秒钟内能加载一篇 3MB 的文档)。 |
• | 在编辑的同时进行即时 XML 架构验证,错误和警告消息显示在任务列表窗口中。 |
• | 支持针对日期、日期时间、时间和颜色数据类型的自定义编辑器。 |
• | 提供显示 XSLT 转换结果的 HTML 查看器。 |
• | 内置有 XML Diff 工具。 |
设计与实现
在可下载程序包中,提供了核心 XmlNotepad dll 项目、用于构建 XmlNotepad.exe 的 Application 项目、用于 XML diff 功能的 XmlDiff 库项目、用于构建 .msi 安装程序的 Setup 项目、用于测试 XML Notepad 的 UnitTest 项目和用于使用 HTML Help Workshop 工具构建 Help.chm 文件的 Help 项目。
![.](https://i-blog.csdnimg.cn/blog_migrate/c5935a1d672937e90f086aad0069f7ce.png)
图 1. XML Notepad 界面
下图显示各主要 UI 类之间的关系。主窗体中包含所有的 UI 元素(从 TaskList 到主 XmlTreeView 以及两者之间的 Resizer);它是 FormSearch、FormSchemas、FormOption 和 FormAbout 等多个对话框的启动点。
![.](https://i-blog.csdnimg.cn/blog_migrate/fef213d10ed8b426e0ddb8b429ac530e.png)
图 2. 主要 UI 类
主要选项卡控件包括 XmlTreeView 和显示 XSL 输出的 XsltViewer。XmlTreeView 包含左侧的 TreeView 和右侧的 NodeTextView,并能协调两个视图间的滚动。TreeView 和 NodeTextView 都使用 TextEditorOverlay 组件提供相同的 IntelliSense 编辑功能。XmlTreeView 实现了 IFindTarget,“查找”对话框 (FormSearch) 用它来实现查找/替换功能。
![.](https://i-blog.csdnimg.cn/blog_migrate/780ba0e15cf24af3ad4e71036798d4bd.png)
图 3.“查找”对话框
“查找”对话框支持全文本、regex 或 XPath 表达式查找,并能按名称或值进行筛选。这里显示的是 XPath 搜索,它还提供了一个表格,用于输入查询中所使用的命名空间前缀映射。
验证、IntelliSense 与自定义编辑器
IntelliSense 是新增的最强大功能,它由通过 SchemaCache 提供的 XML 架构信息驱动。例如,如果您的元素或属性由一个 XSD simpleType 定义,此 simpleType 又包含枚举约束面的列表,那么您将获得如下的一个下拉列表:
![.](https://i-blog.csdnimg.cn/blog_migrate/b5290de78f12359bbb49305a4508c54b.png)
图 4. 下拉列表值
其工作方式是这样的:检查器会跟踪每个编辑操作,以在 TaskList 中检查文档和报表错误。这一过程也会将每个元素和属性的 System.Xml.Schema.XmlSchemaType 信息放入 XmlDocument 中;然后,在编辑该节点的值时,TextEditorOverlay 会使用 XmlIntelliSenseProvider 返回可能值列表。在上面的例子中,它返回了来自 simpleType 枚举约束面的值。对于树视图中的元素名 IntelliSense,XmlIntelliSenseProvider 再次调用检查器,在 System.Xml.Schema.XmlSchemaValidator 上捕获 GetExpectedParticles 和 GetExpectedAttributes 并用其来提供 IntelliSense。
![.](https://i-blog.csdnimg.cn/blog_migrate/158a0c30dac7e70ba207d412d0f641d5.png)
图 5. 树视图中的 IntelliSense
TextEditorOverlay 还支持 DateTimeEditor、UriBuilder 或 ColorBuilder 等自定义编辑器。自定义编辑器分为两种类型:IXmlEditors 和 IXmlBuidlers。前者是取代默认文本框的内嵌式编辑器,后者是弹出式对话框(如 OpenFileDialog 或 ColorDialog)。编辑器类型是从架构类型信息派生而来的,其中“xs:date”、“xs:time”和“xs:datetime”产生 DateTimeEditor,“xs:anyURI”产生 UriBuilder。您也可以使用“http://schemas.microsoft.com/Visual-Studio-IntelliSense”命名空间中的自定义“vs:builder”属性注释此架构。有关详细信息,请参见帮助内容。
无限次撤消/重做
为了实现撤消/重做,我对 Undo 和 Redo 方法采用了常见的命令对象设计模式。UndoManager 负责将这些对象收集在一个列表中。而 UndoManager 的状态控制“撤消”/“重做”菜单项的启用/禁用状态。当用户选择“撤消”菜单项时,会以活动命令调用 Undo 方法,并将该命令压入 Redo 堆栈。
![.](https://i-blog.csdnimg.cn/blog_migrate/40a08ca8e153f1211d444130cbb61761.png)
图 6. 树视图中的“撤消/重做”
编辑器中的某些操作引发了树中的许多编辑操作,包括全部替换操作和命名空间属性值编辑操作。(更改命名空间属性值时,绑定到该命名空间的每个 XmlNode 都需要以一个新的命名空间 URI 重新构建,这显然会影响到树中的许多节点!)因此,为了将这些操作融入一个基本的重做操作,利用了一个包含更小编辑命令列表的 CompoundCommand 对象,此 CompoundCommand 被放入 UndoManager 中。
其他更简单的命令对象包括以下项目,它们全部对 XmlTreeNode 和 XmlNode 对象进行操作:
![.](https://i-blog.csdnimg.cn/blog_migrate/2ac04dfd7a32e3c46a72e4f764b28436.png)
图 7. 树视图中的命令对象
PasteCommand 是一个特殊命令,因为它将随机 XML 文本从剪贴板取下,并在树中当前所选元素的环境中进行解析,从而继承了作用域中的命名空间。助手类 TreeData 使用以 XmlParserContext 作为输入的特殊 XmlTextReader 构造函数。
单元测试
我必须将测试新 XML Notepad 的工作做好,我说服自己使用了新的 Visual Studio 2005 UnitTest 项目。UnitTest 可以从 Visual Studio 中新“测试”菜单下的 TestManager 运行,它可以启动 XML Notepad 并执行我所编写的 15 个测试方法。
![.](https://i-blog.csdnimg.cn/blog_migrate/65570d1913b95b027d8a190c5d4279ec.png)
图 8. UnitTest
测试完毕时,可以在“测试结果”窗口单击右键并选择“代码覆盖率结果”。这样,程序会汇总出每个功能所覆盖的块数。双击列表中的行可以显示一个彩色编码的源代码视图,用以准确显示所覆盖的代码。这种方式使我真正轻松地获得了高达 85% 的代码覆盖率。
唯一需要注意的是,当您在调试程序下运行这些测试时不会获得代码覆盖率,UnitTest 项目会在解决方案旁创建一个称为“localtestrun.testrunconfig”或“XmlNotepad.vsmdi”的文件,这个文件不能丢失。否则,就将无法获得代码覆盖率。除此之外,我不得不说这个新的 UnitTest 是 Visual Studio 2005 中最棒的新功能之一。