| 1 关于插件系统的文档 这是描述SharpDevelop插件系统的文档.如果你打算为SD写一个插件,你应该阅读一下“AddInBuildingGuide” 学习如何组织你的工程。 本文档包括ICSharpCode.Core中通用的插件系统和SD的公共扩展点(common extensions points for SharpDevelop). 2 AddIn Tree 一个插件安装包(译者注:其实就是.sdaddin文件,这个文件是由.addin和.dll压缩后把扩展名改为.sdaddin而得)一般至少包括两个文件:插件描述文件即.addin,和动态链接库.dll,当然也可能包含其他的文件和类库。 插件描述文件包含了插件的描述信息,并且在SD启动的时候会被加载进来,根据文件的内容,SD会把它放到插件数里。 插件数是一棵"binds them all"(译者注:不知道啥意思,估计是说绑定所有插件的意思)的树。. 它的结构像文件系统,如果你想访问节点SubNode2,就必须像这样指定节点的位置:/Path1/SubPath1/Node1/SubNode2.我们看到Node1就像一个路径,但是我们稍后就会看到路径与节点的不同。 从现在开始,我们会把那些路径中包含定义和行为的部分称为节点。(we will just say that nodes are paths that contain definitions of behavior). 对于插件树最常用的一种用法是扩展菜单和Toolbar。当SD想要创建一个菜单或Toolbar的时候,它就会明确的指定插件的路径。路径 “/SharpDevelop/Workbench/MainMenu” 下包含主菜单的项。路径“/SharpDevelop/ViewContent/Browser/Toolbar” 包含Toolbar中用于导航的项 (比如Startpage, 帮助等.). 2.1 AddIn Definition 插件数中的每个节点都包含一个Codon(密码子). 在ICSharpCode.Core 中有一个AddInTreeNode的类,它包含一个Codon的属性,对于路径来说它的值是Null,对于一个节点的Codon来说它就是插件的扩展点(points to a Codon instance for nodes). 让我们看看如何用XML定义一个含Codon的节点吧: <MenuItem id = "Build" label = "${res:XML.MainMenu.BuildMenu.BuildSolution}" shortcut = "F8" icon = "Icons.16x16.BuildCombine" class = "ICSharpCode.SharpDevelop.Project.Commands.Build"/> 当插件树加载后,它会根据Codon中的(译者注:即上面的MenuItem)class attribute所指向的类创建一个对象,这个对象的Name属性值被设为 “MenuItem”,ID属性值设为“Build”.其他的attributes中的信息被保存在一个叫做“Properties” 的数据包里(就像一个Hashtable). 对于MenuItem的Codon包含的信息有:label, shortcut, icon 和当此menu被点击是要执行的类的全名称(译者注:即上面的class attribute,每个class都会实现ICommand接口,当按钮被点击时会调用接口的Run函数)。
对于插件树比较重要的是把所有的插件整合在一起。例如, 插件startpage的插件文件StartPage.addin包含: <Path name = "/SharpDevelop/Workbench/MainMenu/View"> <MenuItem id = "ShowStartPage" insertafter = "ViewItemsSeparator" insertbefore = "FullScreen" label = "${res:XML.MainMenu.ViewMenu.ShowStartPage}" icon = "Icons.16x16.BrowserWindow" class = "ICSharpCode.StartPage.ShowStartPageCommand"/> </Path> 路径"/SharpDevelop/Workbench/MainMenu/View" 在SharpDevelop 的主插件文件(译者注:SharpDevelop.addin)和 StartPage's AddIn文件里都被定义了. 当加载这些插件文件时, ICSharpCode.Core 会把这些文件里有相同路径的内容组织在一起并插到树中。上面的codon的insertafter and insertbefore 两个attributes是比较特别的,它们指定了这一个MenuItem将要插入的位置 |
| 2.2 The Runtime Section 每个插件定义文件都包含一个定义插件对象的类,我们可以在插件文件的头部的runtime 段找到关于类定义的信息,每个Codon都会根据这个runtime段定义的信息来创建插件对象。插件文件头部包含如下attributes: Name, Author, Copyright, URL of the addin homepage, Description, Version. 它们的值被保存在AddIn类中的properties里面(译者注:上面提到的那个类似Hashtable的数据包)。 The values are stored in properties of the AddIn class. Runtime段像这样定义的: <Runtime> <Import assembly = "CSharpBinding.dll"/> <Import assembly = ":ICSharpCode.SharpDevelop"/> </Runtime> Import元素的数据被保存在AddIn类里面的RunTimes属性里.AddIn类有一个函数 “CreateObject”. 当用MenuItem的Class创建一个对象时CreateObject函数就会被调用.一般情况下, 当item被点击时CreateObject才被调用(译者注:并不是每个插件被加载时就会立即创建插件中class的对象,而是当此插件被用到时才创建的。比如MenuItem,如果你从来就不点击一下,那么这个类就不会被创建。). CreateObject 会遍历所有导入的assemblies(按照Import的顺序)来尝试创建此插件对象。 被导入的assemblies 只有在CreateObject 才第一次被加载进来.这对于SD的启动性能是一个很大的改进。 CreateObject查询导入的assemblies的所有类来创建对象,当你想要使用SharpDevelop 的assembly中的类时,(不如一个通用的Command撤销等。), 你也必须导入这个assembly. 当这个assembly被多个插件文件引用时同一个assembly不会被加载对次。既然插件可以放在任一个目录下而不是必须在一个指定的路径,那就不必明确的指定ICSharpCode.SharpDevelop.dll的相对路径,因为有一个特别的方式来引用SD在主目录下的assemblies,当assembly attribute以一个冒号开始 (<Import assembly = ":ICSharpCode.SharpDevelop"/>), SharpDevelop 会以“Assembly.Load”的方式来加载此assembly,而不是一般的“Assembly.LoadFrom”.这就是为什么不必须SD的assembly明确路径,但是和一般的导入方式有一点重要的区别:那就是这种情况下不需要加”.dll”扩展名。
在runtime段里也可以包含doozer和condition的定义, SD会读取doozer和conditions部分的信息并注册到SD里。 |
| 2.3 Doozers 现在有这样一个问题: 一个Codon是如何变成 System.Windows.Forms.MenuStripCommand的? 这些就是doozers完成的:Doozers是一些helper类,它们会根据codons来生成一些对象。 下面是MenuItemDoozer的一个简化版本: public class MenuItemDoozer : IDoozer { // More on HandleConditions in the conditions section. public bool HandleConditions { get { return true; } }
public object BuildItem(object caller, Codon codon, ArrayList subItems) { if (codon.Properties.Contains("type")) type = codon.Properties["type"]; else type = "Command"; switch (type) { case "Separator": return new MenuSeparator(codon, caller); case "CheckBox": return new MenuCheckBox(codon, caller); case "Item": return new MenuCommand(codon, caller); case "Command": return new MenuCommand(codon, caller); case "Menu": return new Menu(codon, caller, subItems); case "Builder": return codon.AddIn.CreateObject(codon.Properties["class"]); default: throw new NotSupportedException(type); } } } 其中的类“MenuCommand”, “MenuCheckBox” 是 SharpDevelop中override系统标准的“MenuStripCommand”的类. 它们会从codon的属性里取出label, icon 和shortcut等信息。 当一个MenuCommand被点击时,MenuCommand 会调用 “codon.AddIn.CreateObject()”来创建一个对象,把此对象转换成ICommand 接口后,再调用此接口的Run函数.
How to add custom doozers 核心的doozers会把SD直接创建加载的. 但是如果你想在你的插件里定义自己的doozer该如何做呢?要想完成这一点,你可以把你的doozer 写到<Runtime> 段里。比如: <Import assembly = 'MyAddIn.dll'> <Doozer name='MyDoozer' class = 'MyAddIn.MyDoozer'/> </Import> 一般情况下,这个doozer (和assembly)在第一次使用时就会被加载进来。 |