使用 SailingEase WinForm 框架构建复合式应用程序(插件式应用程序)
SailingEase WinForm Framework WinForm开发框架开发手册:http://docs.shengxunwei.com/Home/Browser/sewinformfw/
对于一些较小的项目,具备一定经验的开发人员应该能够设计和构建出便于进行维护和扩展的应用程序。但是,随着功能模块数量(以及开发维护这些部件的人员)的不断增加,对项目实施控制的难度开始呈指数级增长。
SailingEase WinForm 框架为您提供了针对此问题提出的解决方案。在本文中,将对基于SailingEase WinForm 框架的复合应用程序的定义进行解释,并简要说明如何才能构建一个基于 SailingEase WinForm 框架功能的复合应用程序。
传统的单一应用程序
传统的单一应用程序每个控件都紧密耦合在一起,UI 中存在大量用于协调各个部分的逻辑。控件之间还存在着相互依赖关系。
由于存在这些依赖关系,因此无法通过某种简单的方法将应用程序分解成可在其中分别开发各个不同部分的窗体。虽然可以将所有用户控件都放在一个单独的程序集中以提高可维护性,但这种做法只是将问题从主应用程序转移到了控件程序集,治标不治本。在这种模型中,进行重大更改或引入新功能都非常困难。
基于 SailingEase WinForm 框架的复合应用程序(插件式应用程序)
基于 SailingEase WinForm 框架的复合应用程序由运行时动态发现和构成的松散耦合模块组成。模块包含代表系统的不同垂直片段的可视和非可视组件。可视组件(视图)被组合在一个常规外壳中,可用作应用程序所有内容的宿主。复合应用程序可提供各种服务,将这些模块级组件结合在一起。模块可提供与应用程序的特定功能相关的其他服务。
从较高的层次来看,复合应用程序是“复合视图”设计模式的实现,此模式可描述包含子项的视图的递归 UI 结构,这些子项本身也是视图。这些视图然后通过某种机制组合起来 — 通常是在运行时而非设计时静态组合。
模块会影响在其中创建主复合视图(也称为外壳)的视图。模块永远不会相互直接引用,也不会直接引用外壳。相反,它们会利用服务在彼此之间以及与外壳之间进行通信,以响应用户操作。
使用模块来组成系统有很多好处。模块可聚合来自同一应用程序中不同后端系统的数据。此外,系统可随着时间的推移更加方便地发展演变。在系统需求发生变化而需要向系统中添加新模块时,与非模块化系统相比,模块化系统面临的冲突要少很多。而且还可以对现有模块进行独立性更强的改进,从而改善可测试性。最后,模块可由不同的团队开发、测试和维护。
一个典型的基于 SailingEase WinForm框架的应用程序
创建基于 SailingEase WinForm 框架的应用程序
引导程序和容器
使用 SailingEase WinForm 框架构建复合应用程序时,首先必须初始化几个核心复合服务。这就引入了引导程序。它可以执行发生复合所需的全部功能。在许多方面,它都类似于应用程序的 Main 方法。
对于容器,指的是控制反转 (IoC) 容器/依赖关系注入 (DI) 容器。容器在应用程序中起着关键作用。容器存储着应用程序中使用的所有应用程序服务。它负责在需要的位置注入这些服务。
在配置容器的同时,还会自动注册几个核心服务(如事件聚合器),基本的引导程序允许您覆盖其中的任何服务。例如,自动注册 ImoduleLoader 服务。如果在引导程序中覆盖 ConfigureContainer 方法,即可注册自己的模块加载程序。
模块的加载
在引导程序中,通过覆盖方法 GetModuleCatalog 即可加载所需的模块,除了通过 ModuleCatalog 的 AddModule 方法来加载模块之外,可以在此实现其它任何所需的模块加载方式。
定义并实现一个模块
在基于 SailingEase WinForm框架的应用程序中,模块是复合应用程序的分离单位,可将其部署为单独的程序集(尽管并非必需)。模块包含了大部分的功能。
这是一个基于 SailingEase WinForm框架的应用中的一个模块,StartPageModule.cs是实现 IModule 接口的类。此接口仅包含一个方法,称为 Initialize。如果把引导程序看作应用程序的 Main 方法,那么此处的 Initialize 方法就是模块的 Main。
在该模块的构造函数中,我们取得 IUnityContainer 容器,事件聚合器 IEventAggregator ,以及工作区服务 IWorkbenchService。
IUnityContainer,IEventAggregator和IWorkbenchService它们究竟从何而来?我是否要将逻辑硬编码到模块的初始化代码中?
答案是“否”。加载模块时,SailingEase WinForm框架自动将模块需要的服务从容器中解析出来,注入到模块的构造函数中,如果我们需要在模块初始化时获得其它服务,只需在构造函数中直接加入参数即可。
在该模块的 Initialize 方法中,我们做了两件事:注册导航项到应用程序的外壳(主窗口)中,订阅应用程序中的事件。
在不同的模块间发布/订阅事件
SailingEase WinForm 框架为您提供了事件聚合服务,在传统的单一应用中,不同功能点之间的事件订阅是直接的引用与依赖关系,而SailingEase WinForm 框架通过事件聚合服务,使得事件的订阅者,与发布者完全解耦合,不存在任何引用与依赖关系。
传统的应用程序事件发布订阅:
功能A发布事件 ProjectOpenedEvent ,功能B订阅此事件时,必须在编码过程中依赖,引用功能A的程序集或者类。
基于 SailingEase WinForm 框架的事件发布订阅:
模块B获得事件聚合服务,向事件聚合器订阅事件 ProjectOpenedEvent,当有其它事件发布者,如模块A,向事件聚合器发布事件 ProjectOpenedEvent时,模块B将得到通知从而执行相关代码。
模块A与模块B,或更多的此事件订阅、发布者,不存在任何依赖,引用关系,它们通过事件聚合服务发布,订阅事件。
例如我们上文中的 StartPageModule 订阅了两个事件,ApplicationRunEvent 和 ProjectOpenedEvent。
我们的 StartPageModule 无需关心谁是事件 ApplicationRunEvent 和 ProjectOpenedEvent 的发布者,也无需关系发布者将在什么情况下,什么时间发布这两个事件,StartPageModule 只需做好响应这两个事件的准备即可。
而我们的事件发布者,也无需关心谁是事件的订阅者,只需在应该发布事件的时候,向事件聚合器发布对应的事件即可。
下面的代码发布了事件 ApplicationRunEvent。
通过事件聚合机制,模块间的事件发布,响应实现了完全的解耦合,模块与模块间不存在任何引用,依赖的关系。即使将某个事件的订阅者模块或发布者模块暂时的移出系统,其它模块都不会受到影响。
通过服务在不同的模块间交互数据或调用功能
在过去单一式应用程序中,当不同的功能点间需要交互数据或存在功能调用时,通常在编码时就将它们放在一起,有经验的程序员或许会将不同的功能点放在不同的程序集中,但依然避免不了程序集间互相引用所造成的高度耦合与依赖。
在 SailingEase WinForm 框架中,通过服务的概念来解决模块间的互操作问题,使模块间不存在任何依赖与引用。
通过一个简单的例子就能了解,例如我们的模块A提供了客户的基本信息管理功能,而模块B专门用于处理与客户的联系记录。如何在模块A中需要列出指定客户的联系记录?
我们为客户联系记录管理模块(模块B)设计一个服务接口 ICustomerContactService,我们将该接口定义在一个所有模块都已知的基础程序集 Infrastructure 中,然后我们在模块B中实现此接口,实现接口中定义的用于提供联系记录信息的方法,并将实现后的服务注册到 SailingEase WinForm 框架的服务容器中。
模块A在需要列出客户的联系记录信息时,只需从服务容器中取出 ICustomerContactService,即可通过该接口调用其所提供的方法获取信息,而不必关系接口的实现者与提供者是谁。
SailingEase WinForm 框架为您实现了许多用于开发基于.NET 平台的 Windows应用程序所需的服务,如多文档窗口管理,上下文菜单管理,系统环境相关的服务等等。基于这些服务,您将轻易获得质量可靠,扩展性强,低耦合度可配置化的专业Windows应用程序。
丰富的控件包
WinForm 控件开发一直是基于.NET Windows应用程序开发的难点之一,开发人员水平的参差不齐,Web的流行,使企业或开发团队想找到精于WinForm控件开发的人才非常困难,我们的团队在人才招募时,精通GDI+的.NET开发人员很难遇到。而我们的SailingEase WinForm开发框架为您提供了最常用也最实用的控件包: 如 DataGridView美化,ComboBox 美化以及专门开发的用于复杂情况的 ComboBox,用于图像缩略图浏览的ImageView。
在此向您简要介绍几种具有代表性的控件效果和我们实现方法的简要说明
DataGridView
我们为微软原生 DataGridView 提供了美化和搜索/替换等功能。
经美化后的DataGridView使您的应用程序具有相当专业水平的视觉效果。此外,我们提供了单元格搜索替换,背景水印文本,带有禁用效果的CheckBox列,以及通过对象类型或属性值映射从而显示不同图像的Image列。
部分源代码结构如下:
如何根据项目需要调整DataGridView的视觉效果?通过DataGridViewRendererTheme类:
通过独立的渲染机制,如何定制您自己的DataGridView视觉效果相信您已一目了然。
如何为DataGridView 实现搜索/提换功能?如果您想将此功能集成到现有项目中是否需要大幅度更改现有代码?并不需要,您只需要在您现有的使用 DataGridView的代码中添加一行代码:
DataGridViewSearchPresenter searchPresenter = new DataGridViewSearchPresenter(dataGridView);
即可调用 DataGridViewSearchPresenter 中提供的公开方法来实现各种对单元格的搜索替换功能。像这样在面像对象编程中使用对象复合的方式来提供功能的例子在我们的框架中有许多。
ComboBox
我们除了提供微软原生ComboBox的加强版本之外,完全重新实现了一个更加美观功能更强的ComboBox。
我们提供的ComboBox除了具有效好的视觉效果,对于开发人员来讲,具有极高的扩展性。定制外观的方式与上文中提到的DataGridView外观定制方法类似,这里主要讲如何扩展ComboBox。首先看一看源代码结构图:
从源代码结构中可以看出我们的 ComboBox 是一个复合控件,点击 ComboBox 时所呈现的列表,使用的是 SEListView来实现的。通过 SEComboSelectorTheme类,您可以定制个性化的视觉效果,而通过 SEListView 提供的 Layout 功能,您可以实现任意效果的下拉列表。这个任意效果的 Layout 实现,到底可以实现什么,我们不妨看一看用于呈现图像的ImageView控件。
ImageView 控件本身就是一个 SEListView,但我们为其实现了专门用于呈现图像的 Layout,在这个复杂的Layout实现中,我们定义了列表项的呈现方式,提供了框选,单选,反选和键盘操作功能。如果您希望在ComboBox 展开时呈现图像缩略图供用户选择,应用这个 Layout 即可。
使用SEListView 的Layout功能和 Theme功能,几乎可以实现任意效果和功能的列表。
ExtendedWebBrowser
即使是Windows应用程序,也许某些情况下也不可避免与Web页面产生交互。
此处点击 Web 页面中的 “Open Project” 或 “New Project” ,会调用WinForm应用程序中对应的功能。
将Web页面集成到您的项目中,除了业务逻辑与其它Web项目可能产生的交互外,使用Web页面来替代一些Windows界面,不仅美观,其设计实现的难度比使用GDI+的WinForm 控件要大大降低,您只需要美工设计传统网页即可。
AddressBar
使用类似Windows7的地址栏为您的应用程序做导航,不但结构清晰,功能性强,同时也获得了专业级别的导航效果。
其它典型WinForm控件:
专业标题栏效果
菜单,上下文菜单美化
命令行效果
向导框架
以上列出了部分具有代表性的部分WinForm控件,尚有部分外观改动较小只是功能性增强的控件没有列出,我们的控件都经过专业的设计和细致的实现,相信您能够从我们上文中的源代码结构和介绍中有所了解,使用 SailingEase WinForm 框架提供的控件,能够大大提高您的应用程序开发效率和专业化水平,使您的项目团队在开发WinForm应用时轻松自如。
丰富的组件包
SailingEase WinForm 开发框架提供了大量的功能组件,这些组件可能多数没有直接反应在项目的用户界面上,但却是框架中的重要组成部分,如:基于HTTP的应用程序自动升级更新,AES,DES,MD5加密算法封装,为提高反射性能的高速缓存,对象属性值快照/还原功能,Attribute相关功能,对类型(Type)功能的加强,XML、ZIP流/文件包的处理,正则表达式相关功能,TCP通信的封装实现等等。
部分源代码结构:
SailingEase WinForm 框架中所有的组件皆是在多年的项目实践中提炼完善而来,能够解决很多WinForm应用程序开发中遇到的问题,能够提高您的项目开发效率,缩短50%以上的开发周期,极好的节约您的项目成本,而框架本身良好的设计实现,亦能够大大提升或加强您项目的健壮性。