Winform-绑定自定义类型对象下拉列表到DataGridViewComboBoxColumn
微软文档里自带的绑定对象到DataGridView示例,仅仅示例了枚举类型填充下拉列表。
关键代码:
DataGridViewComboBoxColumn CreateComboBoxWithEnums()
{
DataGridViewComboBoxColumn combo = new DataGridViewComboBoxColumn();
combo.DataSource = Enum.GetValues(typeof(Title));
combo.DataPropertyName = "Title";
combo.Name = "Title";
return combo;
}
关键就是设置列对象的DataSource和DataPropertyName属性。
但是对于自定义类型,没有设置ToString()重载方法,因此必须通过设置DisplayMember属性来指定列的显示属性。
例如存在一个Title类
class Title
{
public string Name{get;set;}
}
有一个类使用了Title类作为它的一个属性:
private class Knight
{
private string hisName;
private bool good;
private Title hisTitle;
public Knight(Title title, string name, bool good)
{
hisTitle = title;
hisName = name;
this.good = good;
}
public Knight()
{
hisTitle = Title.Sir;
hisName = "";
good = true;
}
public string Name
{
get
{
return hisName;
}
set
{
hisName = value;
}
}
public bool GoodGuy
{
get
{
return good;
}
set
{
good = value;
}
}
public Title Title
{
get
{
return hisTitle;
}
set
{
hisTitle = value;
}
}
}
当我们需要自定义一个Title列表作为可选的下拉列表时,对于此Knight.Title的属性对应的编辑列,按照如下的方式设置会提示Cell类型不正确。
private List titles = new List(){new Title(){Name = "Sir"},new Title(){Name = "King"}};
DataGridViewComboBoxColumn CreateComboBoxWithEnums()
{
DataGridViewComboBoxColumn combo = new DataGridViewComboBoxColumn();
combo.DataSource = this.titles;
combo.DisplayMember = "Name";
combo.DataPropertyName = "Title";
combo.Name = "Title";
return combo;
}
private void EnumsAndComboBox_Load(object sender, System.EventArgs e)
{
// Populate the data source.
bindingSource1.Add(new Knight(new Title(){Name = "King"}, "Uther", true));
bindingSource1.Add(new Knight(new Title(){Name = "King"}, "Arthur", true));
bindingSource1.Add(new Knight(new Title(){Name = "Sir"}, "Mordred", false));
bindingSource1.Add(new Knight(new Title(){Name = "Sir"}, "Gawain", true));
bindingSource1.Add(new Knight(new Title(){Name = "Sir"}, "Galahad", true));
// Initialize the DataGridView.
dataGridView1.AutoGenerateColumns = false;
dataGridView1.AutoSize = true;
dataGridView1.DataSource = bindingSource1;
dataGridView1.Columns.Add(CreateComboBoxWithEnums());
// Initialize and add a text box column.
DataGridViewColumn column = new DataGridViewTextBoxColumn();
column.DataPropertyName = "Name";
column.Name = "Knight";
dataGridView1.Columns.Add(column);
// Initialize and add a check box column.
column = new DataGridViewCheckBoxColumn();
column.DataPropertyName = "GoodGuy";
column.Name = "Good";
dataGridView1.Columns.Add(column);
// Initialize the form.
this.Controls.Add(dataGridView1);
this.AutoSize = true;
this.Text = "DataGridView object binding demo";
}
因为设置了DisplayMember,必须设置ValueMember来指定下拉选择后设置此单元格对应的值属性。如果遇到需要将下拉列表选择的自定义类型对象(Title)设置为列表绑定对象(Knight)的属性值,无论ValueMember设置为null还是"",都无法返回自定义类型对象自身。
解决办法搜索了很多,比较简单的办法是通过在Title中添加一个指向自身的属性,例如:
public Title Self{get=>this;}
然后设置combo.ValueMember="Self"获取Title对象本身,缺点要修改Title的类结构。
进阶方法是构建一个匿名类,一个属性保存显示属性,一个属性保存Title对象:
combo.DataSource = this.titles.Select(t=>new(){Name = t.Name,Self=t}).ToList();
combo.DisplayMember = "Name";
combo.ValueMember = "Self";
缺点是匿名方法比较高版本的C#才有,取代方式是使用Tuple
combo.DataSource = this.titles.Select(t=>new Tuple(t.Name,t)).ToList();
combo.DisplayMember = "Item1";
combo.ValueMember = "Item2";