I want a column of a DataGridView
to use a ComboBoxStyle.DropDown
style ComboBox, where the user can either select one of the entries in the drop-down, or type arbitrary text.
At the moment, I'm using the code from this answer and I can type freely into the text box part of the ComboBox, but if I type something that isn't in the drop-down list then it isn't committed back to the data source and the field reverts to the original selection. Furthermore, if I programmatically set the text to something not in the drop-down list I get a DataError
event "DataGridViewComboBoxCell value is not valid."
I'm using data binding; the DataGridView
itself is bound to a BindingList<T>
.
Unlike this question I don't want the free text added to the drop-down list.
To be clear, the column data type is string
and I don't want it validated against the drop-down list of the ComboBox (or anything else for that matter).
(Do I have to create my own custom DataGridViewColumn
descendent as described in How to: Host Controls in Windows Forms DataGridView Cells?)
I found a simple, if verbose, answer. (But I'd still like to know if there is a way to do this with the standard DataGridViewComboBoxColumn
type.)
I followed the method in How to: Host Controls in Windows Forms DataGridView Cells. My full solution is too long to post here, but I can summarise the changes to make it use a ComboBox
instead of the example's DateTimePicker
control.
Rename the three classes DropDownComboBoxColumn
, DropDownComboBoxCell
, and DropDownComboBoxEditingControl
respectively.
Replace DateTime
everywhere with string
.
Add property public ComboBoxStyle DropDownStyle { get; set; }
to DropDownComboBoxColumn
to allow the calling code to set the drop-down style.
Remove code from DropDownComboBoxCell
constructor.
Remove code from DropDownComboBoxEditingControl
constructor.
Make DropDownComboBoxEditingControl
derive from ComboBox
instead of DateTimePicker
.
Replace OnValueChanged
with OnTextChanged
to account for the different naming in ComboBox
versus DateTimePicker
.
Make the EditingControlFormattedValue
property get and set the inherited Text
property (instead of Value
) and there is no parsing needed.
Make ApplyCellStyleToEditingControl
set ForeColor
and BackColor
instead of CalendarForeColor
and CalendarMonthBackground
.
Make EditingControlWantsInputKey
also claim F4 so it can be used to open and close the drop-down.
Add the following code to PrepareEditingControlForEdit
:
DropDownComboBoxColumn col = _dataGridView.Columns[_dataGridView.CurrentCell.ColumnIndex] as DropDownComboBoxColumn;
if (col == null)
{
throw new InvalidCastException("Must be in a DropDownComboBoxColumn");
}
DropDownStyle = col.DropDownStyle;
// (If you don't explicitly set the Text then the current value is
// always replaced with one from the drop-down list when edit begins.)
Text = _dataGridView.CurrentCell.Value as string;
SelectAll();
Handle the DataGridView
's EditingControlShowing
event as in OhBeWise's answer to a related question to set up the drop-down items and if desired the auto-completion mode:
private void dataGridView1_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
ComboBox box = e.Control as ComboBox;
if (box != null)
{
box.AutoCompleteSource = AutoCompleteSource.ListItems;
box.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
box.DataSource = _dropDownItems;
}
}
If you want the same drop-down items for all rows then you could always make this a property of DropDownComboBoxColumn
like DropDownStyle
and set it up in PrepareEditingControlForEdit
to avoid having to handle EditingControlShowing
.