幾年前參與了一個面向學校的人事管理軟件的開發,基於WinForm平台。今天主要想談一談其中關於控件的使用經驗。這個項目我們大量使用了第三方控件。由於這個產品的生命周期很長,我們在設計時要考慮表現層可能會有不同的形式,比如現在是WinForm以後可能會是WPF或者Web。另外也要考慮控件版本升級對產品的影響降到最低。
基於上面的想法,我們通過兩點來解決這些問題。MVP模式+控件封裝,可以開發出更具彈性的View層。它能更有效的應對UI變化,包括控件升級這種小的UI變化,甚至是平台遷移等這種大的UI變化。
MVP(Model-View-Presenter)模式主要是為了表現層解耦,如果表現層的職責不單一,那麼有控件升級或者UI層使用新的平台時花費的成本就會很高。使用MVC之後只有View層和控件相關,Presenter層實現UI邏輯。當View層變化時(控件升級或者新的UI形式),Presenter層、Model層都會被重用。
控件封裝可以以一種統一的方式使用控件。所有使用到的控件全部封裝即使是.Net控件也進行封裝,不允許使用任何原生控件,只能使用封裝後的控件。我們對控件的進行深度封裝,將很多功能在控件中實現。
我們以表格功能為例來介紹我們如何進行控件封裝。在我們項目中對表格功能的要求非常高,一方面功能要非常強大,而且具有很強的定制開發能力,另一方面要求表格控件具有良好的性能。下面通過幾個UI了解我們對表格的要求。
圖1
圖2
圖3
圖1和圖2展示的是職員一覽畫面中,我們不僅僅加載出了職員信息,而且加載了職員的照片,需要一次性從數據庫中加載幾千張職員照片,對表格的性能要求很高。
圖3展示以時間軸方式顯示的職員狀態。點擊放大、縮小按鈕,時間刻度的粒度也會發生變化,並重新刷新數據。
通過以上的兩個示例可以看出我們對表格的性能和擴展性要求很高。我們綜合對比了Spread,FlexGrid,MutilRow這幾種表格控件,最終我們選擇了葡萄城公司的Component產品中的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方法。根據自己的需求增加對各種特殊鍵的處理。
通過對控件進行全面的封裝,當有新的需求或者有需求變更時和控件相關的功能我們只需在封裝的控件中進行修改即可。