I would like to have a column of ComboBoxes in a DataGridView, which allows the user to freely input some text, which is collected in the dropdown menus, so that entering the same text in the next box is faster. I'd prefer not to use DataGridViewComboBoxColumn, unless I really have to.
The following code nearly does the job but has these issues:
After entering some new text and hitting return, the newly entered text is immediately replaced with the old value
But the new text is successfully added to the dropdown menus of all of the comboboxes
when I select this newly added text in one of the boxes, I get DataGridView-Exceptions complaining about an invalid value.
It seems the boxes somehow have for validation purposes a copy of the datasource which doesn't get updated?
public partial class Form1 : Form
{
List<string> data = new List<string>(); // shared data source for all ComboBoxes
private void checkData(string s) // check wether s in the list, add it if not, keep things sorted
{
if (data.Contains(s))
return;
data.Add(s);
data.Sort();
}
private void addCell(string s) // add a new cell to the grid
{
checkData(s);
DataGridViewComboBoxCell c = new DataGridViewComboBoxCell();
c.DataSource = data;
c.Value = s;
int i = theGrid.Rows.Add();
theGrid.Rows[i].Cells[0] = c;
}
public Form1()
{
InitializeComponent();
theGrid.ColumnCount = 1;
addCell("Foo");
addCell("Bar");
}
// handler to enable the user to enter free text
private void theGrid_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
ComboBox cb = e.Control as ComboBox;
if (cb != null)
cb.DropDownStyle = ComboBoxStyle.DropDown;
}
// handler which adds the entered text to the data source
private void theGrid_CellValidating(object sender, DataGridViewCellValidatingEventArgs e)
{
if (e.RowIndex < 0 || e.ColumnIndex < 0)
return;
checkData(e.FormattedValue.ToString());
}
}
After some tests, I am guessing that the individual combo boxes are not getting updated as you think they are. It appears that in the checkData
method, that the data
is updated with a new s
. This will visually update the combo box cell, however the DataSource
for each of the combos needs to be updated. Hence the DataError
exception when the new added value is selected.
Considering that each combo box cell is “independent” and not part of a DataGridViewComboBoxColumn
… then a loop through all the rows is necessary to update each combo box cell. I am not sure why a DataGridViewComboBoxColumn
would not be used here.
private void checkData(string s) // check wether s in the list, add it if not, keep things sorted
{
if (data.Contains(s))
return;
data.Add(s);
data.Sort();
// now because each cell is independent... we have to update each data source!
UpdateCombos();
}
private void UpdateCombos() {
foreach (DataGridViewRow row in theGrid.Rows) {
if ((!row.IsNewRow) && (row.Cells[0].Value != null)) {
string currentValue = row.Cells[0].Value.ToString();
DataGridViewComboBoxCell c = new DataGridViewComboBoxCell();
c.Value = currentValue;
c.DataSource = data;
row.Cells[0] = c;
}
}
}
Using the posted code, A call to UpdateCombos
is added to the checkData
method. This method as expected loops through all the rows in the grid and replaces each combo box with the updated data. I will not disagree that it may be prudent to replace the data source, however I would use a combo box column, which the code below does. With this change, the UpdateCombos
is not needed and simply update the combo box column.
The DataGridViewComboBoxColumn
is exposed since the data source is updated frequently.
private List<string> comboData;
private DataGridViewComboBoxColumn comboColumn;
private void Form2_Load(object sender, EventArgs e) {
comboData = new List<string>();
comboData.Add("Foo");
comboData.Add("Bar");
comboColumn = new DataGridViewComboBoxColumn();
comboColumn.DataSource = comboData;
theGrid2.Columns.Add(comboColumn);
theGrid2.RowCount = 3;
}
private void checkData2(string s) {
if (!comboData.Contains(s)) {
comboData.Add(s);
comboData.Sort();
comboColumn.DataSource = null;
comboColumn.DataSource = comboData;
}
}
Hope that helps