XOR cyst
XOR cyst

Reputation: 183

Why can't I successfully bind a DataGridviewColumnCollection to a ComboBox.DataSource and specify a Display/ValueMember?

While trying to bind a combo up so that the user can select a column name from the dropdown and I retrieve the column index from the SelectedValue, I noted that the combo appears to ignore the given DisplayMember and ValueMember and instead uses the ToString()

Repro:

    

public Form1()
{
    InitializeComponent();

    var dt = new DataTable();
    dt.Columns.Add("A");
    dt.Columns.Add("B");
    dataGridView1.DataSource = dt;

    comboBox1.DisplayMember = nameof(DataGridViewColumn.Name);
    comboBox1.ValueMember = nameof(DataGridViewColumn.Index);
    comboBox1.DataSource = dataGridView1.Columns;
}

image of form - please embed

If the order of items is swapped:

comboBox1.DataSource = dataGridView1.Columns;
comboBox1.DisplayMember = nameof(DataGridViewColumn.Name);
comboBox1.ValueMember = nameof(DataGridViewColumn.Index);

C# complains the DisplayMember cannot be set. I'm even more confused because:

Tweaking the collection type doesn't produce any different result either:

comboBox1.DataSource = dataGridView1.Columns.Cast<DataGridViewColumn>().ToList();

The problem appears to be in relation to the DataGridViewColumn class attempting to bind. If the column collection is projected to an anoymous type with the same prop names, it works without a problem:

comboBox1.DataSource = dataGridView1.Columns.Cast<DataGridViewColumn>().Select(c => new { c.Name, c.ColumnIndex }).ToList();

I'm curious why DataGridViewColumn, apparently a class with public named props like any other, doesn't work directly in this binding scenario

Upvotes: 1

Views: 124

Answers (1)

Reza Aghaei
Reza Aghaei

Reputation: 125197

It's because those properties are not browsable.

It not something special with DataGridViewColumn class and the behavior is the same even for a custom created class. Those properties (Name and Index) are marked as [Browsable(false)] and when setting up databinding, the CurrencyManager.GetItemProperties just returns browsable properties.

If you start tracing the code from ValueMember, you will end up in some internal methods which check for browsable attributes.

The best workaround is the one that you also mentioned, shaping the result to a custom type:

comboBox1.DataSource = dataGridView1.Columns.Cast<DataGridViewColumn>()
    .Select(c => new { c.Name, c.ColumnIndex }).ToList();
comboBox1.ValueMember = "Index";
comboBox1.DisplayMember = "Name";

Upvotes: 1

Related Questions