I have a datagridview binded to a BindingList and inside this list I have comboboxes binded to a list which is a property of my BindingList, for understanding better:
ListA ---> binded to datagridview
ListA.ListB ---> binded to comboboxes
When I open the form I can corectly set my comboboxes showing the values inside the ListB, but when I add a new item I get an error (value is not valid), here is the code:
private void dataGridView_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e)
{
((DataGridViewComboBoxColumn)dataGridView.Columns["Names"]).DisplayIndex = 4;
for (int i = 0; i < People.Count; i++)
{
var cell = (DataGridViewComboBoxCell)dataGridView.Rows[i].Cells["Names"];
cell.DataSource = People[i].Names;
cell.Value = People[i].Names[0];
}
}
The code above works great, the problem happens here:
private void dataGridView_CellValidating(object sender, DataGridViewCellValidatingEventArgs e)
{
if (dataGridView.CurrentCell.ColumnIndex != dataGridView.Columns["Names"].Index)
return;
var cell = (DataGridViewComboBoxCell)dataGridViewICAO.CurrentCell;
if (cell.EditedFormattedValue.ToString().Equals(String.Empty)) return;
var regex = new Regex("[a-zA-Z]");
if (!regex.IsMatch(cell.EditedFormattedValue.ToString()))
e.Cancel = true;
else
{
People[cell.RowIndex].Names.Add(cell.EditedFormattedValue.ToString());
cell.Value = People[cell.RowIndex].Names.Last();
People[cell.RowIndex].Names = cell.Value.ToString();
}
}
on the row code cell.Value = People[cell.RowIndex].Names.Last(); I get the exception... Thanks to all!
This is how I set the combobox:
private void AddComboBox()
{
var comboNames = new DataGridViewComboBoxColumn { Name = "cmbNames", HeaderText = "Names" };
dataGridView.Columns.Add(comboNames);
}
private void dataGridView_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
if (dataGridView.CurrentCell.ColumnIndex == dataGridView.Columns["cmbNames"].Index)
{
var combo = e.Control as ComboBox;
if (combo == null)
return;
combo.DropDownStyle = ComboBoxStyle.DropDown;
}
}
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 which contain a Combobox Column i want when i select a add value from the combobox it shows a new form. i tried this code but it doesn't work:
private void dataGridView2_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
switch (dataGridView2.Columns[e.ColumnIndex].Name)
{
case "CategorieDataGridViewTextBoxColumn":
if (dataGridView2.Rows[e.RowIndex].Cells["CategorieDataGridViewTextBoxColumn"].Value.ToString() == "Add")
{
Categorie cat = new Categorie();
cat.Show();
}
break;
}
}
So how can i do it??
You should handle the event when a value is changed in a ComboBox in a DataGridView cell. try this code which will fire the event of the selection in the comboBox in the dataGridView:
public Form1()
{
InitializeComponent();
DataGridViewComboBoxColumn cmbcolumn = new DataGridViewComboBoxColumn();
dataGridView2.EditingControlShowing += new DataGridViewEditingControlShowingEventHandler(dataGridView2_EditingControlShowing);
}
private void dataGridView2_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
ComboBox combo = e.Control as ComboBox;
if (combo != null)
{
combo.SelectedIndexChanged -= new EventHandler(ComboBox_SelectedIndexChanged);
combo.SelectedIndexChanged += new EventHandler(ComboBox_SelectedIndexChanged);
}
}
private void ComboBox_SelectedIndexChanged(object sender, EventArgs e)
{
ComboBox cb = (ComboBox)sender;
string item = cb.Text;
if (item == "Add")
{
Categorie cat = new Categorie();
cat.Show();
}
}
I found a way to add a combobox to DataGridview (Winform) cell, but I have not found an event like ItemDataBound of DataGridView to set a value to comboBox. And do not know how to set a selected value of a comboBox to DataItem property of a current row (of a DataGridView) :(
Please give me some clues to do this task
Thanks you so much
You can use below method to add data to a combobox in gridview. If you dont have a list you can add items to the combobox as:
cmbdgv.Items.Add("Test");
private void bindDataToDataGridViewCombo() {
DataGridViewComboBoxColumn cmbdgv = new DataGridViewComboBoxColumn();
List<String> itemCodeList = new List<String>();
cmbdgv.DataSource = itemCodeList;
cmbdgv.HeaderText = "Test";
cmbdgv.Name = "Test";
cmbdgv.Width = 270;
cmbdgv.Columns.Add(dgvCmbForums);
cmbdgv.Columns["Test"].DisplayIndex = 0;
}
After adding if you want to capture the combobox selection change you can use below event in the datagridview.
ComboBox cbm;
DataGridViewCell currentCell;
private void dataGridView1_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
if (e.Control is ComboBox)
{
cbm = (ComboBox)e.Control;
if (cbm != null)
{
cbm.SelectedIndexChanged += new EventHandler(cbm_SelectedIndexChanged);
}
currentCell = this.dataGridView1.CurrentCell;
}
}
void cbm_SelectedIndexChanged(object sender, EventArgs e)
{
this.BeginInvoke(new MethodInvoker(EndEdit));
}
void EndEdit()
{
if (cbm != null)
{
string SelectedItem=cbm.SelectedItem.ToString();
int i = dataGridView1.CurrentRow.Index;
dataGridView1.Rows[i].Cells["Test"].Value = SelectedItem;
}
}
If you are trying to set the value to a Combobox in a DataGridView, see if this answer will help.
To get the selected item of the Combobox (example):
comboBox.SelectedIndexChanged += new EventHandler(comboBox_ComboSelectionChanged);
private void comboBox_ComboSelectionChanged(object sender, EventArgs e)
{
if (myDGV.CurrentCell.ColumnIndex == 5)
{
int selectedIndex;
string selectedItem;
selectedIndex = ((ComboBox)sender).SelectedIndex; // handle an error here.
// get the selected item from the combobox
var combo = sender as ComboBox;
if (selectedIndex == -1)
{
MessageBox.Show("No value has been selected");
}
else
{
// note that SelectedItem may be null
selectedItem = combo.SelectedItem.ToString();
if (selectedItem != null)
{
// Your code
void dataGridView1_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
TextBox txtbox = e.Control as TextBox;
if (this.dataGridView1.CurrentCell.ColumnIndex == 0)
{
if (txtbox != null)
{
//
}
}
}
Also I have coded with AutoCompleteStringCollection.
Code is working,
Before edit Column 1, It won't allow autoCompletion for any of other column.
Once edited Column 1, all column will work same as Column one.
Please help me how to fix issue or else any other best way to do that, please share here.
This should work.
private bool firstColEdited = false;
/************************************************************/
var source = new AutoCompleteStringCollection();
String[] stringArray = Array.ConvertAll<DataRow, String>(products.Select(), delegate(DataRow row) { return (String)row["code"]; });
source.AddRange(stringArray);
/************************************************************/
void dataGridView1_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
TextBox txtbox = e.Control as TextBox;
if (this.dataGridView1.CurrentCell.ColumnIndex == 0 || firstColEdited)
{
firstColEdited = true;
txtbox.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
txtbox.AutoCompleteCustomSource = source;
txtbox.AutoCompleteSource = AutoCompleteSource.CustomSource;
}
}
I am trying to enter values in a datagridview Combobox. but it does not Allows. What to do?
private void GridStockItemEntry_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
DataGridViewRow row = GridStockItemEntry.CurrentRow;
DataGridViewCell cell = GridStockItemEntry.CurrentCell;
if (e.Control.GetType() == typeof(DataGridViewComboBoxEditingControl))
{
if (cell == row.Cells["ItemName"] && Convert.ToString(row.Cells["Type"].Value) == "Raw Material")
{
DataGridViewComboBoxEditingControl cbo = e.Control as DataGridViewComboBoxEditingControl;
cbo.DropDownStyle = ComboBoxStyle.DropDown;
cbo.Validating += new CancelEventHandler(cbo_Validating);
}
}
}
void cbo_Validating(object sender, CancelEventArgs e)
{
DataGridViewComboBoxEditingControl cbo = sender as DataGridViewComboBoxEditingControl;
DataGridView grid = cbo.EditingControlDataGridView;
object value = cbo.Text;
// Add value to list if not there
if (cbo.Items.IndexOf(value) == -1)
{
DataGridViewComboBoxCell cboCol = (DataGridViewComboBoxCell)grid.CurrentCell;
// Must add to both the current combobox as well as the template, to avoid duplicate entries...
cbo.Items.Add(value);
cboCol.Items.Add(value);
grid.CurrentCell.Value = value;
}
}
Maybe, this example is better readable:
private void datagridview_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e) {
DataGridView dgv = (DataGridView)sender;
if(dgv.CurrentCell.ColumnIndex==dgv.Columns["ColumnName"].Index) {
ComboBox cbx = (ComboBox)e.Control;
cbx.DropDownStyle = ComboBoxStyle.DropDown;
cbx.AutoCompleteSource = AutoCompleteSource.ListItems;
cbx.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.Suggest;
}
}
Make sure that EditMode property of the DataGridView is set to EditOnKeystrokeOrF2
Also, verify that ReadOnly property is set to False.