WinForm下ComboBox获取绑定对象集的SelectedValue补充
在上文《WinForm下ComboBox设定SelectedValue总结》中,我列举了出现不能正常获取SlectedValue的一些方法。原文写得比较乱,引起读者的理解分歧,在此表示歉意。现将自己的思路重新整理一下。
注意:本文只限定绑定对象集,而不涉及绑定DataSet或DataTable的情况
(如果绑定DataSet时出现SelectedValue为System.Data.DataRowView的错误,或者在取SelectedVlaue的值转换时出现"不能将对象转换为字符串",请参看这里:Combobox出现System.Data.DataRowView的原因,以及指定ValueMember的时机问题http://blog.csdn.net/lubiaopan/archive/2010/09/30/5915774.aspx)
一、准备工作:
为了方便起见,我们暂时将公用方法和变量简化一下:
一个Area类:
<!-- Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> #region Area public class Area { private string m_Area_ID; public string Area_ID { get { return m_Area_ID; } set { m_Area_ID = value; } } private string m_Area_Name; public string Area_Name { get { return m_Area_Name; } set { m_Area_Name = value; } } private double m_Area_Order; public double Area_Order { get { return m_Area_Order; } set { m_Area_Order = value; } } } #endregion [Serializable] public class AreaLists : List < Area > { private int _maxItems = 0 ; public int MaxItems { get { return this ._maxItems; } set { this ._maxItems = value; } } }
一个WinForm,命名为frmMain
一个ComboBox,名为cbList
两个button,名为btnSetCombobox和btnGetSelectedValu
一个Label,名为lbResult,初始值为Unknown
定义公共变量:
<!-- Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> AreaLists ac; public AreaLists GetTestArea() { AreaLists al = new AreaLists(); for ( int i = 1 ; i < 11 ; i ++ ) { Area a = new Area(); a.Area_ID = i.ToString(); a.Area_Name = " 第 " + i.ToString() + " 名 " ; al.Add(a); } return al; }
几个用到的方法:
方法一:SetValueByValue(通过ValueMember设置)
<!-- Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> private void SetValueByValue() { Area a = new Area(); a.Area_ID = " 8 " ; a.Area_Name = " 第8名 " ; cbList.SelectedValue = a.Area_ID; lbResult.Text = cbList.SelectedValue.GetType().ToString() + " : " + cbList.SelectedValue.ToString(); }
方法二:SetValueByItem(通过SelectedItem设置)
<!-- Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> private void SetValueByItem() { Area a = new Area(); a.Area_ID = " 8 " ; a.Area_Name = " 第8名 " ; cbList.SelectedItem = ac.FindAll( delegate (Area ar) { return ar.Area_ID == a.Area_ID; })[ 0 ]; lbResult.Text = cbList.SelectedItem.GetType().ToString() + " : " + cbList.SelectedValue.ToString(); }
方法三:SetValueByText(通过FindString查找DisplayMember设置)
<!-- Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> private void SetValueByText() { Area a = new Area(); a.Area_ID = " 8 " ; a.Area_Name = " 第8名 " ; cbList.SelectedIndex = cbList.FindString(a.Area_Name); lbResult.Text = cbList.SelectedValue.GetType().ToString() + " : " + cbList.SelectedValue.ToString(); }
获取SelectedValue的值:
<!-- Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> private void GetValueByValue() { lbResult.Text = cbList.SelectedValue.GetType().ToString() + " : " + cbList.SelectedValue.ToString(); }
如下图:
frmMain的Page_Load事件:
<!-- Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> private void frmMain_Load( object sender, EventArgs e) { ac = GetTestArea(); cbList.DataSource = ac; cbList.ValueMember = " Area_ID " ; cbList.DisplayMember = " Area_Name " ; }
btnSetCombobox的Click事件:
<!-- Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> private void btnSetCombobox_Click( object sender, EventArgs e) { // 使用方法一: SetValueByValue(); // 使用方法二 // SetValueByItem(); // 使用方法三 // SetValueByText(); }
<!-- Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> private void btnGetSelectedValue_Click( object sender, EventArgs e) { GetValueByValue(); }
cbList的SelectedIndexChanged和SelectedValueChanged事件:
<!-- Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> private void cbList_SelectedIndexChanged( object sender, EventArgs e) { // GetValueByValue(); int test = 0 ; } private void cbList_SelectedValueChanged( object sender, EventArgs e) { // GetValueByValue(); int test = 0 ; }
二、测试结果(不考虑绑定结果集为空时的情况)
使用方法 | 1、未设置ComboBox的SelectedIndexChanged事件和SelectedValueChanged事件时SelectedValue类型及值 | ||
Page_Load时 | 设置SelectedValue | 读取SelectedValue | |
SetValueByValue | Null | 所选Area_ID对应的字符串 | 所选 Area_ID对应的字符串 |
SetValueByItem | Null | Area | 所选 Area_ID对应的字符串 |
SetValueByText | Null | 所选 Area_ID对应的字符串 | 所选 Area_ID对应的字符串 |
使用方法 | 2、仅设置ComboBox的SelectedIndexChanged事件SelectedValue类型及值 | ||
Page_Load时 | 设置SelectedValue | 读取SelectedValue | |
SetValueByValue | 第一个 Area_ID对应的字符串 | 所选 Area_ID对应的字符串 | 所选 Area_ID对应的字符串 |
SetValueByItem | 第一个 Area_ID对应的字符串 | Area | 所选 Area_ID对应的字符串 |
SetValueByText | 第一个 Area_ID对应的字符串 | 所选 Area_ID对应的字符串 | 所选 Area_ID对应的字符串 |
使用方法 | 3、仅设置ComboBox的SelectedValueChanged事件时SelectedValue类型及值 | ||
Page_Load时 | 设置SelectedValue | 读取SelectedValue | |
SetValueByValue | 第一个 Area_ID对应的字符串 | 所选 Area_ID对应的字符串 | 所选 Area_ID对应的字符串 |
SetValueByItem | 第一个 Area_ID对应的字符串 | Area | 所选A_ID对应的字符串 |
SetValueByText | 第一个 Area_ID对应的字符串 | 所选 Area_ID对应的字符串 | 所选A_ID对应的字符串 |
使用方法 | 4、同时设置ComboBox的SelectedIndexChanged事件和SelectedValueChanged事件时SelectedValue类型及值 | ||
Page_Load时 | 设置SelectedValue | 读取SelectedValue | |
SetValueByValue | 第一个 Area_ID对应的字符串 | 所选 Area_ID对应的字符串 | 所选 Area_ID对应的字符串 |
SetValueByItem | 第一个 Area_ID对应的字符串 | Area | 所选 Area_ID对应的字符串 |
SetValueByText | 第一个 Area_ID对应的字符串 | 所选 Area_ID对应的字符串 | 所选 Area_ID对应的字符串 |
三、结论(不考虑绑定结果集为空时的情况)
本次测试中还发现几个结论:
1、当ComboBox中无论是否设置SelectedIndexChanged事件和SelectedValueChanged事件,如果没有设置SelectedValue值或设置的值超过ValueMember的范围(即赋错值),则此时SelectedValue为第一个对象对应的 Area_ID值,所以,如果出现没有正确获取到想要的值,请检查是否设置或有没有赋了一个不存在于ValueMember集合的值。
2、最好设置ComboBox中的SelectedIndexChanged事件和SelectedValueChanged事件,并在事件中获取SelectedValue值。
有人会担心事件会不会触发?经测试,如果这两个事件同时设置,顺序为:
cbList.DataSource = ac;//这里会触发SelectedValueChanged事件和 SelectedIndexChanged事件各一次。 cbList.ValueMember = "Area_ID";这里会触发SelectedValueChanged事件和 SelectedIndexChanged事件各一次,还有一次SelectedValueChanged事件 cbList.DisplayMember = "Area_Name";这里会触发SelectedValueChanged事件和 SelectedIndexChanged事件各一次。 换句话说,设定两个事件任意一个即可,我建议用SelectedValueChanged事件 。
3、设置正确的绑定顺序。有人指出正确的顺序为:
cbList.ValueMember = "Area_ID";//这里会触发SelectedValueChanged事件一次
cbList.DisplayMember = "Area_Name";//这里不会触发任何事件
cbList.DataSource = ac;//这里会触发SelectedValueChanged事件和 SelectedIndexChanged事件各一次。
在本次测试中发现顺序其实对结果并没有影响,但是如果将ValueMembe在设置DataSource之前设置,触发事件大大减少!
所以建议DataSource设置前先设定ValueMember和DisplayMember。
4、如果设置错误的DisplayMember名称,(如本文中" Area_Name"误设为“ Area_Names”)则取ValueMember作为DisplayMember,如果ValueMember的名称也设置错误,(如本文中" Area_ID"误设为“ Area_IDs”)则获取SelectedValue时出错!
5、回到本文开头,如果绑定的不是对象集,而是DataSet或DataTable,则未设定SelectedValue时,取得的是一个DataRowView而不是null,请注意。