Before setting datagridview with EditMode: EditProgrammatical the ComboBox appeared as it should.
After settings the EditMode: EditProgrammatical it requires 2-3 clicks on the arrow for the selection items to appear.
private void suggestButton_Click(object sender, EventArgs e)
{
var dict = getSuggestDict();
var dataGridViewComboBoxCell = new DataGridViewComboBoxCell
{
DataSource = dict.Keys.ToList();
};
dataGridView[selectedColumn, selectedRow] = dataGridViewComboBoxCell;
}
The function simplified a bit to avoid unnecessary complications.
You need to enable editing and set the focus to in question cell to make combo dropdown to open in single click.
private void suggestButton_Click(object sender, EventArgs e)
{
var dict = getSuggestDict();
var dataGridViewComboBoxCell = new DataGridViewComboBoxCell
{
DataSource = dict.Keys.ToList()
};
dataGridView[selectedColumn, selectedRow] = dataGridViewComboBoxCell;
dataGridView.CurrentCell = dataGridView.Rows[selectedRow].Cells[selectedColumn];
dataGridView.BeginEdit(false);
}
Edited: Moved setting current cell and enabling edit mode at the start of button click event handler to ensure its in edit mode when replacing combobox contents.
private void suggestButton_Click(object sender, EventArgs e)
{
dataGridView.CurrentCell = dataGridView.Rows[selectedRow].Cells[selectedColumn];
dataGridView.BeginEdit(true);
var dict = getSuggestDict();
var dataGridViewComboBoxCell = new DataGridViewComboBoxCell
{
DataSource = dict.Keys.ToList()
};
dataGridView[selectedColumn, selectedRow] = dataGridViewComboBoxCell;
}
Related
I have a DataGridView that has a ComboBox column and I must update each ComboBox's possible values when its drop down shows. I also must make the ComboBoxes capable of having custom typed values. When a new value is typed, it should be added to the list of possible values. The problem is that I get infinitely many DataError event triggers (error message boxes), I know how to handle it by just changing a field in the DataGridViewDataErrorEventArgs object, but I know it is not the correct way to handle it:
private void DataGridView1_DataError(object sender, DataGridViewDataErrorEventArgs e)
{
e.Cancel = false;
}
If I do it in the incorrect way, after selecting a value from the drop down or typing a new value, the CellValueChanged is triggered but the closed ComboBox does not display the current value but an already existing value (the first in the list).
In the following code the Form subclass is Form2, the initial values are stored in the str field and the UpdatePossibleValues method is called to update the possible values in all the ComboBoxes inside the only column in the data grid view, a DataGridViewComboBoxColumn:
public Form2()
{
InitializeComponent();
dataGridView1.EditingControlShowing += DataGridView1_EditingControlShowing;
UpdatePossibleValues();
}
internal List<string> str = new List<string>()
{
"val1",
"val2"
};
private void DataGridView1_EditingControlShowing(object sender,
DataGridViewEditingControlShowingEventArgs e)
{
if (dataGridView1.CurrentCell == null ||
dataGridView1.CurrentCell.OwningColumn == null ||
dataGridView1.CurrentCell.OwningColumn.Name != "column1")
{
return;
}
var combo = e.Control as DataGridViewComboBoxEditingControl;
if (combo == null)
{
return;
}
var cb = combo as ComboBox;
UpdatePossibleValues(cb);
cb.DropDownStyle = ComboBoxStyle.DropDown; // this makes the ComboBoxes editable
cb.Validating += Cb_Validating;
}
private void Cb_Validating(object sender, System.ComponentModel.CancelEventArgs e)
{
var cbo = sender as ComboBox;
string t = cbo.Text;
var cell = (DataGridViewComboBoxCell)dataGridView1.CurrentCell;
// add the value to the list if it is not there
if (!string.IsNullOrEmpty(t) &&
!cbo.Items.Contains(t))
{
str.Add(t);
UpdatePossibleValues(cbo);
cell.Value = t;
e.Cancel = false;
}
}
private void UpdatePossibleValues(ComboBox cb = null)
{
if (cb == null)
{
var col = dataGridView1.Columns[0] as DataGridViewComboBoxColumn;
col.Items.Clear();
foreach (string s in str)
{
col.Items.Add(s);
}
}
else
{
cb.Items.Clear();
foreach (string s in str)
{
cb.Items.Add(s);
}
}
}
Screenshots:
To dynamically add item to DataGridViewComboBoxColumn:
Hanlde EditingControlShowing and get the DataGridViewComboBoxEditingControl
Set editing control DropDownStyle to DropDown
Handle Validating event of editing control and make sure you attach the event handler just once.
Check if the Text of the editing control doesn't exists in the items:
Add it to data source of the column
Then reset data source of the column by setting it to null and assigning data source again.
Notes:
If you have multiple combo box, make sure you use different data sources for combo boxes and update corresponding data source in validating event.
If you handle the events using anonymous method, make sure you have a correct assumption about captured variables. To make it simple, you can handle the event using a normal method.
Example
The following example shows a DataGridView having two DataGridViewComboBoxColumn which for the second one, you can add new values by typing in the combo box at run-time.
To run the example, create a Form and drop a DataGridView on a new Form and just copy and paste the following code in the form:
private List<String> comboSource1;
private List<String> comboSource2;
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
comboSource1 = new List<string> { "A", "B" };
comboSource2 = new List<string> { "1", "2" };
var dt = new DataTable();
dt.Columns.Add("C1");
dt.Columns.Add("C2");
dt.Rows.Add("A", "1");
dt.Rows.Add("B", "2");
var c1 = new DataGridViewComboBoxColumn();
c1.Name = "C1";
c1.DataPropertyName = "C1";
c1.DataSource = comboSource1;
var c2 = new DataGridViewComboBoxColumn();
c2.Name = "C2";
c2.DataPropertyName = "C2";
c2.DataSource = comboSource2;
dataGridView1.Columns.AddRange(c1, c2);
this.dataGridView1.DataSource = dt;
dataGridView1.EditingControlShowing += dataGridView1_EditingControlShowing;
dataGridView1.EditMode = DataGridViewEditMode.EditOnEnter;
}
private void dataGridView1_EditingControlShowing(object sender,
DataGridViewEditingControlShowingEventArgs e)
{
var dataGridView = sender as DataGridView;
if (dataGridView?.CurrentCell?.ColumnIndex != 1) return;
var comboBox = e.Control as DataGridViewComboBoxEditingControl;
if (comboBox == null) return;
comboBox.DropDownStyle = ComboBoxStyle.DropDown;
if (!true.Equals(comboBox.Tag))
{
comboBox.Tag = true;
comboBox.Validating += (obj, args) =>
{
var column = (DataGridViewComboBoxColumn)dataGridView.CurrentCell.OwningColumn;
var list = comboBox.DataSource as List<string>;
if (list == null) return;
var txt = comboBox.Text;
if (!list.Contains(txt))
{
list.Add(txt);
column.DataSource = null;
column.DataSource = list;
}
dataGridView.CurrentCell.Value = txt;
dataGridView.NotifyCurrentCellDirty(true);
};
}
}
I have a DataGridView that has a ComboBox column and I must update each ComboBox's possible values when its drop down shows. I also must make the ComboBoxes capable of having custom typed values. When a new value is typed, it should be added to the list of possible values. The problem is that I get infinitely many DataError event triggers (error message boxes), I know how to handle it by just changing a field in the DataGridViewDataErrorEventArgs object, but I know it is not the correct way to handle it:
private void DataGridView1_DataError(object sender, DataGridViewDataErrorEventArgs e)
{
e.Cancel = false;
}
If I do it in the incorrect way, after selecting a value from the drop down or typing a new value, the CellValueChanged is triggered but the closed ComboBox does not display the current value but an already existing value (the first in the list).
In the following code the Form subclass is Form2, the initial values are stored in the str field and the UpdatePossibleValues method is called to update the possible values in all the ComboBoxes inside the only column in the data grid view, a DataGridViewComboBoxColumn:
public Form2()
{
InitializeComponent();
dataGridView1.EditingControlShowing += DataGridView1_EditingControlShowing;
UpdatePossibleValues();
}
internal List<string> str = new List<string>()
{
"val1",
"val2"
};
private void DataGridView1_EditingControlShowing(object sender,
DataGridViewEditingControlShowingEventArgs e)
{
if (dataGridView1.CurrentCell == null ||
dataGridView1.CurrentCell.OwningColumn == null ||
dataGridView1.CurrentCell.OwningColumn.Name != "column1")
{
return;
}
var combo = e.Control as DataGridViewComboBoxEditingControl;
if (combo == null)
{
return;
}
var cb = combo as ComboBox;
UpdatePossibleValues(cb);
cb.DropDownStyle = ComboBoxStyle.DropDown; // this makes the ComboBoxes editable
cb.Validating += Cb_Validating;
}
private void Cb_Validating(object sender, System.ComponentModel.CancelEventArgs e)
{
var cbo = sender as ComboBox;
string t = cbo.Text;
var cell = (DataGridViewComboBoxCell)dataGridView1.CurrentCell;
// add the value to the list if it is not there
if (!string.IsNullOrEmpty(t) &&
!cbo.Items.Contains(t))
{
str.Add(t);
UpdatePossibleValues(cbo);
cell.Value = t;
e.Cancel = false;
}
}
private void UpdatePossibleValues(ComboBox cb = null)
{
if (cb == null)
{
var col = dataGridView1.Columns[0] as DataGridViewComboBoxColumn;
col.Items.Clear();
foreach (string s in str)
{
col.Items.Add(s);
}
}
else
{
cb.Items.Clear();
foreach (string s in str)
{
cb.Items.Add(s);
}
}
}
Screenshots:
To dynamically add item to DataGridViewComboBoxColumn:
Hanlde EditingControlShowing and get the DataGridViewComboBoxEditingControl
Set editing control DropDownStyle to DropDown
Handle Validating event of editing control and make sure you attach the event handler just once.
Check if the Text of the editing control doesn't exists in the items:
Add it to data source of the column
Then reset data source of the column by setting it to null and assigning data source again.
Notes:
If you have multiple combo box, make sure you use different data sources for combo boxes and update corresponding data source in validating event.
If you handle the events using anonymous method, make sure you have a correct assumption about captured variables. To make it simple, you can handle the event using a normal method.
Example
The following example shows a DataGridView having two DataGridViewComboBoxColumn which for the second one, you can add new values by typing in the combo box at run-time.
To run the example, create a Form and drop a DataGridView on a new Form and just copy and paste the following code in the form:
private List<String> comboSource1;
private List<String> comboSource2;
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
comboSource1 = new List<string> { "A", "B" };
comboSource2 = new List<string> { "1", "2" };
var dt = new DataTable();
dt.Columns.Add("C1");
dt.Columns.Add("C2");
dt.Rows.Add("A", "1");
dt.Rows.Add("B", "2");
var c1 = new DataGridViewComboBoxColumn();
c1.Name = "C1";
c1.DataPropertyName = "C1";
c1.DataSource = comboSource1;
var c2 = new DataGridViewComboBoxColumn();
c2.Name = "C2";
c2.DataPropertyName = "C2";
c2.DataSource = comboSource2;
dataGridView1.Columns.AddRange(c1, c2);
this.dataGridView1.DataSource = dt;
dataGridView1.EditingControlShowing += dataGridView1_EditingControlShowing;
dataGridView1.EditMode = DataGridViewEditMode.EditOnEnter;
}
private void dataGridView1_EditingControlShowing(object sender,
DataGridViewEditingControlShowingEventArgs e)
{
var dataGridView = sender as DataGridView;
if (dataGridView?.CurrentCell?.ColumnIndex != 1) return;
var comboBox = e.Control as DataGridViewComboBoxEditingControl;
if (comboBox == null) return;
comboBox.DropDownStyle = ComboBoxStyle.DropDown;
if (!true.Equals(comboBox.Tag))
{
comboBox.Tag = true;
comboBox.Validating += (obj, args) =>
{
var column = (DataGridViewComboBoxColumn)dataGridView.CurrentCell.OwningColumn;
var list = comboBox.DataSource as List<string>;
if (list == null) return;
var txt = comboBox.Text;
if (!list.Contains(txt))
{
list.Add(txt);
column.DataSource = null;
column.DataSource = list;
}
dataGridView.CurrentCell.Value = txt;
dataGridView.NotifyCurrentCellDirty(true);
};
}
}
I have a combox with list of image labels and a save button. On the save button I need to get the combox selected value on the save button if it is null then validate. How I can do this?
enter image description here
This is how I add combox in datagridview:
string[] ImageLabels = {Photograph, PassportPage1, PassportPage2,
PassportPage3, PassportPage4};
var list=new ArrayList();
var combo = new DataGridViewComboBoxColumn();
combo.HeaderText = "Image Labels";
combo.Name = "combo";
list = new ArrayList();
list.AddRange(ImageLabels);
combo.Items.AddRange(list.ToArray());
dgvFiles.Columns.Add(combo);
I am getting null values.
enter image description here
Add the below event for your datagridview
Here my datagridview name is dataGridView1
private void dataGridView1_CurrentCellDirtyStateChanged(object sender, EventArgs e)
{
dataGridView1.CommitEdit(DataGridViewDataErrorContexts.Commit);
}
private void dataGridView1_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
string comboboxSelectedValue = string.Empty;
if (dataGridView1.Columns[e.ColumnIndex].GetType() == typeof(DataGridViewComboBoxColumn))
comboboxSelectedValue = dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex].Value.ToString();
}
1) dataGridView1_CurrentCellDirtyStateChanged: This event will fire your dataGridView1_CellValueChanged immediately after you select value from your DataGridViewComboBoxColumn.
2) dataGridView1_CellValueChanged: This event will give you the value of your selected option in combobx, we additionally checked that is this the values comes from DataGridViewComboBoxColumn or not.
You may add above events on Form1_Load like
private void Form1_Load(object sender, EventArgs e)
{
dataGridView1.CellValueChanged +=
new DataGridViewCellEventHandler(dataGridView1_CellValueChanged);
dataGridView1.CurrentCellDirtyStateChanged +=
new EventHandler(dataGridView1_CurrentCellDirtyStateChanged);
}
Output:
After I click a particular row and I click a button, I want to change the cell type from default type (DataGridViewTextBoxCell) to DataGridViewComboBoxCell and to change that cell data source to a list.
Somehow I encountered an unexplained behavior, In one line it works and shows the ComboBox, in other, it just enters to edit mode.
My code looks something like this:
private void btnClick(object sender, EventArgs e)
{
dataGridView.BeginEdit(true);
var selectedRow = CurrentCell.RowIndex;
var selectedColumn = CurrentCell.ColumnIndex;
var cellName = dataGridView[0, _selectedCell.RowIndex].Value.ToString();
var dict = GetDict(cellName);
if (dict != null)
{
var comboBoxCell = new DataGridViewComboBoxCell
{
DataSource = dict.Keys.ToList()
}
dataGridView[1, selectedRow] = comboBoxCell;
dataGridView.CurrentCell = dataGridView.Rows[selectedRow].Cells[selectedColumn];
}
dataGridView.BeginEdit(false);
}
Update: It seems only after the event CellBeginEdit is fired, which is caused by pressing other cell it updates, But then it takes like 3-4 click for the ComboBox to open.
Try This Code. You should override CellClick Event or Call You Method There. This code is written in CellClick Event, Don't get confused with Name
//This Code is written in CellClick Event not in CellContentClick (Don'tconfuse with signature)
private void dataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
dataGridView1.BeginEdit(true);
var selectedRow = (sender as DataGridView).CurrentCell.RowIndex;
var selectedColumn = (sender as DataGridView).CurrentCell.ColumnIndex;
var comboBoxCell = new DataGridViewComboBoxCell
{
};
dataGridView1.CurrentCell = dataGridView1.CurrentCell as DataGridViewComboBoxCell;
dataGridView1[selectedColumn, selectedRow] = comboBoxCell;
dataGridView1.CurrentCell = dataGridView1.Rows[selectedRow].Cells[selectedColumn];
dataGridView1.BeginEdit(false);
}
I'm using the Leave event on Combobox to disable it and load some data but, after leaving and disabling, it selects the whole text in Combobox. I want to unselect the text.
Im using:
cmbNome.DropDownStyle = ComboBoxStyle.DropDown;
cmbNome.AutoCompleteMode = AutoCompleteMode.Suggest;
cmbNome.AutoCompleteSource = AutoCompleteSource.CustomSource;
if it helps...
the code:
private void cmbNome_Leave(object sender, EventArgs e)
{
cmbNome.Enabled = false;
CarregarDados();
CarregarTelefones();
}
Try setting data source before disabling combo box:
string[] data = new string[] {
"Absecon","Abstracta","Abundantia","Academia","Acadiau","Acamas",
"Ackerman","Ackley","Ackworth","Acomita","Aconcagua","Acton","Acushnet",
"Acworth","Ada","Ada","Adair","Adairs","Adair","Adak","Adalberta","Adamkrafft",
"Adams"
};
private void comboBox2_Leave(object sender, EventArgs e)
{
comboBox2.DataSource = data;
comboBox2.Enabled = false;
comboBox2.SelectedIndex = -1;
}
This code fills the combo with data; however, no item is selected and text field is empty
EDIT: added sample data
Try to first deselect item and than set combobox disabled.
cmbNome.SelectedIndex = -1;
cmbNome.Enabled = false;