How to bound a DataGridViewComboBoxColumn to a object?

11,675

Solution 1

You are missing a possible piece.

column.DataPropertyName = "Foo";
column.DisplayMember = "SomeNameField"; 
column.ValueMember = "Bar"; // must do this, empty string causes it to be 
                            // of type string, basically the display value
                            // probably a bug in .NET
column.DataSource = from foo in Foo select foo;
grid.DataSource = data;

UPDATE:

Actually, after reading your question again, I think you are facing that noted bug. There is unfortunately no way to make it return the bound object without using a custom TypeDescriptor/TypeConverter/BindingSource.

Answer for binding to a complex object. No by default. I wrote quite a nice one for my current project. This involves making a custom TypeDescriptor/TypeConverter/BindingSource that returns all the nested properties. Another 'bug', you cant use '.' for a member separator, I had to resort to ':' instead.

Solution 2

the DataGridViewComboBoxColumn should always have all the possible values at the combobox Items list or it will throw "FormatException: The DataGridViewComboBoxCell value is not valid".

If you are trying to get back values chosen from one combobox column, you can handle the DataGridView CellParsing event, and get the selected item from DataGridView.EditingControl cause it will be set for editing control from the edited column. Here is a exemple:

private void dataGridView1_CellParsing(object sender, 
 DataGridViewCellParsingEventArgs e) {
   if (dataGridView1.CurrentCell.OwningColumn is DataGridViewComboBoxColumn) {
       DataGridViewComboBoxEditingControl editingControl = 
                (DataGridViewComboBoxEditingControl)dataGridView1.EditingControl;
       e.Value = editingControl.SelectedItem;
       e.ParsingApplied = true;
   }
}

You also can customize the way your objects are show on each cell by handling the cell Formatting Event, here is a code that display toString for any object or interface.

private void dataGridView1_CellFormatting(object sender, 
    DataGridViewCellFormattingEventArgs e) {
        if (e.Value != null) {
            e.Value = e.Value.ToString();
            e.FormattingApplied = true;
        }
    } 

Handles this two events should be enough for show and edit data within any bussiness object and its easer then write type converters. For this work set you DataGridView and you combobox column as follow:

var data = (from item in someTable
        select new { Foo = item.foo, Bar = item.Bar }).ToList();
grid.DataSource = data;
column.DataPropertyName = "Foo";
column.DataSource = (from foo in Foo select foo).ToList ();

No DisplayMember or ValueMember Property need to be set, just make sure your combobox data source list has all the possible values for Foo.

Hope its helps.

Solution 3

Actually, you can use a complex type in DataGridViewComboBoxColumn.

For example:

DataGridViewComboBoxColumn.DataPropertyName = "ValueMode";
DataGridViewComboBoxColumn.DisplayMember = "Label";
DataGridViewComboBoxColumn.ValueMember = "Self"; *
DataGridViewComboBoxColumn.ValueType = typeof(ValueModeItem);

Self is:

public ValueModeItem Self
{
    get
    {
        return this;
    }
}

Very important - need to override 'Equals' method of a complex type. In my case:

public override bool Equals(object obj)
{
    if (obj is ValueModeItem && obj != null)
    {
        if (...)
            return true;
    }
    return false;
}

Solution 4

I was constantely hitting the same problem until I discovert that you cannot set the DisplayMember of the DataGridViewComboBoxCell without setting the ValueMember too.
In the same way, setting the ValueMember and not the DisplayMember is a fail too, you have to define none or both.

Your model is Foo, and you certainly want that the Value of the ComboBox be the item itself. For doing this, the simplest way is too create a Property in your foo, returning itself.

public class Foo
{
    ...
    public Foo This { get {return this; } }
}

Then bindings becomes:

column.DataPropertyName = "Foo";
column.DataSource = (from foo in Foo select foo).ToList (); //foo is an instance of Foo
column.DisplayMember = "SomeNameField"; //Foo.SomeNameField contains a description of the instance
column.ValueMember = "This";

This should work, and the Value of the Cell should be of type Foo as expected.

An interesting reference: Problems with the DataGridViewComboBoxColumn

However, the DataGridViewComboBoxColumn doesn't work like this, although it will display the ToString value if you don't set the DisplayMember, something internally goes wrong when it tries to look up the SelectedItem, you have to set DisplayMember to a public property of your class. Even worse, the default behaviour if you don't set the ValueMember property is to return the DisplayMember, there's no way of getting actual item itself. The only work around is to add a property to your class that returns itself and set that property to the ValueMember. Of course, if your item isn't something you are able to change (such as one of the framework classes) you'll have to cludge together a container object that holds a reference to your item.

Share:
11,675
albertein
Author by

albertein

They say I get a badge for filling all of this form in.

Updated on June 14, 2022

Comments

  • albertein
    albertein almost 2 years

    I'm trying to bound a DataGridViewComboBoxColumn to an instance of Foo, but when i set a value on the grid i got a ArgumentException telling me that i can not convert from String to Foo.

    var data = (from item in someTable
                select new { Foo = item.foo, Bar = item.Bar }).ToList();
    grid.DataSource = data;
    column.DataPropertyName = "Foo";
    column.DataSource = (from foo in Foo select foo).ToList (); //foo is an instance of Foo
    column.DisplayMember = "SomeNameField"; //Foo.SomeNameField contains a description of the instance
    

    Am i missing something? is it possible to databind to a complex object?

    UPDATE:

    I implemented a TypeConverter and overrided CanConvertFrom, CanConvertTo, ConvertTo, ConvertFrom. Now i'm getting

    FormatException: The DataGridViewComboBoxCell value is not valid

    Any ideas?

  • albertein
    albertein about 15 years
    How do i make the TypeConverter?
  • leppie
    leppie about 15 years
    You inherit from TypeConverter and override the GetProperties/GetPropertiesSupported methods. Then apply the type to the TypeConverterAttribute on your desired target type. The subject-area is very large. Google for some more direction.
  • albertein
    albertein about 15 years
    I implemented a TypeConverter and overrided CanConvertTo, CanConvertFrom, ConvertTo and ConvertFrom, now i'm getting the error "FormatException: The value of the DataGridViewComboBoxCell is not valid" after setting the value
  • Peter - Reinstate Monica
    Peter - Reinstate Monica over 8 years
    Funny, this is the only thing that did the job for me, and at the same time the easiest. I cannot see how it only got 4 votes and was hard to find. Any idea why the default implementation tries to Convert the selected item's string representation to the data source's type (which fails for custom types and is hard to customize with a custom converter), instead of just frigging setting the selected item (which is available!), like the standard combo box does??