Как привязать DataGridViewComboBoxColumn к объекту? - PullRequest
4 голосов
/ 10 марта 2009

Я пытаюсь связать DataGridViewComboBoxColumn с экземпляром Foo, но когда я установил значение в сетке, я получил ArgumentException, сообщая, что я не могу преобразовать строку в 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

Я что-то упустил? возможно ли привязать данные к сложному объекту?

UPDATE:

Я реализовал TypeConverter и переопределил CanConvertFrom, CanConvertTo, ConvertTo, ConvertFrom. Теперь я получаю

FormatException: значение DataGridViewComboBoxCell недопустимо

Есть идеи?

Ответы [ 4 ]

6 голосов
/ 12 августа 2011

DataGridViewComboBoxColumn всегда должен иметь все возможные значения в списке элементов комбинированного списка, иначе будет выдано «FormatException: значение DataGridViewComboBoxCell недопустимо».

Если вы пытаетесь вернуть значения, выбранные из одного столбца комбинированного списка, вы можете обработать событие DataGridView CellParsing и получить выбранный элемент из DataGridView.EditingControl, поскольку он будет установлен для редактирования элемента управления из редактируемого столбца. Вот пример:

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

Вы также можете настроить способ отображения объектов в каждой ячейке, обрабатывая событие форматирования ячейки. Вот код, который отображает toString для любого объекта или интерфейса.

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

Обработка этих двух событий должна быть достаточной для показа и редактирования данных в любом бизнес-объекте и его лёгком, а затем писать преобразователи типов. Для этой работы установите DataGridView и столбец комбинированного списка следующим образом:

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

Нет необходимости устанавливать свойство DisplayMember или ValueMember, просто убедитесь, что в списке источников данных в выпадающем списке есть все возможные значения для Foo.

Надеюсь, это поможет.

6 голосов
/ 10 марта 2009

Вы упускаете возможный кусок.

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:

На самом деле, после прочтения вашего вопроса, я думаю, что вы столкнулись с этой отмеченной ошибкой. К сожалению, нет способа заставить его вернуть связанный объект без использования пользовательского TypeDescriptor / TypeConverter / BindingSource.

Ответ за привязку к сложному объекту. Нет по умолчанию. Я написал довольно хороший для моего текущего проекта. Это включает создание пользовательского TypeDescriptor / TypeConverter / BindingSource, который возвращает все вложенные свойства. Еще один «баг», вы не можете использовать «.» для разделителя элементов мне пришлось использовать вместо этого ':'.

4 голосов
/ 29 ноября 2013

На самом деле, вы можете использовать сложный тип в DataGridViewComboBoxColumn.

Например:

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

Self:

public ValueModeItem Self
{
    get
    {
        return this;
    }
}

Очень важно - нужно переопределить метод Equals сложного типа. В моем случае:

public override bool Equals(object obj)
{
    if (obj is ValueModeItem && obj != null)
    {
        if (...)
            return true;
    }
    return false;
}
2 голосов
/ 08 ноября 2013

Я постоянно сталкивался с той же проблемой, пока не обнаружил, что вы не можете установить DisplayMember для DataGridViewComboBoxCell, не установив также ValueMember.
Таким же образом, установка ValueMember, а не DisplayMember также является ошибкой, вы должны определить ни один, ни оба.

Ваша модель Foo, и вы, безусловно, хотите, чтобы значением ComboBox был сам элемент. Для этого проще всего создать свойство в вашем foo, возвращая себя.

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

Тогда привязки становятся:

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

Это должно работать, и значение ячейки должно иметь тип Foo, как и ожидалось.

Интересная ссылка: Проблемы с DataGridViewComboBoxColumn

Однако DataGridViewComboBoxColumn не работает так, хотя он покажет значение ToString, если вы не установите DisplayMember, что-то внутренне идет не так, когда он пытается посмотреть вверх по SelectedItem, вы должны установить DisplayMember для общественности собственность вашего класса. Еще хуже, поведение по умолчанию, если вы этого не сделаете установить свойство ValueMember, чтобы вернуть DisplayMember, есть нет способа получить сам предмет. Единственный способ обойти это добавить свойство вашего класса, которое возвращает себя и установить это свойство ValueMember. Конечно, если ваш предмет не то, что вы можете чтобы изменить (например, один из классов платформы) вам придется вместе контейнерный объект, который содержит ссылку на ваш элемент.

...