插件及UIP配置说明
一,插件系统
1.1,插件系统介绍
插件系统是为了应用程序扩展方便而建立的扩展接口,它把扩展对象抽象化,利用xml文件的配置,来达到组合并使用用户定义的功能的目的。插件系统全部以代码子(Codon)为基础,
通过Xml文件来配置代码子,插件系统在运行时来组合对象并达到利用用户功能的目的,可以简单地把代码子看成是一个对象的XMl序列化,此外对象的生成是由代码子自身完成的,只有代码子能识别代码子,比如代码子的嵌套,也就是说只有代码子自身能够识别其子代码子。在这里要说明一点,代码子对象和代码子运行时对象是两个不同的概念,代码子对象是插件树结构的组成部分,而代码子运行时对象则是通过代码子组合后的对象。如果没有特别说明,在本文档的以下部分都将使用这两个术语。由于插件系统有且只有一棵树,故而在整个插件系统中使用了单件模式,AddInTreeSingleton类就是其实现形式。
1.2,插件配置文件分析并建立自己的代码子对象
插件系统是以.addin文件为结尾的文件,其结构大致如下:
<?xml version="1.0" encoding="GB2312"?>
<AddIn author="Spider"
copyright="Copyright@2005"
description="Spider module"
name="Spider.Application.CTS"
url="http://www.13cn.com"
version="1.0.0"
>
<Runtime>
<Import assembly="../bin/Spider.Application.CTS.BaseInfo.View.dll"/>
<Import assembly="../bin/Spider.Application.CTS.BaseInfo.Controller.dll"/>
<Import assembly="../bin/Spider.Framework.DevExHost.dll"/>
</Runtime>
<Extension path="/Workspace/FunctionalityDataItems">
<FunctionalityDataItem id="CTS.BaseInfo.BM_Otherh"
loader="Spider.Framework.DevExHost.Functionalitys.DevViewFunctionalityLoader"
startupModel="DirectView"
caption="其他资料"
beginGroup="false"
view="CTS.BaseInfo.View.BM_OtherhList"
/>
</Extension>
</AddIn>
从整个插件系统的结构上来看,配置文件可以看成是插件系统各个对象的xml串行化,
AddIn对应AddIn对象
Runtime 对应AddIn.Runtime
Extension对应AddIn.Extension
。。。。。
functionality就是代码子对象,以下就是建立代码子对象的基本步骤:
1,代码子对象的基类是ICodon和AbstractCodon,所有的代码子对象都应该从这两个对象派生,建议从AbstractCodon派生,这样可以省去很多麻烦,以下是其属性的说明:
id 代码子的唯一标识,注意所有的代码子ID号在同一路径下必须唯一
class代码子所关联的行为对象,注意这个对象必须从ICommand或这 AbstractCommand派生,因为代码子始终执行ICommand中的Run方法,
菜单中的点击动作就是这个原理,具体可以参见Spider.Framework.Host.AddIn.Codon和Command的代码。
insertafter 把代码子插入在制定的代码子之后
insertbefore 与上相反
2,建立自定义的代码子属性,注意所有的代码子属性如果想要被配置文件识别,必须添加扩展 属性声明,以下是要用到的扩展属性:
CodonNameAttribute 代码子的名称
XmlMemberArrayAttribute一个字符串数组对象
XmlMemberAttributeAttribute简单的字符对象
例如下面要建立的代码子:
[CodonName("Test")]
public class TestCodon:AbstractCodon{
[XmlMemberAttribute("name")]
private string _name=null;
public string Name{
get{
return _name;
}
set{
_name=value;
}
}
}
3,重写BuildItem方法,完成上面的例子
[CodonName("Test")]
public class TestCodon:AbstractCodon{
[XmlMemberAttribute("name")]
private string _name=null;
public string Name{
get{
return _name;
}
set{
_name=value;
}
}
public override object BuildItem(object owner, ArrayList subItems, Spider.Framework.Host.AddIns.Conditions.ConditionCollection conditions){
object result=new object();
return result;
}
}
4,建立关联的Command对象,以上说了所有的Command对象必须从ICommand或AbstractCommand派生,当然最好从AbstractCommand派生,以下是一个例子:
public class TestCommand:AbstractCommand{
public override object Owner{
get{
return null;
}
set{
;
}
}
public override Run(){
}
}
1.2,插件系统的启动过程,
1,插件系统去搜索用户的插件定义目录,用户定义的目录是在App.conf文件中定义的,此外如 果找不到用户定义的目录,插件系统会使用其自身默认的插件目录,具体可以参见AddInTreeSingleton类,里面有其完整的实现。
2,建立插件树并初始化树。插件系统是以插件文件中Extension节点的PATH路径来识别每个插件树节点,你可以在不同的插件里面来扩展和定义这个路径下面的节点。在这一步中,插件系统会加载所有的插件文件,并根据节点路径生成完整的插件树,注意在生成插件树的过程中代码子运行时对象是不会建立的,仅仅是建立了代码子对象,运行时对象是在用户组合代码子功能的时候才会创建。关于这部分,情自行参见AddInTreeSingleton类里面的代码实现。
3,在建立好插件树之后,就是应用程序来利用代码子组合应用程序的时候。一般来讲插件系统都是通过服务(Service)来管理并组合这些代码子的。关于服务将会在下文中讲到。要建立代码子对象首先要获取给定路径下面的节点,这个步骤是通过AddInTreeSingleton.AddInTree.GetTreeNode这个方法来实现的,它返回一个IAddInTreeNode对象,显然这不是我们想要的,要想利用代码子运行时对象,必须通过IAddInTreeNode.BuildItem或IAddInTreeNode.BuildItems方法来实现,其中前一个方法只会建立一个代码子运行时对象并返回一个Object对象,而后一个方法会建立改路径下面的所有代码子对象并返回一个ArrayList的数组。
4,在第3步中已经看到了,在建立好插件系统后,我们就可以得到任意节点下面所有的代码子运行时对象,公司的应用程序里面整个插件系统的组合都是在DevExHost工程里面完成的,
下面就来讲一下DevExHost工程的结构。
目录组织:
Codons通用代码子对象
Commands代码子运行时接受用户的动作后所做出的反映
Functionalitys功能子对象
Services服务对象
SpiderDevExGui程序对DevExpress控件的封装
UIPUserInterface对象集合
其他目录都是和代码子相关的对象,请自行参见代码,这里重点介绍的SpiderDevExGui目录下Workbench目录下的DevExWorkbenchMainForm窗体对象,因为基本上所有加载的动作都是在这个类里面完成的,请自行参见其代码,那里注释的很清楚了。
1.3,服务对象(Service)
其实Service对象也是建立在代码子的基础之上,主要是这些对象在运行时就已经在高速缓存里面存在,以供快速调用,建立一个Service对象和建立代码子对象没什么区别,但是有一点必须注意,普通的代码子对象可以不必指定class属性,但是Service对象的代码子必须指定
class属性,此外所有的服务对象都是通过ServiceManager类来管理的,要添加一个服务对象请使用ServiceManager.Services.Add(serviceObject)方法,要获取一个服务对象请使用ServiceManager.Services.GetService(Type serviceType)方法,要了解更详细的信息请自行参见
Spider.Framework.Host.Services里面的代码。
1.4,功能子对象(Functionality)
其实功能子对象很简单,在插件系统中,每一个功能都要定义属性及class对象,各个对象之间是不能重复使用的,功能子要做的就是把代码子所有需要的属性通过其来封装,以后在其他的代码子中就能重复调用,要了解更详细的信息,请自行参见Spider.Application.Common.AppFunctionality工程,那里代码注释的已经很清楚了。
二,UIP(User interface processer)说明
2.1,什么UIP
从UIP的名字就可以看出来,它主要是把用户从事件到业务处理代码分离开来,更好第实 现MVC的一个功能性应用程序,具体的说明请参见微软的UIP说明文档。
2.2,UIP的运行方式
UIP是通过Xml文件来配置的,所有对象配置及对象的组合都是在Configuration目录下的对象来完成的,最重要的几个配置有ControllerSettings,ViewSettings,ViewManagerSettings等,其中UIPConfiguration类是UIP的入口对象,详细的请自行参见代码。
首先UIP读取配置信息并建立配置对象的运行时对象,所有的UIP必须先启动,这个是通过UIPManager里面的startTask之类的方法来启动,其中在公司的应用程序里面UIP的启动是在用户成功登陆后启动的,详见Spider.Framework.DevExHost里面的登录窗体里面的代码,
用户到另一个窗体的跳转是通过Navigator对象完成的,所有的事件执行后的动作都是在Controller类里面完成的,窗体的显示是通过ViewMananger来显示的,当然你也可以建立自己的Controller,Navigator,ViewManager类来实现自定义的导航,但必须从相关的基类派生,其他的这里就不在多说了,详见微软的UIP说明文档。
2.3,公司应用程序的UIP配置
UIP的配置本来是通过App.conf配置文件来配置的,但是为了和系统的插件系统保持兼容,故而在公司的架构中UIP的配置是通过代码子来完成的,其中在Spider.Framework.UIProcess.Codons下面有每个配置对象的代码子封装。
从配置文件到代码子对象的实现,其实原理很简单,只不过是在UIP加载的时候做了手脚,过去UIP是从配置文件里面读取并创建对象并添加到相关对象集合里面去的,现在则是通过插件来建立对象并用自定义的方法来添加到UIP相关对象集合里面去。所有的处理动作都放在了Spider.Framework.UIProcess.UIPConfiguration类中执行的,因为它是整个UIP的入口对象,处理代码子的代码请自行参见代码。
2.4,UIP重点配置对象的说明
ViewSettings
id标识,即作为代码子的标识也对应于UIP里面的ViewName
layoutManager布局管理器
stayOpen 是否一直处于打开状态,如果设置了此属性为真,那么这个窗体会在程序运行期间一直存在,当你跳转到其他窗体时它会自动隐藏,离开其它窗口后,它会自动恢复其状态。
openModal标识窗体是否时有模式打开,其实也就对应窗体里面的ShowDialog()方法
floatable标识窗体是否是一个浮动窗体
canHaveFloatingWidows标识窗体是否能具有浮动的窗体,也就是说该窗体是否是其他窗体的容器
ContainerViewSettings
view对应的ViewSettings对象的名字
ViewContainer其父窗体
ViewContainerSettings
mainView对应的主窗体的名字,要作为父窗体的窗体