在前面的文章中,我们在向导的帮助下创建了一些小的VSPackages。在第五讲中我们整理了VSX的一些思路和概念,深入了解了packages是如何工作的以及服务的机制。在这篇文章中我们继续前进。
为了创建创建“容易编写和理解”的代码,从本文开始,我们开始创建一个工具集示例Package。我计划用至少如下三个主题来讨论:
- 序幕:创建示例package的第一部分,它是这个工具集的基础。在这篇中我们将手动添加菜单命令来探讨一下command table configuration文件。
- 完成示例:在这篇文章里,我们创建示例package的第二部分。手动添加一个自定义Tool Window,并且探索一下output window。
- 重构:我们修改package,提取一些在package开发中公共的可复用的类型。
在这个系列中,我们会创建一个工具窗,它可以对两个整数进行算术运算。
写这个系列的目的,并不是为了实现这个工具集的功能,而是为了熟悉创建类似应用的步骤。通过创建这个简单的工具集,可以使我们更熟悉package的开发,这要比直接讲解VS SDK中的interop程序集和MPF类更容易理解。
创建一个空的VSPackage
我们先创建一个空的VSPackage。因为在前面的文章中我说明了创建空package的步骤,所以在这里就省略掉截图了。选择Visual Studio Integration Package类型的项目,该项目模板会弹出我们的朋友—VSPackage向导。命名工程为StartupToolset。选择C#语言,根据下面的图片填写基本的信息:
在下一个向导页面不要勾选Menu command, Tool window 和 Command editor中的任何一个(因为我们要手动添加它们);再下一步也不要勾选任何测试项目,最后点击完成。向导生成了一个空package的项目。运行后检查Help|About对话框,以确认StartupToolset包是否在VS实验室环境下被正确的注册了。(注意:为了减少代码量和提高可读性,这个时候我删除了向导生成的注释,你当然也可以这么做,但这些注释有利于理解代码的含义,很值得一读)
在前面的文章中我们通过向导添加了菜单命令和工具窗口。在这个例子中我们将手动添加。
手动添加新的菜单项
为了显示一个菜单项,我们要这样做:
- 为命令创建一个ID、名字和显示的文本,该命令用于显示tool window
- 创建.vsct文件来设置所谓的command table configuration
- 为package类添加ProvideMenuAttribute
- 设置.vsct文件的Build Action
- 创建菜单项的事件处理函数
- 建立命令和该事件处理函数的关联
什么是command table configuration文件?
在之前的文章中,我提到过VSPackages是“按需加载(on-demand loaded)”的,当packages中的对象将要被创建,或者其中的服务将要被使用的时候IDE才将他们装载进内存。这听起来不错,不过有个问题:如果对象表示了菜单或者工具栏对象,并且和package的源代码编译在一起,那么IDE不得不仅仅为了展示这些UI而加载这个package,哪怕这个package并没有被使用。为了显示这些跟package相关的菜单和工具栏(而避免上述情况的发生),这些对象被设计成package的二进制资源。当package被注册(通过regpkg.exe)时,这些资源被提取并分开存放,这样Visual Studio就可以在不加载package的情况下显示这些资源。
command table configuration文件是要实现这个策略的关键。这个文件的职责是定义与命令相关的UI元素。当我们编译一个package时,command table configuration文件转换成一个cto文件(command table output file),并作为一个资源,编译到package中。
在vs2005版本的VS SDK中,使用一种文本形式的command table configuration文件(.ctc后缀)。理解和编辑.ctc文件不是件容易的事。随着Visual Studio 2008 SDK的发布,微软创建了一种基于XML的文件格式(.vsct: Visual Studio Command Table),并且配以一种新的编译器(VSCTCompile)来将.vsct文件编译成.cto文件。
vsct文件主要的优点是它像其他xml文件一样,很容易编辑,并且沿袭了XML所有的好的特性,比如自动生成结束标签和基于vsct XML 架构的智能感知。尽管仍然可以使用ctc文件,但微软推荐使用vsct文件。
第一步:增加一个command ID
为Command指定ID的目的,是为了将这个package里的命令项和Visual Studio中的命令项或其他package中的加以区分。Command是以ID作为标识的UI相关的对象,就像菜单项或者bitmaps那样。UI相关对象的ID是分层次的,由一个GUID和32位无符号整数组成。GUID表示逻辑上拥有这些UI对象的容器,而32位无符号数则用来在容器内部区分不同的对象。
向导生成的Guids.cs文件包含了一个用于标识package的GUID和一个用于标识命令集(command set)的GUID:
using System;
namespace MyCompany.StartupToolset
3: {
class GuidList
5: {
;
;
new Guid(guidStartupToolsetCmdSetString);
9: }
10: }