study log
博客园
::
首页
::
新随笔
::
联系
::
订阅
::
管理
::
8 随笔 :: 6 文章 :: 132 评论 :: 2 引用
SharpDevelop浅析_1_AddInTree 使用ICSharpCode.Core创建插件支持的应用程序
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)
有一个结构不太好的配置文件定义形式(可能也是我们简单分析后会想到的定义方式,可以看出结构固定,且不易扩展),大家可以自行分析下:
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&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中使用单件模式获取此类,关键代码如下:
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(
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
}
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()中的代码)。
Commands.cs
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
"
,
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
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
绿色通道:
好文要顶
关注我
收藏该文
与我联系
posted on 2007-01-15 22:40
lin-zhang
阅读(7949)
评论(11)
编辑
收藏
评论
1328628
#1楼
2007-01-25 02:09
Paradise[未注册用户]
could you tell me your qq number?
it's good
回复
引用
#2楼
2007-01-25 09:39
yunhuasheng
I feel it's very good,I'll pay more attention!!
回复
引用
查看
#3楼
2007-01-25 09:55
forrestsun[未注册用户]
资源本地化问题如何解决?
回复
引用
#4楼
[
楼主
]
2007-01-25 13:10
lin-zhang
to Paradise:
我的qq: 93947150, 现在不常聊QQ,经常隐身
to forrestsun
下一篇就要写多语言支持方面的了,你可以参考
《Dissecting a C# Application Inside SharpDevelop》的
Chapter 7 ( Internationalization)
回复
引用
查看
#5楼
2007-01-25 15:07
乐章
<<C#软件项目开发全程剖析>>以前看了一点
现在 做一个项目正想参照一下SharpDevelop
发现版本已经到2.1了,变化很多
力挺楼主,写的越多越好哦,也表示感谢
回复
引用
查看
#6楼
2007-03-03 23:16
lwqdongtai[未注册用户]
正想研究SharpDelevop,此刻正在下载SharpDevelop 2.1的安装文件,这个叫慢啊~~呵.
我同样会关注你的blog,希望有机会能与你学习探讨!
回复
引用
#7楼
2007-03-06 14:17
飘飘[未注册用户]
写的很好,佩服,我研究一个来月了,没弄明白,你研究了多久啊
回复
引用
#8楼
[
楼主
]
2007-03-06 20:21
lin-zhang
谢谢楼上诸位 :)
@飘飘
最早是2003年读大二时接触C#的,现在对程序设计有些认识了吧,许多东西也是在不断学习中...
SharpDevelop源码差不多是07年一月初开始看的; 带着问题去“研究”,相信收效会好点
回复
引用
查看
#9楼
2007-06-14 15:42
yza[未注册用户]
今天刚看到楼主的文章,感觉不错。通俗易懂,前一段时间在SharpDevelop上做过二次开发。但对细节不是很了解,感谢楼主。
回复
引用
#10楼
2007-10-03 11:58
晨曦秋蝶[未注册用户]
小弟谢谢了
回复
引用
#11楼
2008-09-27 17:31
咸鱼翻身
谢谢
回复
引用
查看
注册用户登录后才能发表评论,请
登录
或
注册
,
返回博客园首页
。
首页
博问
闪存
新闻
园子
招聘
知识库
最新IT新闻
:
·
甲骨文19亿美元收购人才管理软件厂商Taleo
·
不止图片分享:Instagram 的专注与野心
·
畅想 G-Drive
·
一个时代的结束:柯达将退出数码相机和便携摄像机等业务
·
苹果员工称下一代iPad显示效果“令人惊叹”
»
更多新闻...
最新知识库文章
:
·
如何学习一门新的编程语言?
·
学习不同编程语言的重要性
·
为什么我喜欢富于表达性的编程语言
·
计算机专业的女生为什么要学编程
·
前端必读:浏览器内部工作原理
»
更多知识库文章...
China-pub 2011秋季教材巡展
China-Pub 计算机绝版图书按需印刷服务
<
2012年2月
>
日
一
二
三
四
五
六
29
30
31
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
1
2
3
4
5
6
7
8
9
10
公告
昵称:
lin-zhang
园龄:
5年1个月
粉丝:
7
关注:
0
搜索
常用链接
我的随笔
我的评论
我的参与
最新评论
我的标签
随笔档案
(8)
2012年1月 (2)
2011年7月 (1)
2008年11月 (1)
2007年2月 (2)
2007年1月 (2)
文章分类
(6)
SharpDevelop分析(6)
(rss)
DotNet Technology
CodeProject
MSDN Magazine
WindowsForms
Workflow
DotNetTools Workflow
OpenSymphony Workflow
最新评论
阅读排行榜
评论排行榜
推荐排行榜