Programmatically add cells and rows to DataGridView

YOhan picture YOhan · Aug 22, 2014 · Viewed 19.6k times · Source

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";

Answer

YOhan picture YOhan · Aug 26, 2014

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);
}