打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
SharpDevelop浅析_1_AddInTree 使用ICSharpCode.Core创建插件支持的应用程序
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)有一个结构不太好的配置文件定义形式(可能也是我们简单分析后会想到的定义方式,可以看出结构固定,且不易扩展),大家可以自行分析下:

 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):

 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&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&xit"
31
                      shortcut 
= "Control|X"
32
                      class 
= "MainForm.CmdExit"/>
33
    
</MenuItem>
34
    
<MenuItem id = "Manager"
35
                  type 
= "Menu"
36
                  label 
= "&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中使用单件模式获取此类,关键代码如下:

 1
using ICSharpCode.Core;
 2

 3
// 变量声明
 4
const string _BoundProperty = "FormBounds";
 5
Label _lblMsg;
 6
MenuStrip _menuStrip;
 7
ToolStrip _toolStrip;
 8
//

 9
void IniFrm()
10
{
11
    
// 设置窗体位置
12
    Rectangle rect = PropertyService.Get<Rectangle>(_BoundProperty, new Rectangle(1010this.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
}

34
public 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()中的代码)。

 1
//using 

 2
namespace 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"nulltrue);
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()函数):

 1
LoggingService.Info("Application start");
 2
Assembly asm 
= Assembly.GetExecutingAssembly();
 3
FileUtility.ApplicationRootPath 
= Path.GetDirectoryName(asm.Location);
 4
ResourceService.RegisterNeutralStrings(
new ResourceManager("Entry.StrImgRes", asm));
 5
ResourceService.RegisterNeutralImages(
new ResourceManager("Entry.StrImgRes", asm));
 6

 7
LoggingService.Info(
"Starting core services
");
 8
CoreStartup coreStartup 
= new CoreStartup("michael's add-in test");
 9
coreStartup.PropertiesName 
= "myCfgParas";
10
coreStartup.StartCoreServices();
11
// 在指定文件夹中搜寻插件的配置文件
12
coreStartup.AddAddInsFromDirectory(Path.Combine(FileUtility.ApplicationRootPath, "myAddIns"));
13
// AddinManager 插件的属性:保存用户禁用的插件信息
14
coreStartup.ConfigureExternalAddIns(Path.Combine(PropertyService.ConfigDirectory, "AddIns.xml"));
15
// AddinManager 插件的属性:保存用户安装的插件信息
16
coreStartup.ConfigureUserAddIns(Path.Combine(PropertyService.ConfigDirectory, "AddInInstallTemp"),
17
                                Path.Combine(PropertyService.ConfigDirectory, 
"AddIns"));
18
coreStartup.RunInitialization();
19
try
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
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
SharpDevelop源码分析 (三、插件系统)
俩个窗体之间出现 :" 未将对象引用到新的实例" 错误的情况
自定义SharpDevelop5.1/5.2的代码智能提示(代码完成)快捷键
详解WinForm窗体间交互的方法
ICSharpCode.SharpZipLib生成tar、tar.gz
AddIn加载项在word中的概念及使用
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服