几年前参与了一个面向学校的人事管理软件的开发,基于WinForm平台。今天主要想谈一谈其中关于控件的使用经验。这个项目咱们大量使用了第三方控件。因为这个产品的生命周期很长,咱们在设计时要考虑表现层可能会有不一样的形式,好比如今是WinForm之后可能会是WPF或者Web。另外也要考虑控件版本升级对产品的影响降到最低。数据库
基于上面的想法,咱们经过两点来解决这些问题。MVP模式+控件封装,能够开发出更具弹性的View层。它能更有效的应对UI变化,包括控件升级这种小的UI变化,甚至是平台迁移等这种大的UI变化。ide
MVP(Model-View-Presenter)模式主要是为了表现层解耦,若是表现层的职责不单一,那么有控件升级或者UI层使用新的平台时花费的成本就会很高。使用MVC以后只有View层和控件相关,Presenter层实现UI逻辑。当View层变化时(控件升级或者新的UI形式),Presenter层、Model层都会被重用。函数
控件封装能够以一种统一的方式使用控件。全部使用到的控件所有封装即便是.Net控件也进行封装,不容许使用任何原生控件,只能使用封装后的控件。咱们对控件的进行深度封装,将不少功能在控件中实现。性能
咱们以表格功能为例来介绍咱们如何进行控件封装。在咱们项目中对表格功能的要求很是高,一方面功能要很是强大,并且具备很强的定制开发能力,另外一方面要求表格控件具备良好的性能。下面经过几个UI了解咱们对表格的要求。this
图1spa
图2设计
图3code
图1和图2展现的是职员一览画面中,咱们不单单加载出了职员信息,并且加载了职员的照片,须要一次性从数据库中加载几千张职员照片,对表格的性能要求很高。orm
图3展现以时间轴方式显示的职员状态。点击放大、缩小按钮,时间刻度的粒度也会发生变化,并从新刷新数据。xml
经过以上的两个示例能够看出咱们对表格的性能和扩展性要求很高。咱们综合对比了Spread,FlexGrid,MutilRow这几种表格控件,最终咱们选择了FlexGrid控件。
图4
图4是咱们基于FlexGrid进行了深度封装的类图。在项目中不容许直接使用FlexGrid,只能使用咱们封装后的JijiEditGrid等控件。咱们以JijiBaseGrid为例来看看咱们是如何进行封装。
下面节选了咱们JijBaseGrid中的一些功能。
功能 |
描述 |
处理 |
Option机制 |
记忆列宽、字段顺序等 |
方法:LoadOption、SaveOption |
列头菜单 |
字段显示/隐藏 |
属性:NeedHeaderMenu(设计时不可用) |
统一管理颜色 |
Grid的配色是否随着Form走 |
属性:IsColorManaged(设计时可用) 接口:IColorControl |
拷贝到剪贴板 |
根据Option进行特殊处理,分隔符、日期格式等 |
方法:CopyGridRangeToClip(RealText realText) 事件:public event EventHandler DataCopy = delegate { }; |
特殊键 |
F9:自动调整列宽 Ctrl+A:全选 Ctrl+C:复制 Delete:删除 PageDown:数据不满一页,PageDown按下时,跳转到最后一行 |
事件处理函数:override void OnKeyDown |
咱们仅仅选择“特殊键”这个功能,看看在OnKeyDown中的处理。这里主要处理各类特殊按钮,好比F9等。
/// <summary> /// Grid的按键押下事件 /// </summary> /// <param name="e">参数</param> protected override void OnKeyDown(KeyEventArgs e) { base.OnKeyDown(e); // 记录Control键是否按下 this.controlDown = e.Control; switch (e.KeyCode) { case Keys.F9: if (this.CanF9) { // 列宽自动调整 Color oldColor = this.BackColor; this.AutoSizeCols(); this.BackColor = oldColor; } break; case Keys.Delete: if (this.CanDeleteRow()) { // 处理行删除事件 this.RowDelete(this, new EventArgs()); } else if (this.CanDeleteCellRange()) { // 处理Cell范围选中删除事件 this.CellRangeDelete(this, new EventArgs()); } break; case Keys.A: if (e.Control) { // ctrl + A鍵的操做(全選) int rowCount = this.GetRowCount(); if (rowCount > this.Rows.Fixed) { this.isControlA = true; this.Select(this.Rows.Fixed, this.Cols.Fixed, false); this.Select(this.Rows.Fixed, this.Cols.Fixed, rowCount, this.Cols.Count - 1, false); this.isControlA = false; } } break; case Keys.C: if (e.Control && this.AllowCopy && this.DataCopy != null && this.DataCopy.Target != null) { // ctrl + C鍵的操做(復制) this.DataCopy(this, new EventArgs()); } break; case Keys.PageDown: // 特殊处理数据不满一页,PageDown按下时,跳转到最后一行 this.DoPageDownEvent(); break; } }
默认的FlexGrid是没有这些功能的,咱们重写OnKeyDown方法。根据本身的需求增长对各类特殊键的处理。
经过对控件进行全面的封装,当有新的需求或者有需求变动时和控件相关的功能咱们只需在封装的控件中进行修改便可。