本文分2部分 第一为自定义多项目模板 第二为vs add-in开发
效果图
1.自定义模板
2. 工具菜单
3.窗口
4.工程
5.文件
…
一. 多项目模板
单项目模板做起来很简单 选中一个项目在文件一栏中选中导出模板
然后选择项目模板
在最后一项向导会给出你的输出路径,一般都是系统的用户文档路径+\Visual Studio 2010\My Exported Templates
在对应目录下会生成你对应的项目模板压缩包
我们生成2个项目的模板文件 然后做一个多项目模板
我们解压2个模板文件并且放进一个新建的名称为MaoyaTemplates的文件夹 剪切到Visual Studio 2010\Templates\ProjectTemplates路径下
新建模板文件MyTemplate.vstemplate
根据要定义的模板内容 修改文件
然后将整个文件夹压缩成zip文件即可 在TemplateData可以定义一些自己需要的信息 例如icon定义你的模板图标 把图标文件放在相对路径即可
然后重新打开vs 即可看到刚才作成的模板
打开以后就是定义的2个demo工程 相当简单
这样就不用每次来新项目都去copy代码了
二 vs插件
概念性的东西可以参考 http://msdn.microsoft.com/zh-cn/library/bb384200.aspx
vs插件可以帮助或者优化你在vs开发过程的大部分窗口 它可以针对文件 针对项目 针对窗口 甚至针对不同的后缀做不同的处理
下面图文+代码介绍开发步骤(vs2010)
选择插件项目 进入向导根据自己的场景选择即可
中间向导过程略过 最后一步
完成向导后 默认工程框架如下
最关键的就是connect.cs文件
Connect 继承了2个类 IDTExtensibility2, IDTCommandTarget
IDTExtensibility2 包含在实现接口时用作事件的方法。 每当发生影响某个外接程序的事件时(如加载或卸载该外接程序时)以及对该外接程序进行任何更改时,Visual Studio 都会调用这些方法。
IDTCommandTarget 接口使开发者得以在环境中实现命名命令。 并且以定义命令状态或执行命令。
每个方法的描述
中文版可以参考msdn
http://msdn.microsoft.com/zh-cn/library/extensibility.idtextensibility2.aspx
http://msdn.microsoft.com/zh-cn/library/envdte.idtcommandtarget.aspx
其中我们作为入门开发 需要关注得的是
OnConnection 为IDTExtensibility2的 main 方法,这是因为每次加载外接程序时都会调用该方法。 该方法为外接程序在加载时的入口点,因此可以将要在外接程序启动时运行的任何代码放置在此处(或调用任何其他函数)。
1
2
3
4
5
6
|
void OnConnection( Object Application, ext_ConnectMode ConnectMode, Object AddInInst, ref Array custom ) |
参数
Application
类型:System.Object
对集成开发环境 (IDE) 的一个实例 (DTE) 的引用,该实例是 Visual Studio 自动化模型的根对象。
ConnectMode
类型:Extensibility.ext_ConnectMode
一个 ext_ConnectMode 枚举值,指示向 Visual Studio 中加载外接程序的方式。
AddInInst
类型:System.Object
一个对外接程序自己的实例的 AddIn 引用。 此引用存储起来以供以后使用(如用于确定外接程序的父集合)。
custom
类型:System.Array
一个空数组,可用来传递在外接程序中使用的特定于主机的数据。
下面我们开始建一个工具栏(Tools)下的菜单
首先判断加载方式
1
2
|
if (connectMode == ext_ConnectMode.ext_cm_UISetup || connectMode == ext_ConnectMode.ext_cm_Startup || connectMode == ext_ConnectMode.ext_cm_AfterStartup) |
这里的ConnectMode来自一个枚举 参考msdn http://msdn.microsoft.com/zh-cn/library/extensibility.ext_connectmode.aspx
然后添加注册插件命令和窗口 关键代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
var toolsBar = commandBars[Tools]; if (toolsBar != null ) { toolsSubPopup = (CommandBarPopup)toolsBar.Controls.Add(MsoControlType.msoControlPopup, Type.Missing, Type.Missing, 1, true ); toolsSubPopup.Caption = MainMenuName; CommandBar toolsSubBar = toolsSubPopup.CommandBar; Command command = commands.AddNamedCommand2( _addInInstance, MenuName1, MenuName1, MenuName1, true , 190, ref contextGUIDS, ( int )vsCommandStatus.vsCommandStatusUnsupported + ( int )vsCommandStatus.vsCommandStatusEnabled); command.AddControl(toolsSubBar); command = commands.AddNamedCommand2( _addInInstance, MenuName2, MenuName2, MenuName2, true , 190, ref contextGUIDS, ( int )vsCommandStatus.vsCommandStatusUnsupported + ( int )vsCommandStatus.vsCommandStatusEnabled); command.AddControl(toolsSubBar, 2); } |
commandBars[Tools]表示命令栏中的工具栏,这里Tools实际为定义的字符串“Tools”。
CommandBarPopup表示弹出命令条。
_addInInstance表示当前插件实例。
3个Menuname分别表示命令名称的缩写形式,UI显示的名称以及当用户将鼠标指针悬停在任何绑定到新命令的控件上时所显示的文本。
true表示下面的190 为 Microsoft Office 位图的 ID。
ContextUIGUIDs表示GUID 确定哪些环境上下文 (即调试模式,设计模式,等等) 显示命令。
vsCommandStatusValue确定命令的禁用条件是不可见或禁用,当您提供一个 ContextUIGUIDs 参数,并且都不是当前活动的。
AddNamedCommand2 创建命名命令,该命令由环境保存,并且在下次环境启动时(无论是否加载外接程序)可用。 外接程序以后可以通过响应 QueryStatus 方法来更改 ButtonText 名称。 如果文本以 # 开头,则该字符串的其余部分是一个整数,该整数表示外接程序已注册的附属 DLL 中的资源 ID。有两个默认值顺序状态:该默认值启用了状态和默认值可见性状态。 这些默认状态很重要,如果找不到命令处理程序 (因为该元素未加载或不实现 IDTCommandTarget)。 如果您的组件加载并实现 IDTCommandTarget,默认值不适用。
Command.AddControl创建此命令的持久性命令栏控件。后面的数字表示菜单位置。
详细参考http://msdn.microsoft.com/zh-cn/library/envdte80.commands2.addnamedcommand2.aspx
这样基本上就完成了下图所示的效果
但是我们需要对2个菜单进行功能型的开发
QueryStatus方法返回指定的已命名命令的当前状态(启用、禁用、隐藏等)。
在此方法添加片段
1
2
3
4
5
6
7
8
|
if (neededText == vsCommandStatusTextWanted.vsCommandStatusTextWantedNone) { if (commandName == GetCommandName(MainMenuName)) { status = vsCommandStatus.vsCommandStatusSupported | vsCommandStatus.vsCommandStatusEnabled; return ; } } |
vsCommandStatusSupported表示命令在此上下文中受支持,vsCommandStatusEnabled表示命令当前处于启用状态。
Exec方法执行指定的命名命令。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
if (executeOption == vsCommandExecOption.vsCommandExecOptionDoDefault) { if (commandName == GetCommandName(MainMenuName)) { MessageBox.Show( "This is maoya test" ); handled = true ; return ; } if (commandName == GetCommandName(MenuName1)) { MessageBox.Show( "This is maoya Test1 test" ); handled = true ; return ; } if (commandName == GetCommandName(MenuName2)) { MessageBox.Show( "This is maoya Test2 test" ); handled = true ; return ; } } |
这里实现的很简单就是弹出个winform。你可以在这里实现很丰富的功能。
上面是工具栏的实现,下面介绍个操作窗口的
Onconnection:
1
2
3
4
5
6
7
8
9
10
|
CommandBar mdiCommandBar = commandBars[ "Easy MDI Document Window" ]; if (mdiCommandBar != null ) { Command mdiCmd = commands.AddNamedCommand2(_addInInstance, MenuName3, MenuName3, MenuName3, false , 0, ref contextGUIDS); mdiCmd.AddControl(mdiCommandBar); } |
QueryStatus:
1
2
3
4
5
|
if (commandName == GetCommandName(MenuName3) || commandName == GetCommandName(MenuName4) || commandName == GetCommandName(MenuName5)) { status = vsCommandStatus.vsCommandStatusSupported | vsCommandStatus.vsCommandStatusEnabled; return ; } |
Exec 功能是新建一个文件
1
2
3
4
5
6
|
if (commandName == GetCommandName(MenuName3)) { _applicationObject.ExecuteCommand( "File.NewFile" , string .Empty); handled = true ; return ; } |
下面我们在做一个针对项目工程的自定义按钮
OnConnection:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
CommandBar projectBar = commandBars[ "Project" ]; if (projectBar != null ) { projectSubPopup = (CommandBarPopup)projectBar.Controls.Add(MsoControlType.msoControlPopup, Type.Missing, Type.Missing, 1, true ); projectSubPopup.Caption = MainMenuName; CommandBar projectSubBar = projectSubPopup.CommandBar; Command projectCommand = commands.AddNamedCommand2( _addInInstance, MenuName4, MenuName4, MenuName4, true , 190, ref contextGUIDS, ( int )vsCommandStatus.vsCommandStatusUnsupported + ( int )vsCommandStatus.vsCommandStatusEnabled); if (projectSubBar != null ) { projectCommand.AddControl(projectSubBar, 1); } } |
QueryStatus:
1
2
3
4
5
|
if (commandName == GetCommandName(MenuName3) || commandName == GetCommandName(MenuName4) || commandName == GetCommandName(MenuName5)) { status = vsCommandStatus.vsCommandStatusSupported | vsCommandStatus.vsCommandStatusEnabled; return ; } |
Exec 这里可以抓到你选择的是哪一个项目 包括名称 路径等等 根据具体的需求再来处理对应的逻辑
1
2
3
4
5
6
|
if (commandName == GetCommandName(MenuName4)) { MessageBox.Show( string .Format( "This is maoya {0} test,/r/n该项目名称为{1}" , MenuName4, _applicationObject.SelectedItems.Item(1).Name)); handled = true ; return ; } |
类似的还有操作文件,代码就不贴了 直接上图
值得一提你可以利用文件后缀等做一些特殊操作
在QueryStatus方法中添加
1
2
3
4
5
6
7
8
9
|
if (commandName == GetCommandName(MenuName6)) { var uiHierarchy = (UIHierarchy)_applicationObject.Windows.Item( Constants.vsWindowKindSolutionExplorer).Object; if (( from UIHierarchyItem item in (Array)uiHierarchy.SelectedItems select item.Name).Any(itemName => itemName.IndexOf( ".config" ) > -1)) { status = vsCommandStatus.vsCommandStatusEnabled | vsCommandStatus.vsCommandStatusSupported; } } |