Привязка DataGridView к DataTable с ComboBox не работает

Я пытаюсь создать DataGridView, привязанный к DataTable, где один столбец является ComboBox. Код выполняется, но после привязки (не при привязке данных) появляется следующая ошибка: System.ArgumentException: значение DataGridViewComboBoxCell недействительно.

В DataGridView одним из столбцов является DataGridViewComboBoxColumn, который использует перечисление (с именем structureType) в качестве источника:

// ColumnStructure
// 
this.ColumnStructure.ValueType = typeof(structureType);
this.ColumnStructure.DataSource = Enum.GetValues(typeof(structureType));
this.ColumnStructure.HeaderText = "Structure";
this.ColumnStructure.Name = "ColumnStructure";
this.ColumnStructure.DataPropertyName = "Structure";
//

Когда я заполняю DataGridView без использования DataTable, он работает нормально:

structureType? structure = GetStructure(part);
dgvObjectTypes.Rows.Add(name, type, structure, count);

Теперь я хотел бы использовать вместо этого DataTable, но не могу заставить его работать. DataTable создается следующим образом:

DataTable table = new DataTable();
table.Columns.Add("Name", typeof(string));
table.Columns.Add("Type", typeof(string));
table.Columns.Add("Structure", typeof(DataGridViewComboBoxCell));
table.Columns.Add("Count", typeof(int));

Другие столбцы работают отлично, но я не могу заставить работать столбец «Структура». Вот как я пытался создать поле со списком:

var cb = new DataGridViewComboBoxCell();
cb.ValueType = typeof(structureType);
cb.DataSource = Enum.GetValues(typeof(structureType));
cb.Value = (structureType)structure;

После этого я просто создаю строки для таблицы и устанавливаю таблицу как источник данных для DataGridView:

table.Rows.Add(name, type, cb, count);
dgv.DataSource = table;

Я прочитал много сообщений, в которых утверждалось, что использование перечислений в комбинированных списках вызывает проблемы (например, DataGridView, связанный с DataTable со столбцом Combobox на основе enum), но, похоже, здесь это не так. Я даже пытался использовать явно типизированные массивы строк, но все равно получаю ту же ошибку. Думаю, я что-то не так делаю с DataGridViewComboBoxCell.

В чем может быть проблема?


person JayByte    schedule 31.08.2016    source источник


Ответы (1)


Похоже, что вам не хватает шага, чтобы указать имена и значения для CBO. DataTable может хранить значение, а DGV может отображать связанное имя, но вам нужно помочь с переводом.

private enum structureType
{ None, Circle, Square, Pyramid}
...

dtStruct = new DataTable();
dtStruct.Columns.Add("Name", typeof(string));
dtStruct.Columns.Add("Type", typeof(string));
dtStruct.Columns.Add("Structure", typeof(structureType));
dtStruct.Columns.Add("Count", typeof(int));

// autogen columns == true
dgv2.DataSource = dtStruct;

// create DataSource as list of Name-Value pairs from enum
var cboSrc = Enum.GetNames(typeof(structureType)).
                    Select( x => new {Name = x, 
                                      Value = (int)Enum.Parse(typeof(structureType),x)
                                      }
                           ).ToList();

// replace auto Text col with CBO col
DataGridViewComboBoxColumn cb = new DataGridViewComboBoxColumn();
cb.ValueType = typeof(structureType);
cb.DataSource = cboSrc;
cb.DisplayMember = "Name";          // important
cb.ValueMember = "Value";           // important
cb.HeaderText = "Structure";
cb.DataPropertyName = "Structure";  // where to store the value

dgv2.Columns.Remove(dgv2.Columns[2]);  // remove txt col
dgv2.Columns.Add(cb);
cb.DisplayIndex = 2;

// add data
dtStruct.Rows.Add("Ziggy", "Foo", structureType.Circle, 6);

Первая часть создает DataTable, обратите внимание, что тип столбца структуры - structureType (или часто int). DataTable будет хранить данные, а не DataGridViewComboBoxCell элементы. В случаях, когда данные поступают из базы данных, этот столбец будет int, поскольку structureType не будет известным типом.

Затем создается DataSource из имен и значений перечисления. Это дает возможность элементу управления отображать имя, но при этом сохранять значение в DataTable.

Если DGV настроен на автоматическое создание столбцов, вам нужно будет заменить значение по умолчанию TextBoxColumn на ComboBoxColumn. Это делается после установки DataSource, но до добавления каких-либо данных. Когда данные поступают из БД (и поэтому обычно не бывает пустой типизированной таблицы), вы можете использовать событие ColumnAdded для замены одного столбца другим.

При добавлении столбца CBO важно установить свойства ValueMember и DsiplayMember, чтобы обеспечить преобразование значения ‹-> Name и DataPropertyName, чтобы он знал, где сохранить выбранное значение в DataTable.

введите описание изображения здесь

person Ňɏssa Pøngjǣrdenlarp    schedule 31.08.2016
comment
Спасибо! Я уже потерял надежду. Дальнейший вопрос: есть ли простой способ установить пустое поле со списком, если structureType? будет иметь значение NULL? Это было бы круто, но я мог бы пойти и с None, добавленным в перечисление. - person JayByte; 01.09.2016
comment
Самый простой вариант - добавить None. - person Ňɏssa Pøngjǣrdenlarp; 01.09.2016