Exploring DataGridViewComboBoxColumn databinding

Let’s start with a simple example: Each Person has a Name (string) and PersonTypeCode (an Enumerated value) property. We drag a DataGridView on the designer form and add two columns (DataGridViewComboBoxColumn for the PersonTypeCode property). And then we hook up the Bindingsource as following:

class Form1 : Form
{
 public Form1()
 {
  InitializeComponent();

  this.dataGridView1.AutoGenerateColumns = false;
  this.ColumnName.DataPropertyName = "Name";
  this.ColumnPersonTypeCode.DataPropertyName = "PersonTypeCode";

  BindingSource bindingSource = new BindingSource();
  bindingSource.DataSource = FindPersons();
  this.dataGridView1.DataSource = bindingSource;
 }

 private BindingList<person> FindPersons()
 {
  BindingList<person> bindingList = new BindingList<person>();
  bindingList.Add(new Person("Timvw", PersonTypeCode.Geek));
  bindingList.Add(new Person("John Doe", PersonTypeCode.Anonymous));
  bindingList.Add(new Person("An Onymous", PersonTypeCode.Anonymous));
  bindingList.Add(new Person("Jenna Jameson", PersonTypeCode.Babe));
  return bindingList;
 }
}

enum PersonTypeCode
{
 Geek = 0,
 Anonymous = 1,
 Babe = 2
}

class Person
{
 private PersonTypeCode personTypeCode;
 private string name;

 public Person(string name, PersonTypeCode personTypeCode)
 {
  this.name = name;
  this.personTypeCode = personTypeCode;
 }

 public string Name
 {
  get { return this.name; }
  set { this.name = value; }
 }

 public PersonTypeCode PersonTypeCode
 {
  get { return this.personTypeCode; }
  set { this.personTypeCode = value; }
 }
}

If we run this code we run in the following error:

DataGridViewComboBoxCell value is not valid.

Always make sure the DataGridViewComboxColumn knows about all the possible values (Add them via the Items property or use databinding). Let’s extend our Form1 class as following:

public Form1()
{
 InitializeComponent();

 this.dataGridView1.AutoGenerateColumns = false;
 this.ColumnName.DataPropertyName = "Name";
 this.ColumnPersonTypeCode.DataPropertyName = "PersonTypeCode";
 this.ColumnPersonTypeCode.DataSource = FindPersonTypeCodes();

 BindingSource bindingSource = new BindingSource();
 bindingSource.DataSource = FindPersons();
 this.dataGridView1.DataSource = bindingSource;
}

private BindingList<personTypeCode> FindPersonTypeCodes()
{
 BindingList<personTypeCode> bindingList = new BindingList<personTypeCode>();
 foreach (PersonTypeCode personTypeCode in Enum.GetValues(typeof(PersonTypeCode)))
 {
  bindingList.Add(personTypeCode);
 }
 return bindingList;
}

Allright, here is a screenshot of our first working version:

datagridviewcomboboxcolumn with enumerated values

Instead of displaying the bare enum values we want to display a nice label. In order to achieve this we define a class PersonType to hold both the PersonTypeCode and the label:

class PersonType
{
 private PersonTypeCode personTypeCode;
 private string label;

 public PersonType(string label, PersonTypeCode personTypeCode)
 {
  this.label = label;
  this.personTypeCode = personTypeCode;
 }

 public string Label
 {
  get { return this.label; }
  set { this.label = value; }
 }

 public PersonTypeCode PersonTypeCode
 {
  get { return this.personTypeCode; }
  set { this.personTypeCode = value; }
 }
}

We modify our code so that this new PersonType class is used for the ComboBoxColumn:

public Form1()
{
 InitializeComponent();

 this.dataGridView1.AutoGenerateColumns = false;
 this.ColumnName.DataPropertyName = "Name";
 this.ColumnPersonTypeCode.DataPropertyName = "PersonTypeCode";
 this.ColumnPersonTypeCode.DisplayMember = "Label";
 this.ColumnPersonTypeCode.ValueMember = "PersonTypeCode";
 this.ColumnPersonTypeCode.DataSource = FindPersonTypes();

 BindingSource bindingSource = new BindingSource();
 bindingSource.DataSource = FindPersons();
 this.dataGridView1.DataSource = bindingSource;
}

private BindingList<personType> FindPersonTypes()
{
 BindingList<personType> bindingList = new BindingList<personType>();
 bindingList.Add(new PersonType("A geeky person", PersonTypeCode.Geek));
 bindingList.Add(new PersonType("A coward", PersonTypeCode.Anonymous));
 bindingList.Add(new PersonType("Feeling hot hot hot", PersonTypeCode.Babe));
 return bindingList;
}



datagridviewcomboboxcolumn with nice labels.

Great! Now we’ll add some functionality that limits the possible values in the ComboBoxColumn basesd on the Name (I already demonstrated this technique here). Simply handle the EditingControlShowing Event on the DataGridView as following:

void dataGridView1_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
 BindingSource bindingSource = this.dataGridView1.DataSource as BindingSource;
 Person person = bindingSource.Current as Person;
 BindingList<personType> bindingList = this.FindPersonTypes(person);

 DataGridViewComboBoxEditingControl comboBox = e.Control as DataGridViewComboBoxEditingControl;
 comboBox.DataSource = bindingList;
}

private BindingList<personType> FindPersonTypes(Person person)
{
 // by default, all persons simply have one of the available persontypecodes
 BindingList<personType> bindingList = FindPersonTypes();

 if (person.PersonTypeCode == PersonTypeCode.Geek)
 {
  // geeks are doomed to stay geeks
  bindingList.RemoveAt(2);
  bindingList.RemoveAt(1);
 }

 return bindingList;
}

If you open the combox for “Timvw” you see that you can only choose “A geeky person”:

datagridviewcomboboxcolumn with limited options.

Instead of using an enum we could have used a regular class too. The key is to override the Equals method:

class PersonTypeCode
{
 public static PersonTypeCode Geek
 {
  get { return new PersonTypeCode(0); }
 }

 public static PersonTypeCode Anonymous
 {
  get { return new PersonTypeCode(1); }
 }

 public static PersonTypeCode Babe
 {
  get { return new PersonTypeCode(2); }
 }

 private int id;

 public PersonTypeCode(int id)
 {
  this.id = id;
 }

 public override bool Equals(object obj)
 {
  if (obj == null) return false;
  if (obj == this) return true;
  PersonTypeCode personTypeCode = obj as PersonTypeCode;
  if (personTypeCode == null) return false;
  return this.id == personTypeCode.id;
 }

 public override int GetHashCode()
 {
  return this.id.GetHashCode();
 }
}

private BindingList<personType> FindPersonTypes(Person person)
{
 // by default, all persons simply have one of the available persontypecodes
 BindingList<personType> bindingList = FindPersonTypes();

 //if (person.PersonTypeCode == PersonTypeCode.Geek)
 if (person.PersonTypeCode.Equals(PersonTypeCode.Geek))
 {
  // geeks are doomed to stay geeks
  bindingList.RemoveAt(2);
  bindingList.RemoveAt(1);
 }

 return bindingList;
}

And now you’re thinking: But i want the user to not select a PersonTypeCode (null). We’ll represent that with an empty string “”:

private BindingList<person> FindPersons()
{
 BindingList<person> bindingList = new BindingList<person>();
 bindingList.Add(new Person("Timvw", PersonTypeCode.Geek));
 bindingList.Add(new Person("John Doe", PersonTypeCode.Anonymous));
 bindingList.Add(new Person("An Onymous", PersonTypeCode.Anonymous));
 bindingList.Add(new Person("Jenna Jameson", PersonTypeCode.Babe));
 bindingList.Add(new Person("Null Able", null));
 return bindingList;
}

private BindingList<personType> FindPersonTypes()
{
 BindingList<personType> bindingList = new BindingList<personType>();
 bindingList.Add(new PersonType("A geeky person", PersonTypeCode.Geek));
 bindingList.Add(new PersonType("A coward", PersonTypeCode.Anonymous));
 bindingList.Add(new PersonType("Feeling hot hot hot", PersonTypeCode.Babe));
 bindingList.Add(new PersonType(string.Empty, null));
 return bindingList;
}

private BindingList<personType> FindPersonTypes(Person person)
{
 // by default, all persons simply have one of the available persontypecodes
 BindingList<personType> bindingList = FindPersonTypes();

 if (person.PersonTypeCode != null && person.PersonTypeCode.Equals(PersonTypeCode.Geek))
 {
  // geeks are doomed to stay geeks
  bindingList.RemoveAt(2);
  bindingList.RemoveAt(1);
 }

 return bindingList;
}

datagridviewcomboboxcolumn with null option.

When the user starts editing the record, the combobox will choose the first item in the list (A geeky person). Now we change this behaviour so that the actual PersonTypeCode is selected:

void dataGridView1_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
 if (this.dataGridView1.CurrentCell.ColumnIndex == this.ColumnPersonTypeCode.Index)
 {
  BindingSource bindingSource = this.dataGridView1.DataSource as BindingSource;
  Person person = bindingSource.Current as Person;
  BindingList<personType> bindingList = this.FindPersonTypes(person);

  DataGridViewComboBoxEditingControl comboBox = e.Control as DataGridViewComboBoxEditingControl;
  comboBox.DataSource = bindingList;
  if (person.PersonTypeCode != null)
  {
   comboBox.SelectedValue = person.PersonTypeCode;
  }
  else
  {
   comboBox.SelectedValue = string.Empty;
  }
 }
}



datagridviewcomboboxcolumn with selected item.

In order to make the DataGridView more usable we set the EditMode property to EditOnEnter. Selected values in the ComboBox are only commited when the user leaves the current cell. Handling the SelectionChangeCommited event on the ComboBox allows us to commit that value without requiring the user to leave the current cell:

void dataGridView1_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
 if (this.dataGridView1.CurrentCell.ColumnIndex == this.ColumnPersonTypeCode.Index)
 {
  BindingSource bindingSource = this.dataGridView1.DataSource as BindingSource;
  Person person = bindingSource.Current as Person;
  BindingList<personType> bindingList = this.FindPersonTypes(person);

  DataGridViewComboBoxEditingControl comboBox = e.Control as DataGridViewComboBoxEditingControl;
  comboBox.DataSource = bindingList;
  if (person.PersonTypeCode != null)
  {
   comboBox.SelectedValue = person.PersonTypeCode;
  }
  else
  {
   comboBox.SelectedValue = string.Empty;
  }

  comboBox.SelectionChangeCommitted -= this.comboBox_SelectionChangeCommitted;
  comboBox.SelectionChangeCommitted += this.comboBox_SelectionChangeCommitted;
 }
}

void comboBox_SelectionChangeCommitted(object sender, EventArgs e)
{
 this.dataGridView1.EndEdit();
}

Now it’s up to you to apply these simple techniques and build great software. Feel free to download the complete source: DataGridViewComboBoxBinding.zip.

38 thoughts on “Exploring DataGridViewComboBoxColumn databinding

  1. Hi,
    after having a look at your suggestions, i think you are the right person to discuss the problem. My problem is that i normally binds the comboboxcolumn of datagridview with a disconnected datatable, my requirement is that I want to filterout the items in a comboboxcolumn depending upon the value selected in another comboboxcolumn. but this needs to be done for a particular row. when i try to change the datasource of comboboxcolumn, it simply causes the error ” System.Argument.Exception Datagridviewcomboboxcell value is not valid”
    plz reply as soon as possible

    thanks in advance

    S.H.Raza
    System Architect
    raza_sh@sancharnet.in

  2. Here are the things you have to keep an eye on:

    -) The drop-down list values (or the values indicated by the ValueMember property) must include the actual cell values or the DataGridView control will throw an exception.

    -) In case the type of the values in the drop-down list is a reference type you will have to make sure to override the Equals method, otherwise the ‘equality’ will be determined with a simple call to Object.ReferenceEquals

    -) Sometimes you don’t have control over the type and thus overriding the Equals method is not an option. In that situation you can handle the CellParsing event on the DataGridView it allows you to convert the user-specified value. I use that technique in http://www.timvw.be/implementing-masterdetail-for-custom-objects-with-datagridviewcomboboxcolumns/ (Instead of adding/removing items based on the choosen parent, you’ll have to do that based on the dataGridView.CurrentCell.RowIndex…)

  3. This example is very close to something I’ve been struggling with. I have a DataTable that contains a name column and an enumeration column which I would like to use to populate a DataGridViewComboBoxColumn (similar to your PersonType). However, I’d like the selected enumeration value from the combobox to be data bound to an enumeration column within another DataTable. Can you suggest a way to do this?

  4. Pingback: Tim Van Wassenhove » Exploring DataGridViewComboBoxColumn databinding (part2)

  5. Hi Tim
    I really have a problem with binding data to a combobox inside a datagridview. The thing is that the valuemember is some times repeated so when a select one item it changes to the first item that has the same value.
    I work with the combobox datasource. Also Im programming in VB.NET
    Please help me, its becoming very anoying
    Thank you

  6. You should make sure that your items do not have the same Value because what you’re experiencing now is expected behaviour.

    A possible workaround i see is to declare a OrderedItem<Type> and use that to databind with:

    class OrderedItem<Type>
    {
     int OrderId { get; set;  }
     Type Item { get; set; }
    }
    
  7. As a matter of fact the valuemember is repeated several times. How can I solve this problem or should I resign to have this datagridviewcombobox.
    thank you again for your time

  8. Hello Tim,

    Very nice example. But does not quite fit the problem I’m struggling with (.net 2005).

    I have a datagrid combobox column that I want attached to a specific column in another datatable (all the other columns bind to tableA). I also want the datasource on the combobox to be filtered (active userIDs + userID of the current record).

    So, I’m looking for a combination of a combobox that displays a different source data, which is variable for each row (depending on the current userID stored in the main datatable for that row), and the data actually be stored in the main datatable.

    Sorry, a bit difficult to explain, but I’m having a very difficult time trying to get this working correctly.

    Any help is appreciated.

    Thank you!

  9. I had the error. Your example was very close to what I was doing. After looking at your dynamic filling of PeopleType, I realized the rest of the solution fit my needs perfectly.
    Your dynamic filling of the combo box allows me to handle business status, user security and list item edit state all in the EditingControlShowing.
    I rarely respond to the examples people put on the web but I must respond to say how great your example is. Coding in Alaska means I have only a few colleagues and the internet is the best source for help.

  10. Hello Tim,
    Thank you very much for great examples and
    making them available for os others.
    I have a little problem(quite anoying actually) with databound datagridview.
    I have continued working on somebody else’s project and it is quite hard.
    The grid has 5 columns, and all of them are DataGridViewTextBoxColumns(all are databound), and everything works fine. The grid loads entries from the database and so on.
    I have to change somethings now to satisfy my boss.
    I sholud add some empty rows(I can do that) and in the added rows the first column sholud have both, a DataGridViewTextCell and a DataGridViewComboBoxCell.
    Is this possible with databound gridview?
    I have tried but cannot get it to work right. can you help me please, I’m quite desperate now. Is it possible to exchange private messages with you. I’m grateful in advance for any help you can offer. Best regards, Linn

  11. Hello again
    I can see that I have made a mistake.

    In the added rows first cell I sholud have ComboBoxCell instead of
    TexBoxCell. how do I do that. When I add empty row the first cell is texbox and I sholud cance it to combobox somehow
    Best regards, Linn

  12. Here is a little example that would replace the gender DGVTextBoxCell on the first row with a DGVComboBoxCell (The trick is to subscribe to the DataBindingComplete event, which allows you to replace stuff after DataBinding has completed…):

    public partial class Form1 : Form
    {
     public Form1()
     {
      InitializeComponent();
     }
    
     private void Form1_Load(object sender, EventArgs e)
     {
      BindingList<Person> persons = new BindingList<Person>();
      persons.Add(new Person(1, "Tim", Gender.Male));
      persons.Add(new Person(2, "Evy", Gender.Female));
    
      this.dataGridView1.DataBindingComplete += this.dataGridView1_DataBindingComplete;
    
      this.dataGridView1.DataSource = persons;
     }
    
     private void dataGridView1_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e)
     {
      DataGridViewComboBoxCell cboCell = new DataGridViewComboBoxCell();
      cboCell.DataSource = Enum.GetValues(typeof(Gender));
      cboCell.Value = ((Person)this.dataGridView1.Rows[0].DataBoundItem).Gender;
      this.dataGridView1.Rows[0].Cells[2] = cboCell;
     }
    }
    
    public class Person
    {
     private int personId;
     private string name;
     private Gender gender;
    
     public Person(int personId, string name, Gender gender)
     {
      this.personId = personId;
      this.name = name;
      this.gender = gender;
     }
    
     public int PersonId
     {
      get { return this.personId; }
      set { this.personId = value; }
     }
    
     public string Name
     {
      get { return this.name; }
      set { this.name = value; }
     }
    
     public Gender Gender
     {
      get { return this.gender; }
      set { this.gender = value; }
     }
    }
    
    public enum Gender
    {
     Male,
     Female
    }
  13. Thank you very very much for your replay.
    I’m going to try it now, and than I’LL let you know about the result.
    Best regards Linn

  14. I can see the dropdown, but it doesn’t work, can’t see any values in te box.
    Can I send you a private message with my code.

  15. Hi,

    is it possible we can get the datagridviewcombobox index? I need it urgently… thanks.

  16. You’ll have to explain what “DGV ComboBox Index” means.. Because right now, i really don’t know what you’re asking for…

  17. Don’t know who you are but thank you very very much. Solved (all of my) problem(s)

  18. Hello Tim,

    I struggling with a DataGridView ComboboxColumn. More specifically with the CTRL+0 keystroke to set a dbnull value into the combobox.
    The datagrid has a datasource with a dataset. The combobox shows an optional relationship with a “lookup table”.

    It works fine…. That is to say it works fine if the type of valuemember of the combobox is for example a string. BUT if it is an integer ist doesn’t work. And of course the primarykey of the lookup tables is a identity integer.

    It gives following error when I type in CTRL+0 : Cannot set Column “myColumn” to be Null. Please use DBNull instead.

    Once again it works OK with a combobox if the value is of a string type.

    Could you help me out?

    Thanks is advance.

    Best Regards,

    Alexander

  19. Hi Tim,

    I have a similar requirement in my project. I have placed a combo box in the data grid. I have also set the data source, datapropertyname, displaymember, valuemember for the combo. I have set bindingsource for the datagrid too. My problem is when I try to select a value from the combo, it doesn’t drop down. It doesn’t show the list. Can you please help me with this. Thanks.

  20. This thread seems to be a bit old but I will post here anyways for future reference for anyone who has experienced the same oddity with the combobox column as I have.

    When working with the DataGridViewComboBoxColumn object that was bound to a datatable, I was getting the error: Cannot set Column “columnName” to be null. Please use DBNull instead. I got this error when attempting to select a blank item from the combobox. The blank item was an actual record in the source table for the combobox because the client wanted to be able to set a blank value for the column. So, in this case the value of the ValueMember was 574 and the value of the DisplayMember was “” (String.Empty).

    After an hour and a half of DataGridView related articles, I finally came upon a DataGridView white paper article. One of the sections refers to handling null values in the grid. After quickly looking over the table that listed the default values of the DefaultCellStyle.NullValue property of each different column object, I noticed the default for the combobox column just happens to be String.Empty. So, after setting the DefaultCellStyle.NullValue property to System.DBNull.Value my error dissapeared. It appears that the grid was “confused” by my use of String.Empty as the DisplayMember of an item in the combobox when its default “null” value was also String.Empty. I hope someone else out there finds this information useful.

    Take Care,
    Ryan Hansen
    When working with the DataGridViewComboBoxColumn object that was bound to a datatable, I was getting the error: Cannot set Column “columnName” to be null. Please use DBNull instead. I got this error when attempting to select a blank item from the combobox. The blank column was an actual record in the source table for the

  21. is there a VB sourcecode for this ?
    i’m quite new and there are some parts where i really am clueless on what it means

  22. hai, i used a DataGridViewComboBoxCell in a DataGridviewControl

    i set the data source and display member and value member but my problem is to set a default value to that combo box !!! thnaks in advance !!!

  23. Hi Tim,

    In DataGridViewComboBoxColumn, user has to go four clicks in order to pick an option:
    1)select the cell
    2)select the dropdown control within the cell
    3)click to drop down control to display its items
    4)select an item

    Is there a way such that DataGridViewComboBoxColumn behaves like a normal combobox where user will only click dropdown box and then pick an option.

    Thanks,

    Reno

  24. Tim,

    What do I do when my underlying data object has a property of type int, but I want to treat it as an enum? I have a value in my database stored as an int that is effectively an enum and want to use a combobox like you show here. I keep getting cell value is not valid exceptions from the datagridview and I don’t know what to try next.

  25. Hi Tim,

    I situation is same as Linn, were I have get data from datatable. SupplierID base on CategoryID. on debug mode the supplierID have list but the combobox is empty.

    Can you email me the code you send to Linn? that fix the empty combobox isure

    Thanks
    Jack

  26. My Question is :
    How I can display selected value from combo box in data grid view using VB.Net
    Please send me code in my email id

  27. Pls. tim,
    i have a datagridview with a combobox column. the datasource of the combobox is a datatable retrieved from a database. however, i cant seem to get the datagridview to be editable @ runtime. the combbox will not dropdown at all. pls. help thanks

  28. Thank you very much Tim, for this valuable article.
    Complex databinding with business objects are much clear for me now:)

  29. Thank you !!
    I have been tring to get my combo column to work right for a week with no success, until i read your article.

  30. Hey,

    nice work. Exactly what I was looking for. I’ve got one more question. How did you get it that in the ComboboxColumn Item No.1 is preselected? In my application SelectedIndex seems to be -1 and I have no clue how to change this at application start.

    Regards, Alex

Comments are closed.