study log
博客园
::
首页
::
新随笔
::
联系
::
订阅
::
管理
::
5 随笔 :: 6 文章 :: 123 评论 :: 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
阅读(4735)
评论(11)
编辑
收藏
网摘
所属分类:
SharpDevelop分析
评论
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
咸鱼翻身
谢谢
回复
引用
查看
刷新评论列表
刷新页面
返回页首
发表评论
昵称:
[登录]
[注册]
主页:
邮箱:
(仅博主可见)
验证码:
看不清,换一个
评论内容:
登录
注册
[使用Ctrl+Enter键快速提交评论]
0
621148
链接:
切换模板
导航:
网站首页
社区
新闻
博问
闪存
网摘
招聘
找找看
Google搜索
China-pub 计算机图书网上专卖店!6.5万品种 2-8折!
China-Pub 计算机绝版图书按需印刷服务
相关文章:
最新IT新闻:
Twitter无处不在 魔兽世界Twitter发送器插件发布
Firefox 3.5匆忙推出漏洞多 Mozilla本月将更新
预测:Twitter最可能收购的十家公司
网易澄清:与暴雪合资公司仅提供技术支持
杰克逊悼念仪式或成史上最大规模Web活动
相关链接:
<
2009年7月
>
日
一
二
三
四
五
六
28
29
30
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
30
31
1
2
3
4
5
6
7
8
与我联系
发短消息
搜索
常用链接
我的随笔
我的空间
我的短信
我的评论
更多链接
我的参与
我的新闻
最新评论
我的标签
留言簿
给我留言
查看留言
我参与的团队
苏州.Net俱乐部(0/0)
随笔档案
(5)
2008年11月 (1)
2007年2月 (2)
2007年1月 (2)
文章分类
(6)
SharpDevelop分析(6)
(rss)
DotNet Technology
CodeProject
MSDN Magazine
WindowsForms
Workflow
DotNetTools Workflow
OpenSymphony Workflow
最新评论
1. re: SharpDevelop浅析_4_TextEditor_自动完成、代码折叠……
正需要这样的东东,谢谢了!
--laoda
2. re: SharpDevelop浅析_3_Internationalization & TextEditor 国际化、文档编辑器、语法高亮显示……
请问,如何设置这个控件的只读属性啊?
--asato
3. re: SharpDevelop浅析_3_Internationalization & TextEditor 国际化、文档编辑器、语法高亮显示……
请问下,定位好光标后,
如何移动光标的?
--FLYDZK
4. re: SharpDevelop浅析_4_TextEditor_自动完成、代码折叠……
楼主:
问一下SharpDevelop的关键字之类是不是不能自动完成,代码提示,就像输入点后有提示一样。
--blueshake
5. re: SharpDevelop浅析_3_Internationalization & TextEditor 国际化、文档编辑器、语法高亮显示……
怎么去掉那个控件上的红色波浪线和那个换行符呢?
--conquer's
阅读排行榜
1. SharpDevelop代码分析(6683)
2. SharpPad文本编辑器: 已完成添加代码自动完成、代码折叠等功能(3334)
3. SharpDevelop浅析_3_Internationalization-TextEditor __ 读书心得(2484)
4. 博客开张了(268)
5. silverlight小游戏: 贪吃蛇(122)
评论排行榜
1. SharpDevelop浅析_3_Internationalization-TextEditor __ 读书心得(9)
2. SharpDevelop代码分析(9)
3. SharpPad文本编辑器: 已完成添加代码自动完成、代码折叠等功能(8)
4. 博客开张了(5)
5. silverlight小游戏: 贪吃蛇(0)