Reputation: 183
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;
}
Name
"A" and "B" - instead it looks to use the ToString()
: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:
comboBox1.DataSource = dataGridView1.Columns;
the Display/ValueMembers are both ""
comboBox1.DisplayMember = nameof(DataGridViewColumn.Name);
the DisplayMember is still ""
image of debugger stopped after setting displaymember - please embedcomboBox1.ValueMember = nameof(DataGridViewColumn.Index);
the DisplayMember is set to "Index"
and an exception is thrown
image of debugger stopped after setting valuemember - please embedTweaking 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
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