闲话WinFrom与WPF(2) 控件篇——ComboBox

来源:互联网 发布:室内健身器材 知乎 编辑:程序博客网 时间:2024/05/17 22:47

下拉框是一个很经典的控件,网上也有不少对于下拉框控件的扩展,其中包括与TreeView结合,做成树形选择;与DataGrid结合,做成列表选择;又或增加几个按钮来达到快捷编辑集合,选中项特殊显示等等。
对于复杂的组合控件,我这里就不必多说,只说说原生ComboBox的一些应用。

原文链接:http://nanqi.info/blog/2013/03/29/winform-wpf-2/

绑定数据源


简单的给Items添加项我就不说了,比如一些类型的选择多数使用这种,如一个员工的职位,是程序猿呢,还是项目经理。
但往往这种职位,是需要考虑扩展的,也就是说这个职位也要可维护的,所以一般的软件做法,在数据库中以字典表的形式去保存这类情况,而数据库设计中又有这么一个要求,每个表都要有不重复的ID做主键,所以经常看到如此的表结构:

GUID    varchar(50)Name    varchar(50)...


那么,将该数据展示在ComboBox中,可能就需要展示的是Name,而你只想获取选择的GUID。
所幸不管在WinForm还是WPF中,更或是在web开发中,即使纯Html的ComboBox,都有这种选择的一个值,而可能实际获得的是另一个值。
WinForm中指定DisplayMemberValueMember属性,WPF中指定DisplayMemberPathSelectedValuePath属性,可以很简单的实现上述要求。
而如果当这个字典表很大,或者客户需要给这种职位、类别之类的东西编号的时候,这时可能需要在显示的时候既显示编号,又显示名称,这时的表结构可能会这样:

GUID    varchar(50)No      varchar(50)Name    varchar(50)...


如果不拘于形式而言,一般完全可以使用No替换GUID,不管怎样,现在就是想要既获得主键列的值,又想显示No + Name的形式。

重写ToString的一种实现方式


一般如此数据,都会有一个实体层(Model/Entity),比如上例,这个实体层可能看起来像这样:

public class UserModel{    public string GUID { get; set; }    public string No { get; set; }    public string Name { get; set; }}


假设我们从数据源中获取了一个UserModel的集合(如List<UserModel>),将该集合直接绑定倒ComboBox中,而不指定它的那几个属性,那么他可能显示的是UserModel的类名全称,而为什么显示的是类的全名而不是其他呢,其实这里调用的是UserModelToString方法,而现在UserModelToString继承自objectobjectToString方法正是返回当前类的全名。
知道了这点,我们想让显示No + Name的形式就好办的多了,我们重写UserModelToString方法,不指定ComboBox的上述几个属性,直接绑定数据源,ToString方法可能会这样:

public void ToString(){    return No + "---" + Name;}


但随即你就会发现WinForm有一个问题,如果你没有指定DisplayMember属性,而是只指定ValueMember属性,那么他会将ValueMember属性也当成DisplayMember属性去处理,而不会调用ToString方法。

因为WinForm的这个问题,我们可能会使用另一种万能方式去获取选择项的某列值——使用SelectedItem属性。之所以现在才说,是因为SelectedItem属性是一个object类型,这样在我这个程序里,可能是UserModel,在另一个程序里,可能是DataViewRow,虽然万能,但实现不够优雅。

添加一个属性


这点很简单,很正常,但是有些人认为不应该修改Model层的属性,Model层应该和数据库字段一一对应,而且堂而言之,为工具生成Model层替换方便...
这里我也表示你下自己的观点,本人十分反感如此说法,正如在使用MVVM时不能在后台写代码一样反感,将原本一个事件,一行的代码,复杂的弄在xaml文件中去实现。
添加一个属性,这种方式没有任何错误,如果使用MVVM,这个属性可以写在VM中,这个属性可能是这样:

public string DisplayName{    get     {         return ToString();    }}


也可以不重写ToString,而是把ToString中的代码移过来。

自动提示(AutoComplete)


关于自动提示,ComboBox其实都是天生就支持的,只是WinForm里做的功能多一点,而WPF功能少一点(只是定位)。
当然,这只能说WPF比WinForm灵活,扩展方便。

WinForm中的自动提示有专门的属性去做,如果你按类型排序,这几个类型都在杂项中,都以AutoComplete为前缀。
其实这里的AutoCompleteSource属性定的很鸡肋,我用的基本也只有ListItemsCustomSource两个选项,而且本人认为CustomSource使用有点重复的概念,几乎没有人想在提示的时候使用一个数据源,而选择的时候使用另一个数据源。
当然,也不排除各种变态的需求,但多做一点总比少做一点要好么,而也正是这点想法的分歧,铸就了WPF的反其道而行之。

本来想写专文来说说这个,但是自己WPF所知甚浅,惟恐误人子弟,这里就随便扯两句,以WPF的ComboBox为例,也不算脱离主题。

WPF的ComboBox其实是一个复杂控件,有一个ToggleButton用来表示下拉按钮,一个Popup用来表示下拉框,在编辑状态(IsEditable="True"),还会有一个TextBox,所以,如果WPF没有提供一个ComboBox,照道理来说,是很容易自己写一个ComboBox的。
WPF中很多控件都是这样,可能都是使用Primitives命名控件下的控件组合而成。

在WPF中如果自带的自动提示功能有点简单,只要设置3个属性即可:IsEditableStayOpenOnEditIsTextSearchEnabled,而这种自动提示只是简简单单的一个定位功能,很多时候我们要的是筛选,而不是定位。

要实现WPF中ComboBox的自动提示,方式可以有很多,假设咱们不考虑如何去实现对于数据源的筛选,而只是实现界面中当ComboBox的Text改变时,让其筛选,并展示下拉框(Popup)也不是一个简单的事情。
其中一个原因是由于WPF中一个问题:

当设置ComboBoxIsDropDownOpen属性为True时(让ComboBox展开下拉框),TextBox中的文本被全选。

其实WPF中的问题很多,又何止这一个。
当然,正式因为WPF的简单性,才使得这些问题(Bug)都可以自己解决,所以很多东西,其实根本不是问题。


写着写着,就写多了,至于如何实现WPF中ComboBox的自动提示,我看还是留在以后去细讲,这篇就此结束。

原创粉丝点击