C#實現的多列資料繫結組合框控制元件MultiColumnComboBoxEx
0、前言
組合框ComboBox是一個十分常用的多功能窗體控制元件,兼具文字框(TextBox)與列表框(ListBox)兩控制元件的特點,並獨具特性AutoCompleteMode。但筆者在實際專案開發中往往感到如下方面的不足:
- 不能分別設定框高與項高,在調整項高ItemHeigth時也調整了組合框本身的高度;
- 繫結資料來源時,只有DisplayMember與ValueMember兩個屬性,不能呈現多列資訊。
在著名開源網CodeProject上找了年份較新的兩篇文章:A data-bound multi-column combobox(Nishant Sivakumar, 2007.7)介紹的資料來源繫結控制元件MultiColumnComboBox基本滿足要求,但不能獨立設定框高和項高;
- 多資料列顯示:可以在下拉框和文字框中顯示多列資訊;
- 指定列與順序:可以指定需要顯示的資料來源列名,同時指定輸出順序;
- 框高項高分離:可以分別設定文字框本身高度與下拉框的項高度;
- 查詢函式ItemIndexOf:提供了代替Items.Index的資料項查詢函式ItemIndexOf;
- RTL語言風格:支援一些國家或民族的RightToLeft(RTL)語言風格。
此外,還增補與完善了一些實現細節,例如:是否顯示分隔線、是否在框中顯示多列、下拉框文字垂直居中、獲取全部顯示列文字框、DropDownWidth/DropDownHeight計算、RTL時增加左邊寬度,等等。
1、MultiColumnComboBoxEx介紹
該控制元件派生自ComboBox,下面介紹其增加與過載(new)的一些屬性、功能和及使用:
- ComboBoxHeight:組合框高度,該屬性取代了基類的ItemHeight,項高屬性則由ItemDropDownHeight代替;
- DisplayColumnNames:
- DisplayMultiColumnsInBox:為true時在文字框中顯示多列資訊;
- DisplayVerticalLine:為true時在下拉框中顯示分隔線;
- DrawMode:基類的new屬性,只能設定為DrawMode.OwnerDrawVariable;
- DropDownStyle:基類的new屬性,只能是DropDown與DropDownList;
- ItemDropDownHeight:下拉框項的高度,代替基類的ItemHeight屬性;
- MaxDropDownItems:基類的new屬性,用於重算下拉框高度;
- TextDisplayed:多列顯示時獲取用逗號(,)分隔的顯示文字;
- Version:控制元件版本。
需要指出,上述new屬性是指類定義中重寫基類的某個屬性並附加關鍵字new,如下程式碼給出了DrawMode的new屬性:
上述new屬性繼承了基類的全部Attribute,如:預設值(DefaultValue)、說明(Description)、分類(Category),等等。
控制元件MultiColumnComboBoxEx提供了一個有4個過載版本的項查詢public方法ItemIndexOf,用來取代Items.IndexOf,其函式原型如下:
- public int ItemIndexOf(string itemValue, bool ignoreCase, string columnName)
- public int ItemIndexOf(string itemValue, string columnName)
- public int ItemIndexOf(string itemValue, bool ignoreCase)
- public int ItemIndexOf(string itemValue)
函式返回第一個查詢到的項的索引號,-1表示指定列沒有要查詢的值。函式的各個引數用途如下:
- itemValue:要查詢指定列的項值,省略columnName時表示查詢控制元件DisplayMember屬性指定的列;
- ignoreCase:是否忽略大小寫,預設true,即不區分大小寫;
- columnName:要查詢的列名稱,預設是控制元件屬性DisplayMember給出的列。
2、實現技術要點
定製多列組合框控制元件關鍵步驟包括:1)設定DrawMode為DrawMode.OwnerDrawFixed或DrawMode.OwnerDrawVariable;2)重寫OnDrawItem與OnMeasureItem方法,相關技術要點請參考文章A data-bound multi-column combobox與Searchable MultiColumn ComboBox with Linked TextBox。這裡介紹控制元件MultiColumnComBoBoxEx與它們的不同點,主要體現在:下拉框高度與寬度計算、文字框中顯示多列。
2.1 下拉框高度DropDownHeight計算
由於棄用基類屬性ItemHeight,使用自定義的ItemDropDownHeight,此時需要考慮下拉框高度計算問題:
- 計算DropDownHeight公式:base.DropDownHeight = base.MaxDropDownItems * m_itemDropDownHeight + m_bottomOffset。其中,固定偏移量m_bottomOffset = 2(無技術資料,俺猜出來的,呵呵。);
- 相關屬性更新時重算高度:更新三種屬性時需要呼叫私有函式SetDropDownHeight(),該函式重算高度並呼叫函式base.RefreshItems():更新了ItemDrowpDownHeight、ComboBoxHeight與MaxDropDownItems屬性值。其中,base.RefreshItems()函式將重新整理組合框的所有項,激發事件OnMeasureItem並計算每列的輸出寬度。
2.2 下拉框寬度DropDownWidth計算
在有無資料來源繫結時,下拉框寬度計算公式不同,在方法OnMeasureItem中計算下拉框與每個項寬度,程式碼如下:
上述程式碼中,m_columnNames是列名集合Collection<string>,儲存指定的列名,並按先後順序輸出各列。m_maxItemWidth是無資料來源時項的最大寬度,m_columnWidths[]陣列表示每列的最大寬度。this.DropDownTotalWidht屬性表示下拉框總寬度。
特別需要指出,只有呼叫base.RefreshItems()函式,才能在修改相關屬性時激發MeasureItem事件,歸納起來有如下屬性更改時需要重算項寬度與列寬度:
- DisplayColumnNames:更新了顯示列或順序
- DataSource:更新繫結的資料來源
- ComboBoxHeight:更新組合框高度
- ItemDropDownHeight:更新下拉框項的高度
- MaxDropDownItems:更新最大下拉項數
還需要指出,增加資料項(增加資料來源的資料項、無資料來源時增加Items項)時,將自動激發MeasureItem事件,重算各個寬度值。
2.3 在文字框中顯示多列
在DropDownStyle.DropDownList時,可以在文字框中顯示多列資訊,此時需要在OnDrawItem方法判斷是否繪製當前的文字框,方法是:判斷事件引數e.State是否具有DrawItemState.ComboBoxEdit位,即(e.State & DrawItemState.ComboBoxEdit) != 0 表示正在繪製文字框。
3、總結、版本與原始碼
實現了下拉框中顯示多列資訊,但在文字框中繪製多列資訊僅僅針對DropDownStyle.DropDownList風格,DropDownStyle.DropDown時無效。筆者沒有找到直接捕獲ComboBox的文字框繪製事件。雖然通過OnFormat可以定製輸出格式(FormattingEnabled為false該事件無效),但不能Paint/Draw輸出文字。另外,也沒有考慮組合框的另一個強大功能AutoCompleteMode(可以參考文章Searchable MultiColumn ComboBox with Linked TextBox及其原始碼)。歡迎使用、評論與建議MultiColumnComboBoxEx。
寒假過去兩週了,期間釋出了基於WebService的自升級框架WebSAUF 1.0:一個ClickOnce的替代方案 1.0,寫篇了現在看起來十分膚淺的文章:繫結資料來源時組合框ComBoBox.DrawItem的事件處理方法。因專案應用中感覺ComboBox不夠靈活,於是在已有控制元件基礎上改寫為MultiColumnComboBoxEx。現在,得抓緊時間做交通統計綜合歷史資料庫專案(HNJT-ISHDB)了,否則無法交差哦!uggah-muggah!
- MultiColumnComboBoxEx 1.0(C#2005), 2009年2月1日。下載V1.0原始碼與示例。
- MultiColumnComboBoxEx 1.1(C#2005), 2009年2月10日。下載V1.1(更新)原始碼與示例。
- MultiColumnComboBoxEx 1.2(C#2005), 2009年2月18日。下載V1.2原始碼與示例。
- 修改了焦點問題:點選另一個MultiColumnComboBoxEx的下拉圖示時,該控制元件沒有聚焦。
- 精細了RTL對齊:在RTL風格時,精確了左邊對齊。