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 = "<enter name>";
        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<Title> titles = new List<Title>(){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<string, Title>()元祖类

combo.DataSource = this.titles.Select(t=>new Tuple<string, Title>(t.Name,t)).ToList();
combo.DisplayMember = "Item1";
combo.ValueMember = "Item2";

posted on 2021-11-15 17:36  鹿果一夏  阅读(472)  评论(0编辑  收藏  举报

导航