1. 程式人生 > >C#實現的多列資料繫結組合框控制元件MultiColumnComboBoxEx

C#實現的多列資料繫結組合框控制元件MultiColumnComboBoxEx

0、前言

組合框ComboBox是一個十分常用的多功能窗體控制元件,兼具文字框(TextBox)與列表框(ListBox)兩控制元件的特點,並獨具特性AutoCompleteMode。但筆者在實際專案開發中往往感到如下方面的不足:

  1. 不能分別設定框高與項高,在調整項高ItemHeigth時也調整了組合框本身的高度;
  2. 繫結資料來源時,只有DisplayMember與ValueMember兩個屬性,不能呈現多列資訊。

在著名開源網CodeProject上找了年份較新的兩篇文章:A data-bound multi-column combobox(Nishant Sivakumar, 2007.7)介紹的資料來源繫結控制元件MultiColumnComboBox基本滿足要求,但不能獨立設定框高和項高;

Searchable MultiColumn ComboBox with Linked TextBox(Darryl Caillouet,2008.2) 介紹的同名控制元件功能強大,也沒有分開框高與項高,且不能指定多列的呈現順序。於是,借鑑這兩個開源倥件的主要技術與思路,在增補與完善部分功能後編寫出MultiColumnComboBoxEx控制元件,主要功能如下:

  • 多資料列顯示:可以在下拉框和文字框中顯示多列資訊;
  • 指定列與順序:可以指定需要顯示的資料來源列名,同時指定輸出順序;
  • 框高項高分離:可以分別設定文字框本身高度與下拉框的項高度;
  • 查詢函式ItemIndexOf:提供了代替Items.Index的資料項查詢函式ItemIndexOf;
  • RTL語言風格:支援一些國家或民族的RightToLeft(RTL)語言風格。

此外,還增補與完善了一些實現細節,例如:是否顯示分隔線、是否在框中顯示多列、下拉框文字垂直居中、獲取全部顯示列文字框、DropDownWidth/DropDownHeight計算、RTL時增加左邊寬度,等等。

1、MultiColumnComboBoxEx介紹

該控制元件派生自ComboBox,下面介紹其增加與過載(new)的一些屬性、功能和及使用:

  • ComboBoxHeight:組合框高度,該屬性取代了基類的ItemHeight,項高屬性則由ItemDropDownHeight代替;
  • DisplayColumnNames:
    資料來源繫結時,可以指定下拉框呈現的列名及順序,列名之間用逗號(,)或|分隔,不區分大小寫,列名之間可以有空格。例如,Employee Id,Name,Job,或employee id|name,job。該屬性為空時表示全部的列並按資料來源的列順序,列名錯誤時該列名無效;
  • 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,其函式原型如下:

  1. public int ItemIndexOf(string itemValue, bool ignoreCase, string columnName)
  2. public int ItemIndexOf(string itemValue, string columnName)
  3. public int ItemIndexOf(string itemValue, bool ignoreCase)
  4. 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 comboboxSearchable 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!