Databinding a combobox column to a datagridview per row (not the entire column)

goku_da_master picture goku_da_master · May 6, 2011 · Viewed 9k times · Source

There are a few posts about this, but after hours of searching I still can't find what I need.

The answer in the following post almost gets me what I want: Combobox for Foreign Key in DataGridView

Question 1:

Going off that example where a Product has many Licenses, my database mappings are all many-to-one relationships which means my License class holds a reference to the Product class. The License class does not have a property for the ProductId since that can be retrieved via the Product reference. I don't want to muck up the License class with both a reference to Product and a ProductId property just to make binding in the UI easier.

Because of this I can't set the DataPropertyName to an Id field. It needs to be the class reference name like so:

DataGridViewComboBoxColumn dataGridViewComboBoxColumn = 
(DataGridViewComboBoxColumn)myDataGridView.Columns("LicenseComboBoxColumn");

dataGridViewComboBoxColumn.DataPropertyName = "License"; // not LicenseID

****Update**** I was able to get this to partially work without creating the ProductId property by specifying the Product.Id as the DataPropertyName like so:

dataGridViewComboBoxColumn.DataPropertyName = "License.Id";

However, when doing so, it broke databinding which caused me to manually get and set the cell value.

I've also seen posts about binding to the DataGridView cell, but databinding breaks when I do that and the datasource itself is never updated:

// populate the combo box with Products for each License

foreach (DataGridViewRow row in myDataGridViewProducts.Rows) 
{
    IProduct myProduct = row.DataBoundItem as IProduct;
    DataGridViewComboBoxCell cell = (DataGridViewComboBoxCell)row.Cells("myProductCol");
    cell.DataSource = getListOfILicenseObjectsFromDao(myProduct.Id);
    cell.Value = myProduct.License.Id;
}

Maybe I'm doing something wrong, or maybe there's a different way. Can anyone help here?

Question 2:

How do I display a different list of Licenses for each Product? In other words, the combobox list of Licenses will be different for each Product in the grid. I'd like to do this using databinding so I don't have to get and set the values myself.

Answer

goku_da_master picture goku_da_master · May 16, 2011

I found the answer myself. I had this same issue a while ago and found the solution in some old code I dug up. The solution was to add a Self property to the object I wanted to databind to in the combobox (in the example above it would be the License class) and use that property as the ValueMember like so:

foreach (DataGridViewRow row in myDataGridViewProducts.Rows) 
{
    IProduct myProduct = row.DataBoundItem as IProduct;
    DataGridViewComboBoxCell cell = (DataGridViewComboBoxCell)row.Cells("myProductCol");
    cell.DataSource = getListOfILicenseObjectsFromDao(myProduct.Id);
    cell.DataPropertyName = "License";        
    cell.DisplayMember = "Name";
    cell.ValueMember = "Self"; // key to getting the databinding to work
    // no need to set cell.Value anymore!
}

The License class now looks like this:

Public class License
{
    public string Name
    {
        get; set;
    }

    public ILicense Self
    {
        get { return this; }
    }

    // ... more properties
}

Granted I had to "muck" up the Business classes with a property named Self, but that's much better (less confusing to the programmer) than having both a reference to License and a LicenseId property in the Product class IMO. Plus it keeps the UI code much much simpler as there's no need to manually get and set the values - just databind and done.