您的位置:首页 > 编程语言 > C#

C#实现的多列数据绑定组合框控件MultiColumnComboBoxEx

2009-02-01 14:37 696 查看
(原创文章,转载请注明来源:http://blog.csdn.net/hulihui)



0、前言

组合框ComboBox是一个十分常用的多功能窗体控件,兼具文本框(TextBox)与列表框(ListBox)两控件的特点,并独具特性AutoCompleteMode。但笔者在实际项目开发中往往感到如下方面的不足:

不能分别设置框高与项高,在调整项高ItemHeigth时也调整了组合框本身的高度;

绑定数据源时,只有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属性:

public new DrawMode DrawMode
{
get { return base.DrawMode; }
set
{
if (value != DrawMode.OwnerDrawVariable)
{
throw new NotSupportedException("Must be DrawMode.OwnerDrawVariable");
}
base.DrawMode = value;
}
}


上述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 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中计算下拉框与每个项宽度,代码如下:

protected override void OnMeasureItem(MeasureItemEventArgs e)
{
base.OnMeasureItem(e);
if (m_columnNames.Count == 0)  // 无绑定数据源
{
string item = Convert.ToString(Items[e.Index]) + "A";  // 增加一个字母
int width = (int)(e.Graphics.MeasureString(item, base.Font).Width);
if (width > m_maxItemWidth)
{
m_maxItemWidth = width;  // 项的最大宽度
}
}
else
{
for (int k = 0; k < m_columnNames.Count; k++)  // 计算每个列的最大宽度
{
string item = Convert.ToString(FilterItemOnProperty(Items[e.Index], m_columnNames[k]));
int width = (int)(e.Graphics.MeasureString(item, base.Font).Width + m_columnPadding + m_leftOffset);
m_columnWidths[k] = Math.Max(m_columnWidths[k], width);
}
}
e.ItemWidth = this.DropDownTotalWidth;
e.ItemHeight = m_itemDropDownHeight;
}


上述代码中,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!

附注:关于项为空(即Items.Count=0)的bug处理和框高项高的设置方法,请参考拙文MultiColumnComboBoxEx: An Extended Data-Bound Multiple Column ComboBox。

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风格时,精确了左边对齐。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐