这几天牟足了力气写,快写完了。今天Boss说下周一开始授课。Faint马上又开始做ppt。晚上回家又堵车。不过现在终于清静下来了。今天帖的是关于Add-in控制DTE菜单、按钮的部分。不多,也比较简单。
这里下载
我试试把内容帖上来。不知道效果如何。
AddIn编程的好处就是我们可以使用自动化对象模型,在我们不必知道.NET内部的实现机制的前提下操纵.NET IDE的控件,完成一些既存功能。在这部分,我们将按照不同的对象单独介绍各种自动化对象模型的使用方法。
需要注意的是,我们所有的DTE模型都是从EnvDTE里面得到的,这也就是为什么前面我们说EnvDTE是我们AddIn编程的基础。此后的部分,如果没有特别指明,那么相关的对象都是在EnvDTE命名空间下的。而为了方便起见,我们将按照文章的层次在我们先前的AddIn工程下面建立相应的文件夹、文件和类。而从Connect.vb文件调用这些类的代码就不再明确说明。
DTE使用的菜单是标准的Office系列菜单,这和我们使用Windows.Forms.MenuItem菜单是不一样的。Office系列的菜单实际上和工具条上面的按钮是同一个类,但是有所不同的是,顶级菜单使用的是PopupCommandBar,而其他的菜单都是使用的普通的CommandBarButton。
首先我们声明我们需要的所有菜单项目,包括一个顶级菜单项“MyAddIn Command”;下属的三个子菜单“Tool Window”、“Source Code”和“Others”。那么下面的声明是必须的。
Private m_cpopMenu As Microsoft.Office.Core.CommandBarPopup
Private WithEvents m_miToolWnd As Microsoft.Office.Core.CommandBarButton
Private WithEvents m_miSourceCode As Microsoft.Office.Core.CommandBarButton
Private WithEvents m_miOthers As Microsoft.Office.Core.CommandBarButton
【注意】
Ø 子菜单项需要加入WithEvents关键字,这样这些菜单项才可能得到消息相应,完成我们的选择操作,启动相关的代码。
Ø 操作DTE的菜单或工具条,必须要在工程共加入Office的引用。当然前提是开发的计算机上面已经安装了MS Office™。
|
现在我们的声明部分已经结束,我们需要实例化这些菜单。在写入这些代码的时候可能已经发现,这些菜单的类型并不是类,而是接口。接口意味着我们不能简单地通过New方法实例化这些对象。所幸的是,DTE已经为我们预留好了接口,让我们可以方便的实例化自己的菜单。
在DTE上面添加主菜单项目,通过DTE.CommandBars.ActiveMenuBar.Controls.Add这个方法就可以简单地实现了。Add方法主要使用第一个参数Type,这个参数确定了这个要添加的项目类型。这里我们只能选择msoControlPopup这种类型。随后我们通过设置Caption属性设置显示在DTE顶级菜单上面的名字。Add的返回值赋给顶级菜单的变量,这样我们就通过这种方法实例化了定义菜单。
'Init main menu in DTE
m_cpopMenu = m_DTE.CommandBars.ActiveMenuBar.Controls.Add(Microsoft.Office.Core.MsoControlType.msoControlPopup)
m_cpopMenu.Caption = "MyAddIn Command"
上面的代码就是实例化顶级菜单,这里m_DTE是从Connect.vb里面传递进来的EnvDTE实例。
实例化子菜单项目主要通过顶级菜单的Microsoft.Office.Core.CommandBarControls.Add方法来实现。与实例化顶级菜单稍有不同,子菜单的实例化参数也是设定了这个菜单的类型,而菜单的名字等等信息要在实例化之后设定。
'Init [Tool WIndow] menu in main menu that had been created
m_miToolWnd = m_cpopMenu.Controls.Add(Microsoft.Office.Core.MsoControlType.msoControlButton)
m_miToolWnd.Caption = "Tool Window (&T)" 'Set the display caption
m_miToolWnd.FaceId = 642 'Set the icon of the menu
m_miToolWnd.BeginGroup = False 'Set the divition line up of the menu
上面的代码设定了Action菜单项目,通过代码我们看到,首先我们通过顶级菜单的Controls.Add命令生成它的下一次菜单项,而类型通过参数指定。随后我们分别对子菜单项的Caption、FaceId、BeginGroup进行设定。
Ø Caption属性:设定菜单显示的名字。支持“&”符号的快捷键设定。
Ø FaceId属性:设定菜单左侧显示的图标(如果不设置就没有图标)。这种设置图标的方法不支持用户自己的图标,只能够从Office系统提供的将近2000个图标里面选择相应的ID。这也是最简单的设置图标的办法。
Ø BeginGroup属性:设置这个菜单项的上边是不是要加入一个分割线。在Windows应用程序中,我们使用一个Text属性是减号的菜单来代替分割线,但是Office菜单里面,我们是通过这样的属性才实现分割线的。
同样的方法,我们可以设定其余的两个菜单项目。然后我们将我们的代码填充到我们的AddIn程序中,运行就可以看到我们做成的菜单了。
【提示】
Ø 不要妄图在MSDN里面检索到关于DTE菜单和工具条的帮助。这些对象是Office所有的。所以如果你需要帮助,参考Office的SDK。
【未知】
Ø 通过AcitveMenuBar.Controls.Add的第四个参数Before可以设置这个顶级菜单项的添加位置,将一个存在的顶级菜单设定为Before参数,那么这个新的菜单应该添加在Before菜单的前面。但是目前我没有设置成功。上面的代码只能将新的菜单添加在DTE所有菜单的最后。
Ø CommandBarButton.ShortcutText可以设定ShortCut的文字,只是一个String类型的属性。也就是说它只能设定显示的内容,不能完成快捷键的设定,设定之后快捷键仍旧无效。目前并不清楚如何设置快捷键。
|
菜单的目的就是为了响应用户的消息,启动相关的程序。由于我们此前已经将菜单项目设定为可以响应消息的模式,所以我们响应DTE菜单事件的操作和Windows应用程序基本一致。
Private Sub m_miToolWnd_Click(ByVal Ctrl As Microsoft.Office.Core.CommandBarButton, ByRef CancelDefault As Boolean) Handles m_miToolWnd.Click
MsgBox("Tool window menu clicked.")
End Sub
这段代码就是相应ToolWindow菜单的一个Handle函数,为了简单,我们只是进行了一个操作。但是它足以说明问题。同样的,我们可以为每一个菜单项目Handle一个函数,然后写入我们的代码,和普通的Windows应用程序没有任何区别。
与Windows应用程序不同,我们的AddIn程序不是一个独立的程序,也就是说我们可能退出了AddIn程序,但是宿主程序仍然运行。这时候我们在宿主程序里面添加的所用控件和窗口都不会随着我们AddIn程序的退出而被丢弃。所以AddIn很重要的一个设计原则就是需要考虑资源的回收问题。现在的菜单项目就反映这一点。
只是上面的程序,我们在关闭AddIn之后,这个菜单依旧存在于DTE环境里面。而如果我们再次打开AddIn,又会建立一个新的完全一致的菜单项目。这显然不是我们希望看到的。所以我们要做的事情就是在AddIn关闭的时候卸载这个菜单。还记得OnDisconnection函数么,我们就是要在这个函数里面进行所有的卸载操作,当然现在只有卸载菜单的操作。
卸载菜单很简单。通过CommandBarControl.Delete方法就可以实现。
Public Sub Dispose()
m_miToolWnd.Delete()
m_miSourceCode.Delete()
m_miOthers.Delete()
m_cpopMenu.Delete()
End Sub
上面的函数,只要我们在OnDsiconnection函数里面调用了,我们的程序就会自动地在退出的时候卸载这个菜单项目。
【提示】
Ø 并没有明确的说明,删除父菜单不会自动删除子菜单,但是个人认为使用“从小到大”的原则逐个删除可能更加安全。况且这样逐个删除在效率上并没有比只删除顶级菜单慢。
|
前面已经介绍了,在Office控件里面,菜单和工具条没有明确的界限,我们甚至可以说,Office的菜单和工具条是同样的东西,只是表现形式不一样。对于工具条的处理,和处理菜单基本一致。所以下面我们会将重点放在他们不相一致的地方。
与菜单不同,工具条的声明是一个工具条对象和多个工具条按钮对象。这很类似于Windows应用程序的CommandBar控件。
Private m_cmdBar As Microsoft.Office.Core.CommandBar
Private WithEvents m_cmdInsert As Microsoft.Office.Core.CommandBarButton
Private WithEvents m_cmdDelete As Microsoft.Office.Core.CommandBarButton
Private WithEvents m_cmdFormat As Microsoft.Office.Core.CommandBarButton
实例化工具条和按钮,和菜单基本一致,都是使用接口声明的,所以我们只能通过DTE给我们提供的方法得到我们想要的新控件。只是在实例化工具条的时候,我们要通过DTE.CommandBar.Add方法添加。而且参数我们设定的就是这个工具条的名称。
'Init command bar in DTE
m_cmdBar = m_DTE.CommandBars.Add("My AddIn Command")
下面的代码是实例化一个按钮。和菜单不同,按钮的Caption属性就是这个按钮的ToolTip属性。当然如果你的DTE环境设定为“显示按钮图标和文字”或者“只显示按钮文字”的话,这个属性将会显示成按钮上面的文字。
'Init [Insert] menu in command bar that had been created
m_cmdInsert = m_cmdBar.Controls.Add(Microsoft.Office.Core.MsoControlType.msoControlButton)
m_cmdInsert.Caption = "Insert" 'Set the display caption
m_cmdInsert.FaceId = 534 'Set the icon of the menu
m_cmdInsert.BeginGroup = False 'Set the divition line up of the menu
由于工具条在DTE环境下可以有很多,而且每个工具条都可以停靠到DTE四周的各个位置,所以和菜单不同,我们还可以设定这个工具条默认的停靠位置。另外工具条控件默认是不会显示的,所以和菜单不同,我们还必须手动设置CommandBar的Visible属性。
'Visible the command bar
m_cmdBar.Visible = True
'Set the command bar position in DTE
m_cmdBar.Position = Microsoft.Office.Core.MsoBarPosition.msoBarTop
上面的代码显示工具条,并且将这个工具条默认停靠到DTE的上方。如果不进行这个操作,工具条将会浮在DTE环境之上。而其他的属性,工具条控件和菜单控件完全一致。
和菜单一样,简单的通过Handle函数就可以响应按钮事件。
Private Sub m_cmdInsert_Click(ByVal Ctrl As Microsoft.Office.Core.CommandBarButton, ByRef CancelDefault As Boolean) Handles m_cmdInsert.Click
MsgBox("This is insert command called from command bar button.")
End Sub
和菜单一样,在AddIn结束的时候,也应该卸载掉建立的工具条和内部的按钮控件。
Public Sub Dispose()
m_cmdInsert.Delete()
m_cmdDelete.Delete()
m_cmdFormat.Delete()
m_cmdBar.Delete()
End Sub