SharpDevelop浅析_1_AddInTree
使用ICSharpCode.Core创建插件支持的应用程序

http://www.cnblogs.com/michael-zhang/articles/621148.html
Demo运行界面:
使用AddIn好处:
AddIn实现分析:
SharpDevelop重要概念
Demo代码分析
总结:
相关资料:
Demo下载

1、Demo运行界面:
程序初始运行界面如下:

添加插件界面如下:

添加插件后界面如下:

运行环境:VS2005

2、使用AddIn好处
方便扩展,可以看到SharpDevelop几乎是通过插接功能模块组装而成;核心可以不必实现自己的定义,方便地通过接口扩充功能;插件dll可以放在任意位置,对插件使用拷贝、粘贴式的部署。
许多应用程序也使用了一些插件机制,但大多数局限于特定的功能,如扩展菜单或新文件格式。SharpDevelop插件体系的目标是为应用程序提供简单易用而又强大的扩展点,allowing AddIns to extend nearly everything.

3、AddIn实现分析:
简单分析后,实现思路是这样的:定义一个接口ICommand,声明void DoCommand()方法,新增插件必须实现此接口;
单击菜单项或工具栏按钮时需要与主窗体交互,这可以通过在ICommand中定义属性MainForm或在void DoCommand(MainForm frm)中增加方法参数来传递主窗体的引用,这些实现起来倒也简单。
接 下来的问题是如何通知应用程序新增加了插件呢,答案是使用xml配置文件,怎么组织这个配置文件的结构呢?这个问题其实成了实现插件功能的重点和难点,配 置文件中希望说明新增插件的dll位置、类名、插接入主程序的菜单还是工具栏项、插接位置,或许还希望配置文件更容易被扩展?
这里(http: //www.codeproject.com/cs/library/Net_AddinProjFrmwork.asp)有一个结构不太好的配置文件定义形式(可能也是我们简单分析后会想到的定义方式,可以看出结构固定,且不易扩展),大家可以自行分析下:

sample.xml
 1<ProjectFrameworkAddin>
 2 <AppVer>1</AppVer>
 3 <AddinName>Report Addin</AddinName>
 4 <ToobarButtonCount>1</ToobarButtonCount>
 5 <MainMenu>
 6  <Name>Bar Code</Name>
 7  <ShortcutKeyIndex>1</ShortcutKeyIndex>
 8  <SubMenu>
 9      <Name>Bar Code</Name>
10      <ShortcutKeyIndex>1</ShortcutKeyIndex>
11    <LeafMenu>
12      <Name>Test Menu</Name>
13      <FunctionName>AddinFunctionName</FunctionName>
14      <HelpString>Some Status bar text</HelpString>
15      <ToolTip>Some tool tip text</ToolTip>
16      <ToolBarIndex>Addin2Settings.ico</ToolBarIndex>
17      <ShortCutKey>Ctrl + H</ShortCutKey>
18    </LeafMenu>
19    <LeafMenu>
20      ..
21      ..
22    </LeafMenu> 
23  </SubMenu>
24  <SubMenu>
25    ..
26    ..
27  </SubMenu>
28 </MainMenu>
29 <MainMenu>
30  
31  
32 </MainMenu>
33</ProjectFrameworkAddin>

现 在来看看SharpDevelop的AddIn配置文件结构(参见Demo中的Entry.myAddins.Menus.addin):

Menus.addin
 1<AddIn name        = "basic menus"
 2       author      = "michael zhang"
 3       url         = "http://www.cnblogs.com/michael-zhang/"
 4       description  = "基本插件"
 5       addInManagerHidden = "true">
 6  <Manifest>
 7    <Identity name="michael.addin.basic" version = "@EntryAssemblyVersion"/>
 8  </Manifest>
 9  <Runtime>
10    <Import assembly = "../MainForm.dll"/>
11  </Runtime>
12  <Path name = "/michael/BlackText">
13    <FileFilter id = "Text" name = "Text files" extensions = "*.txt"/>
14    
15  </Path>
16  <Path name = "/michael/dymanic_Menus">
17  </Path>
18  <Path name = "/michael/myMenus">
19    <MenuItem id = "File"
20                  type = "Menu"
21                  label = "${res:Demo.Menu.File}">
22      <MenuItem id = "CmdBlack"
23                      label = "Cmd&amp;Black"
24                      shortcut = "Control|B"
25                      icon = "qq.face1"
26                      class = "MainForm.CmdBlack"/>
27      <Include id = "DynamicMenuList" path = "/michael/dymanic_Menus"/>
28      <MenuItem id = "Separator1" type = "Separator"/>
29      <MenuItem id = "Exit"
30                      label = "E&amp;xit"
31                      shortcut = "Control|X"
32                      class = "MainForm.CmdExit"/>
33    </MenuItem>
34    <MenuItem id = "Manager"
35                  type = "Menu"
36                  label = "&amp;Manager">
37      <Include id = "AddInManager" path = "/michael/AddInManager"/>
38    </MenuItem>
39  </Path>
40  <Path name = "/michael/myToolbar">
41    <ToolbarItem id = "CmdBlack"
42                     tooltip = "Black command"
43                     icon = "qq.face1"
44                     class = "MainForm.CmdBlack"/>
45    <ToolbarItem id = "Separator1" type = "Separator"/>
46    <ToolbarItem id = "Exit"
47                      tooltip = "Exit the app"
48                      icon = "CloseIcon"
49                      class = "MainForm.CmdExit"/>
50  </Path>
51</AddIn>

< AddIn>节提供了插件的名称、作者、url、插件描述等信息
<Indetity>提供了插件的唯一标识名称及版本,其它插件的配置文件可以引用该名称,如<Dependency addin=...>
<Import>指向该插件引用的dll位置
后 面的形如<Path name="..."><MenuItem>...</Path>的是定义配置文件的核心数据,path节的name属性 指明该节下的节点所处(在AddInTree中)的命名层次,节点下的MenuItem, ToolBarItem, FileFilter, Include等统称为Condon,各代表菜单项、工具栏按钮、文件过滤等,这些数据结构可以非常简单地被扩展、解析。
注:
addin配置文件中的 label = "${res:Demo.Menu.File}", icon = "qq.face1" 等属性值是指向资源文件的引用,资源文件见Entry项目的StrImgRes.resx

4、SharpDevelop插件树中的重要概念
Condon: 代表<Path>节下的一个(一般化)节点(如:<MenuItem><ToolBarItem>...)统称为 Condon,该类含ID、Name、InsertBefore、InsertAfter、Conditions、Properties(类似于 HashTable的结构)等属性,配置节中的其它属性(除ID,Name,InsertBefore,InsertAfter外,如label, shortcut等)存储在Properties对象中。
Doozer: 代表Condon节点的更具体的实例,如MenuItemDoozer, ToolBarItemDoozer, FileFilterDoozer, IncludeDoozer, FileFilterDoozer等,用以创建具体的object对象,可以扩展编写自定义的Doozer。

5、Demo项目代码分析
至此,我们大概能猜到SharpDevelop中的插件机制是怎样的,下面就结合Demo的分析来体验一下SharpDevelop的插件功能:
MainForm.FrmMain.cs中使用单件模式获取此类,关键代码如下:

FrmMain.cs
 1using ICSharpCode.Core;
 2
 3// 变量声明
 4const string _BoundProperty = "FormBounds";
 5Label _lblMsg;
 6MenuStrip _menuStrip;
 7ToolStrip _toolStrip;
 8//
 9void IniFrm()
10{
11    // 设置窗体位置
12    Rectangle rect = PropertyService.Get<Rectangle>(_BoundProperty, new Rectangle(10, 10, this.Width, this.Height));
13    this.StartPosition = FormStartPosition.Manual;
14    this.Bounds = rect;
15    this.FormClosing += delegate
16    {
17        // 设置用户属性信息
18        PropertyService.Set<Rectangle>(_BoundProperty, this.Bounds);
19    };
20
21    _lblMsg = new Label();
22    _lblMsg.Dock = DockStyle.Fill;
23    _lblMsg.Font = new Font("Arial", 16, FontStyle.Bold);
24    _lblMsg.Text = "App loaded!";
25    this.Controls.Add(_lblMsg);
26
27    _toolStrip = ToolbarService.CreateToolStrip(this, "/michael/myToolbar");
28    this.Controls.Add(_toolStrip);
29
30    _menuStrip = new MenuStrip();
31    MenuService.AddItemsToMenu(_menuStrip.Items, this, "/michael/myMenus");
32    this.Controls.Add(_menuStrip);
33}
34public void DrawMsg(string msg,Color color)
35{
36    _lblMsg.Text = msg;
37    _lblMsg.ForeColor = color;
38}

主 窗 体的类中声明了Label, MenuStrip, ToolStrip 分别用以显示文字、菜单、工具栏。菜单、工具栏对象的获取通过ICSharpCode.Core内置类仅用简单的两行代码实现。此类中公开的 DrawMsg(...)方法用以向扩展菜单、工具栏按钮等提供公开可调用的功能。创建菜单、工具栏的两个方法中的一个重要参数是路径参数(分别是 "/michael/myToolbar"和"/michael/myMenus"),在前面的配置文件代码中可以找到相关定义节,其中相关节点一个重要 属性是class, 该属性指定了(菜单、工具栏按钮的)相关类,其实现代码如下(MainForm项目的Commands.cs):
注:
FrmMain 类的IniFrm()方法中前几行代码用以设置启动窗体的大小和位置,使用到了ICSharpCode.Core.PropertyService类,该 类将配置文件保存在"C:/Documents and Settings/michael/Application Data/michael's add-in test/myCfgParas.xml",其中[michael]是计算机名;[michael's add-in test]是应用程序名称,在程序启动时创建CoreStartup实例时指定;[myCfgParas.xml]由属性 CoreStartup.PropertiesName指定(详见后面Main()中的代码)。

Commands.cs
 1//using 
 2namespace MainForm
 3{
 4    public class CmdBlack : AbstractMenuCommand
 5    {
 6        public override void Run()
 7        {
 8            FrmMain frm = (FrmMain)this.Owner;
 9
10            StringBuilder sBuilder = new StringBuilder();
11            ArrayList alDatas = AddInTree.BuildItems("/michael/BlackText", null, true);
12            foreach (string str in alDatas)
13                sBuilder.AppendLine("suported types:     " + str);
14
15            frm.DrawMsg(sBuilder.ToString(), Color.Black);
16        }
17    }
18
19    public class CmdExit : AbstractMenuCommand
20    {
21        public override void Run()
22        {
23            FrmMain frm = (FrmMain)this.Owner;
24            if (MessageBox.Show("Sure to exit?","Info:",MessageBoxButtons.YesNoCancel,MessageBoxIcon.Question) == DialogResult.Yes)
25                frm.Close();
26        }
27    }
28}

可 以看到插件类必须实现ICommand接口或继承 AbstractMenuCommand类,ICommand接口定义的Owner属性返回该对象的拥有者Object,在此例子中即FrmMain对 象,CmdBlack类中通过((FrmMain)Owner).DrawMsg(...)向主窗体发出功能命令。
至此,只剩下对ICSharpCode.Core进行必要的初始化配置了(参见Demo中的Entry项目Program.cs中的static void Main()函数):

void Main
 1LoggingService.Info("Application start");
 2Assembly asm = Assembly.GetExecutingAssembly();
 3FileUtility.ApplicationRootPath = Path.GetDirectoryName(asm.Location);
 4ResourceService.RegisterNeutralStrings(new ResourceManager("Entry.StrImgRes", asm));
 5ResourceService.RegisterNeutralImages(new ResourceManager("Entry.StrImgRes", asm));
 6
 7LoggingService.Info("Starting core services");
 8CoreStartup coreStartup = new CoreStartup("michael's add-in test");
 9coreStartup.PropertiesName = "myCfgParas";
10coreStartup.StartCoreServices();
11// 在指定文件夹中搜寻插件的配置文件
12coreStartup.AddAddInsFromDirectory(Path.Combine(FileUtility.ApplicationRootPath, "myAddIns"));
13// AddinManager 插件的属性:保存用户禁用的插件信息
14coreStartup.ConfigureExternalAddIns(Path.Combine(PropertyService.ConfigDirectory, "AddIns.xml"));
15// AddinManager 插件的属性:保存用户安装的插件信息
16coreStartup.ConfigureUserAddIns(Path.Combine(PropertyService.ConfigDirectory, "AddInInstallTemp"),
17                                Path.Combine(PropertyService.ConfigDirectory, "AddIns"));
18coreStartup.RunInitialization();
19try
20{
21    LoggingService.Info("Running application");
22    Application.Run(MainForm.FrmMain.Instance);
23}catch{
24

注:
Demo 项目引用的AddinManager也是SharpCode.Core中的一个插件实现,查看AddinManager.Addin,可见其定义了菜单 项、新界面(点击新菜单时Run()方法中定义弹出的新窗体)、新界面的上下文菜单,上下文菜单中使用<Condition>配置菜单项何时 可用……
Demo项目中扩展菜单的例子参见Demo中的ExtenalMenus工程中的Command.cs和ExternalMenu.addin,该项目实现了两个新的菜单项,并且实现了一个自定义的Doozer。

6、总结:
a, ICSharpCode.Core默认实现的Doozer
Class        根据配置文件的声明由System.Reflection创建出相关对象
FileFilter    创建出路径后缀名的过滤选项提供给OpenFileDialog或是SaveFileDialog使用
Include    向addin tree引进一个(使用item属性)或多个(使用path属性)子项
Icon        用以创建文件类型与图标间的关联
MenuItem    创建菜单项 type可为:Seperator, CheckBox, Item/Command, Menu, Builder
ToolBarItem    创建工具栏按钮荐 type可为:Seperator, CheckBox, Item, ComboBox, DropDownButton

b, 配置文件中引用预定义资源格式
${res:ResourceName}        引用ResourceService系统资源
${property:PropertyName}    引用PropertyService中的属性
${env:VariableName}        引用系统变量
${exe:ProperName}        引用整个程序集的属性

c, Condition
[略(有待进一步分析)]

7、相关资料:
《Dissecting a C# Application Inside SharpDevelop.pdf》
SharpDevelop源代码(/src/ 和 /samples/ICSharpCode.Core.Demo/)
http://www.sharpdevelop.com/OpenSource/SD/Default.aspx
http://www.codeproject.com/csharp/ICSharpCodeCore.asp

SharpDevelop浅析_1_AddInTree相关推荐

  1. 浅析 JavaScript 中的 函数 uncurrying 反柯里化

    柯里化 柯里化又称部分求值,其含义是给函数分步传递参数,每次传递参数后部分应用参数,并返回一个更具体的函数接受剩下的参数,这中间可嵌套多层这样的接受部分参数函数,直至返回最后结果. 因此柯里化的过程是 ...

  2. 浅析Python中bytes和str区别

    本博转载自:Chown-Jane-Y的浅析Python3中的bytes和str类型 Python 3最重要的新特性之一是对字符串和二进制数据流做了明确的区分.文本总是Unicode,由str类型表示, ...

  3. 学习《Linux设备模型浅析之设备篇》笔记(深挖一)

    这篇文章既然说了是浅析,那就是跳过了一些东西,我们把这些跳过的东西给它尽可能的补回来 今天登陆 lxr.free-electrons.com 发现内核版本已经升级到3.15了,那以后都使用3.15的源 ...

  4. 学习《Linux设备模型浅析之设备篇》笔记(一)

    最近在学习Linux设备模型,前面几篇文章也是读这篇的时候遇到问题,然后为了搞清楚先转去摸索才写出来的. 当然了,刚开始是先读到<Linux那些事儿之我是Sysfs>,搞不清楚才去读的&l ...

  5. 架构周报| 浅析MySQL JDBC连接配置上的两个误区

    经典案例 \\ 浅析MySQL JDBC连接配置上的两个误区:相信使用MySQL的同学都配置过它的JDBC驱动,多数人会直接从哪里贴一段URL过来,然后稍作修改就上去了,对应的连接池配置也是一样的,很 ...

  6. 超级账本(Hyperledger Fabric)之权限管理浅析

    链客,专为开发者而生,有问必答! 此文章来自区块链技术社区,未经允许拒绝转载. 超级账本(Hyperledger Fabric)之权限管理浅析 超级账本是联盟链的代表,而其相对于共链(例如比特币,以太 ...

  7. linux内核SMP负载均衡浅析

    需求       在<linux进程调度浅析>一文中提到,在SMP(对称多处理器)环境下,每个CPU对应一个run_queue(可执行队列).如果一个进程处于TASK_RUNNING状态( ...

  8. CAS、原子操作类的应用与浅析及Java8对其的优化

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 作者:CoderBear juejin.im/post/5c7a8 ...

  9. Python标准库queue模块原理浅析

    Python标准库queue模块原理浅析 本文环境python3.5.2 queue模块的实现思路 作为一个线程安全的队列模块,该模块提供了线程安全的一个队列,该队列底层的实现基于Python线程th ...

最新文章

  1. 开发日记-20190827 关键词 读书笔记《Unix环境高级编程(第二版)》DAY 3
  2. asp.net mvc 3 RTM 发布了!
  3. jquery,angular 对象数组的克隆和深度克隆
  4. 2018年最新的single-cell-RNA-seq analysis repositories
  5. Wannafly挑战赛24 无限手套(生成函数)
  6. 归档和解档-Archiver
  7. linux 搭建go编译环境搭建,linux上搭建完整go语言vim开发环境
  8. 搭建Magento电子商务网站
  9. Android开发笔记(四十九)异步任务处理AsyncTask
  10. 埃斯顿服务器上电无显示,埃斯顿伺服常见现象报警及排除
  11. 挖一挖那些让公司网站瘫痪的SQL“终结者”
  12. 有关电子邮箱的各种协议
  13. SEO伪原创文章批量生成的方法「冷师兄」
  14. 用Java输出高频词_编程高阶用法--开发者高频词汇
  15. 关于 Cannot read property ‘length‘ of null 报错的解决办法
  16. win10 小而美的软件推荐
  17. 职场中干好工作的18准则
  18. PC版有道词典更换护眼背景色
  19. ssm教育机构管理系统毕业设计源码010224
  20. 力扣算法学习(十二)

热门文章

  1. 容器化改造要怎么做?
  2. 直流放大器2级电路设计
  3. 详解 atoi 函数并模拟实现
  4. 每个人心中都有一艘小白船
  5. ssm毕设项目基于Java的医疗器械销售系统oy281(java+VUE+Mybatis+Maven+Mysql+sprnig)
  6. RVT贴片铝电解电容规格
  7. HEVC中变换(Transform)过程中的scaling操作的理解
  8. 编程之美 1.2 中国象棋将帅问题
  9. Linux中根据文件大小排序
  10. java.io.IOException: 设备未就绪