1  /  1  页   1 跳转 查看:861

SharpDevelop浅析

SharpDevelop浅析

原帖地址: http://www.cnblogs.com/michael-zhang/category/82115.html

                SharpDevelop浅析_序

换了工作,最近在作.NET的C/S项目,由于经验比较少,便想通过读《Dissecting a C# Application Inside SharpDevelop》结合SharpDevelop源码的学习,全面地了解一下好的应用程序是如何编写的,从中不但能锻炼编程能力,也可以进行一些架构的思考。
电子书共有17章,计划每周读两章,争取春节前读完第一遍。到今天为止已经读了6章,理解有限。但是我希望结合SharpDevelop源码和自己的理解写一些简单、突出重点的Demo出来,在这里与大家讨论,欢迎指正。
电子书的章节如下:
Summary of Contents
Chapter 1:    Features at a Glance
Chapter 2:    Designing the Architecture
Chapter 3:    Implementing the Core
Chapter 4:    Building the Application with Add-ins
Chapter 5:    Providing Functionality with Workspace Services
Chapter 6:    The User Interface
Chapter 7:    Internationalization
Chapter 8:    Document Management
Chapter 9:    Syntax Highlighting
Chapter 10:  Search and Replace
Chapter 11:  Writing the Editor Control
Chapter 12:  Writing the Parser
Chapter 13:  Code Completion and Method Insight
Chapter 14:  Navigating Code with the Class Scout and the Assembly Scout
Chapter 15:  The Designer Infrastructure
Chapter 16:  Implementing a Windows Forms Designer
Chapter 17:  Code Generation
除了一些泛泛的特性或理论介绍章节外,尽量每章写一个Demo,每周写一篇文章出来,欢迎大家监督

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

SharpDevelop浅析_2_User-Interface -- 创建易扩展且功能模块松散耦合的应用程序

SharpDevelop浅析_3_Internationalization & TextEditor 国际化、文档编辑器、语法高亮显示……

SharpDevelop浅析_4 TextEditor_自动完成、代码折叠……


SharpDevelop浅析_5_Windows Forms Designer

-------------------------------------------------------------------------------------------
SharpDevelop浅析系列这一阶段的文章基本完成了;
暂未涉及到Project组织方式, Debugger 及其它一些Addin项目的分析;学以致用,如果以后有应用需求,也可能会再来补充分析些其它有意思的项目吧...
Sinoprise Network Studio
 

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位置
后面的形如<Pathname="..."><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'sadd-in test\myCfgParas.xml",其中[michael]是计算机名;[michael's add-intest]是应用程序名称,在程序启动时创建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
最后编辑enjoyo 最后编辑于 2008-05-25 22:22:49
Sinoprise Network Studio
 

SharpDevelop浅析_2_User Interface 创建易扩展且功能模块松散耦合的应用程序

SharpDevelop浅析_2_User Interface
创建易扩展且功能模块松散耦合的应用程序


Demo界面及功能解释
相关概念
Demo代码分析
总结
Demo下载

1、Demo界面及功能解释:

启动界面:

支持打开图片:

支持打开网页:

功能说明:程序有个浮动窗口显示当前磁盘文件,选择文件并双击后在中心窗口中以适当的察看器打开文件;持拖曳打开文件;程序关闭后再次启动时能够“记忆”前一次程序关闭时的窗体大小位置及打开过的文件。

2、相关概念:
主窗体部分:              Workbench
磁盘文件查看窗口:    Pad
文件查看窗口:        ViewContent
说明:
Pad可以使用ICSharpCode.Core的Addin插件机制来扩充Pad,如Visual Studio的资源管理器、类查看器、属性窗口、消息窗口等都属于Pad
ViewContent同样支持插件扩充,根据不同的文件类型使用不同的控件来处理显示,Demo中已实现的文件类型支持:网页文件、图片、普通文本,Visual Studio中的代码窗口、窗体设计器、资源编辑器、对象查看器等都属于ViewContent
Workbench与WorkbenchLayout结合使用来控制主窗口的外观显示,如普通的MDI(PhotoShop?)或Demo使用的WeifenLuo.WinFormsUI.Docking显示方式……

3、Demo代码分析
(注:下面的分析前提是你已经了解如何使用ICSharpCode.Core的Addin插件机制)
通过上面的界面及功能说明,可以确定程序至少应包括接口声明、基本实现两个项目,而接口中又分为:Pad接口、ViewContent接口、Workbench接口,这样的程序设计重点与难点在哪呢?思考后的结果应该是接口,从接口中能看到程序各部分是如何交互的,以及插件的扩展点,接口的设计好坏决定着程序的易维护性与易扩展性,所以下面就重点看接口的定义(附介绍相关对象的创建等):

Pad接口
1//  Core|Interface 定义部分:
2public
interface IPadContent : IDisposable
3{
4    System.Windows.Forms.Control Control { get; }
5   
6    void RedrawContent();
7}
8public
class PadDescriptor : IDisposable
9{
10    Codon      codon;
11    IPadContent padContent;
12    bool        padContentCreated;
13   
14    public
string Title {
15        get
{
16            return codon.Properties["title"];
17        }
18    }
19    public
string Icon {
20        get
{
21            return codon.Properties["icon"];
22        }
23    }
24    public
string Class {
25        get
{
26            return codon.Properties["class"];
27        }
28    }
29    public IPadContent PadContent {
30        get
{
31            CreatePad();
32            return padContent;
33        }
34    }
35    public
void CreatePad()
36    {
37        if (!padContentCreated) {
38            padContentCreated =
true;
39            padContent = (IPadContent)codon.AddIn.CreateObject(Class);
40        }
41    }
42    //省略部分方法、属性
43
public PadDescriptor(Codon codon)
44    {
45        this.codon = codon;
46    }
47}
48//  Gui项目中的实现
49class PadContentWrapper : DockContent
50{
51    PadDescriptor padDescriptor;    //通过此对象来获取相关属性
52    //
53}

看到IPadContent接口返回一个WinForm的Control控件,此控件在实现时被相应的窗体获取并适当地显示,Demo中的磁盘查看器Pad返回的Control是一个UserConrol,使用了TreeView和ListView组合。注意IPadContent接口中定义返回一个Control而非Form是很聪明的技巧,可以看到实现端的PadContentWrapper继承自WeifenLuo.WinFormsUI.DockContent,这依赖于实现端的表现方式,而接口可以不受影响。PadDescriptor是个辅助类,用以返回Pad相关的属性信息,包括返回IPadContent的实例对象。
接下来看Pad的xml声明及客户端调用:
Pad声明及使用
1//取自Entry项目的SD_UI.addin
2<Path name =
"/SharpDevelop/Workbench/Pads">
3    <!--
4    ProjectBrowser
5    ClassBrowser
6    SideBar
7    ErrorList
8    TaskList
9    CompilerMessageView
10    PropertyPad
11