I'm struggling with DataGridViewComboBoxCell
. On some cases (let's say, events) I must preselect a value of ComboBox
in my Form's DataGridView
. When user changes one box, I am able to change another programatically like this:
var item = ItemName.Items.GetListItem(name);
if (item != null)
{
_loading = true; // that's needed to come around another CellValueChanged events
itemView.Rows[e.RowIndex].Cells["ItemName"].Value = item;
_loading = false;
}
I'm populating ItemName.Items
like this:
foreach (var item in _model.DefaultData.ItemList)
{
if (item.Value.Code.HasValue()) ItemCode.Items.Add(new ListItem(item.Key, item.Value.Code));
ItemName.Items.Add(new ListItem(item.Key, item.Value.Name));
}
GetListItem method:
public static ListItem GetListItem(this DataGridViewComboBoxCell.ObjectCollection col, string name)
{
ListItem retItem = null;
foreach (ListItem item in col)
{
if (item.Name == name) { retItem = item; break; }
}
return retItem;
}
That works fine, BUT...
Now I want to add rows to DataGridView
on form load like that:
foreach (var item in _model.SelectedItems)
{
var row = new DataGridViewRow();
row.Cells.Add(new DataGridViewTextBoxCell { Value = item.Id });
row.Cells.Add(new DataGridViewComboBoxCell { Value = ItemCode.Items.GetListItem(item.Code) });
row.Cells.Add(new DataGridViewComboBoxCell { Value = ItemName.Items.GetListItem(item.Name) });
row.Cells.Add(new DataGridViewTextBoxCell { Value = item.Units });
...
itemView.Rows.Add(row);
}
and that throws beloved DataGridViewComboBox value is not valid
exception. Please help, I'm out of thoughts on that. I don't want to use DataSource
or something like that. ItemCode
and ItemName
column items are populated and returned properly through GetListItem()
. I don't understand why it works normally, but on form load it doesn't work (on shown it doesn't work either).
EDIT: sorry, forgot to add.
My ListItem class:
public class ListItem
{
public int Id { get; set; }
public string Name { get; set; }
public ListItem(int sid, string sname)
{
Id = sid;
Name = sname;
}
public override string ToString()
{
return Name;
}
}
I already put that too on form load:
ItemName.ValueMember = "Id";
ItemName.DisplayMember = "Name";
ItemCode.ValueMember = "Id";
ItemCode.DisplayMember = "Name";
Ok, I managed to solve it myself.
Apparently, it's not enough for DataGridViewComboBoxColumn.Items
to contain possible items. You must also add items to DataGridViewComboBoxCell.Items
if you're adding a new row programatically.
So if someone else tries to use my approach of not having data bindings like DataTable
etc., here's my solution:
foreach (var item in _model.SelectedItems)
{
var row = new DataGridViewRow();
var codeCell = new DataGridViewComboBoxCell();
codeCell.Items.AddRange(ItemCode.Items);
codeCell.Value = ItemCode.Items.GetListItem(item.Code);
var nameCell = new DataGridViewComboBoxCell();
nameCell.Items.AddRange(ItemName.Items);
nameCell.Value = ItemName.Items.GetListItem(item.Name);
row.Cells.Add(new DataGridViewTextBoxCell { Value = item.Id });
row.Cells.Add(codeCell);
row.Cells.Add(nameCell);
row.Cells.Add(new DataGridViewTextBoxCell { Value = item.Units });
row.Cells.Add(new DataGridViewTextBoxCell { Value = item.Quantity });
row.Cells.Add(new DataGridViewTextBoxCell { Value = item.PriceLt });
row.Cells.Add(new DataGridViewTextBoxCell { Value = item.PriceEu });
itemView.Rows.Add(row);
}