Работа с DataGridViewComboBoxColumn: привязка данных и типичные ошибки

Узнайте, как быстро привязать список к DataGridViewComboBoxColumn в WinForms, какие источники данных использовать и как обойти типичные подводные камни.

Бесплатная консультация юриста

+7 (800) 301-07-30

Реклама jurik.ru

В Windows Forms DataGridView часто используется для отображения табличных данных. Иногда в одной из колонок необходимо предоставить пользователю список значений – для этого существует DataGridViewComboBoxColumn. В этой статье мы подробно разберём, как правильно привязать данные к такому выпадающему списку, какие бывают варианты источников данных и как избежать типичных ошибок.


Содержание

  1. Что такое DataGridViewComboBoxColumn
  2. Подготовка источника данных
  3. Привязка столбца к источнику
  4. Установка выбранного значения
  5. Полные примеры
  6. Типичные ошибки и их решение
  7. Полезные советы

Что такое DataGridViewComboBoxColumn

Это специализированный столбец DataGridView, в ячейках которого отображается ComboBox. Он поддерживает:

  • Привязку к отдельному источнику данных (список, DataTable, BindingList<T> и т.п.).
  • Настройку DisplayMember – свойства, которое будет показываться пользователю.
  • Настройку ValueMember – свойства, которое будет храниться в ячейке.
  • Синхронизацию с полем объекта, если DataGridView привязан к коллекции.

Ключевые свойства

DataSource Коллекция, из которой берутся элементы выпадающего списка.
DisplayMember Имя свойства (или колонки), которое будет отображаться в списке.
ValueMember Имя свойства (или колонки), которое будет использоваться как значение ячейки.
DataPropertyName Имя свойства объекта‑источника DataGridView, куда будет записываться выбранный ValueMember.

Подготовка источника данных

Существует несколько популярных вариантов:

Список объектов (List<T> или BindingList<T>)

public class Phone
{
 public int Id { get; set; }
 public string Name { get; set; }
 public int Year { get; set; }
}

// где‑то в конструкторе формы
var phones = new List<Phone>
{
 new Phone { Id = 1, Name = "Samsung Galaxy Ace", Year = 2012 },
 new Phone { Id = 2, Name = "Samsung Galaxy S4", Year = 2013 },
 new Phone { Id = 3, Name = "iPhone 6", Year = 2014 },
 new Phone { Id = 4, Name = "Microsoft Lumia 435",Year = 2015 },
 new Phone { Id = 5, Name = "Xiaomi Mi 5", Year = 2015 }
};

DataTable

DataTable dt = new DataTable;
dt.Columns.Add("Table_ID", typeof(int));
dt.Columns.Add("Table_Name", typeof(string));
dt.Rows.Add(1, "Customers");
dt.Rows.Add(2, "Orders");
dt.Rows.Add(3, "Products");

DataSet (таблица внутри набора)

DataSet ds = new DataSet;
ds.ReadXml("Tables.xml"); // пример чтения из XML
DataTable tables = ds.Tables["Tables"];

Привязка столбца к источнику

Самый простой способ – создать столбец программно и добавить его в DataGridView:

// 1. Создаём колонку
DataGridViewComboBoxColumn comboColumn = new DataGridViewComboBoxColumn;
comboColumn.HeaderText = "Таблица";
comboColumn.Name = "TableColumn";

// 2. Указываем, откуда брать данные
comboColumn.DataSource = tables; // любой из вариантов выше

comboColumn.DisplayMember = "Table_Name"; // что показывать
comboColumn.ValueMember = "Table_ID"; // что сохранять

// 3. Если DataGridView привязан к коллекции объектов,
// указываем свойство, в которое будет записываться выбранный ID
comboColumn.DataPropertyName = "TableId"; // имя свойства в объекте‑строке
// 4. Добавляем колонку в грид
dataGridView1.Columns.Add(comboColumn);

Важно: если DataGridView уже привязан к источнику (DataSource = someList), то DataPropertyName обязателен – без него выбранное значение не будет сохраняться в объекте.

Привязка к уже существующей колонке

Если колонка была добавлена через дизайнер, её можно настроить в коде:

var col = (DataGridViewComboBoxColumn)dataGridView1.Columns["TableColumn"];
col.DataSource = tables;
col.DisplayMember = "Table_Name";
col.ValueMember = "Table_ID";
col.DataPropertyName = "TableId";

Установка выбранного значения в ячейке

После привязки часто возникает вопрос: «как задать значение в конкретной строке?»

Через объект‑источник

Если DataGridView привязан к списку объектов, достаточно изменить свойство объекта и вызвать Refresh:

var rowObject = (MyRow)dataGridView1.Rows[2].DataBoundItem;
rowObject.TableId = 3; // ID нужной таблицы
dataGridView1.Refresh; // обновит отображение

Через ячейку напрямую

Если грид не привязан к объектам, можно задать значение ячейки вручную:

int rowIndex = 0;
int columnIndex = dataGridView1.Columns["TableColumn"].Index;
dataGridView1.Rows[rowIndex].Cells[columnIndex].Value = 2; // ID таблицы

Обратите внимание: значение должно соответствовать типу ValueMember. Если типы не совпадают (например, строка вместо int), значение не отобразится и будет выброшено исключение DataGridViewComboBoxCell value is not valid.

Обработка события DataError

Чтобы отлавливать такие ситуации, подпишитесь на событие:

dataGridView1.DataError += (s, e) =>
{ // Игнорируем ошибку, если она связана с несовпадением типов
 if (e.Exception is ArgumentException)
 e.ThrowException = false;
};

Полные примеры

Пример с List<Phone> в отдельном столбце

public partial class Form1 : Form
{
 private List<Phone> _phones;

 public Form1
 {
 InitializeComponent;
 LoadPhones;
 InitGrid;
 }

 private void LoadPhones
 {
 _phones = new List<Phone>
 {
 new Phone { Id = 1, Name = "Samsung Galaxy Ace", Year = 2012 },
 new Phone { Id = 2, Name = "Samsung Galaxy S4", Year = 2013 },
 new Phone { Id = 3, Name = "iPhone 6", Year = 2014 },
 new Phone { Id = 4, Name = "Microsoft Lumia 435",Year = 2015 },
 new Phone { Id = 5, Name = "Xiaomi Mi 5", Year = 2015 }
 };
 }

 private void InitGrid
 {
 // 1. Привязываем основной источник (может быть любой DataTable)
 var dt = new DataTable;
 dt.Columns.Add("Id", typeof(int));
 dt.Columns.Add("Title", typeof(string));
 dt.Columns.Add("PhoneId", typeof(int)); // будет хранить Id выбранного телефона
 dt.Rows.Add(1, "First row", 2);
 dt.Rows.Add(2, "Second row", 5);
 dataGridView1.DataSource = dt;

 // 2. Создаём ComboBox‑колонку
 var phoneColumn = new DataGridViewComboBoxColumn
 {
 HeaderText = "Phone",
 Name = "PhoneColumn",
 DataSource = _phones,
 DisplayMember = "Name",
 ValueMember = "Id",
 DataPropertyName = "PhoneId", // <-- связь с колонкой PhoneId в dt
 Width = 150,
 FlatStyle = FlatStyle.Flat
 };

 dataGridView1.Columns.Add(phoneColumn);
 }
}

В результате в каждой строке будет выпадающий список с названиями телефонов, а в скрытой колонке PhoneId будет храниться их Id.

Пример с DataTable и динамической установкой значения

// Предположим, у нас уже есть DataGridView, привязанный к List<Order>
public class Order
{
 public int OrderId { get; set; }
 public int TableId { get; set; } // сюда будем записывать выбранный ID
}
// В конструкторе формы
var tables = GetTables; // DataTable с колонками Table_ID, Table_Name
var orders = new BindingList<Order>
{
 new Order { OrderId = 101, TableId = 2 },
 new Order { OrderId = 102, TableId = 1 }
};

dataGridView1.AutoGenerateColumns = false;
dataGridView1.DataSource = orders;

// Добавляем колонку ID (скрытую)
var idCol = new DataGridViewTextBoxColumn
{
 DataPropertyName = "OrderId",
 HeaderText = "Order ID",
 ReadOnly = true
};
dataGridView1.Columns.Add(idCol);

// Добавляем ComboBox‑колонку
var combo = new DataGridViewComboBoxColumn
{
 HeaderText = "Table",
 DataPropertyName = "TableId", // <-- связь с Order.TableId
 DataSource = tables,
 DisplayMember = "Table_Name",
 ValueMember = "Table_ID",
 FlatStyle = FlatStyle.Popup,
 Width = 120
};
dataGridView1.Columns.Add(combo);

// Устанавливаем значение программно (например, после загрузки из БД)
orders[0].TableId = 3; // будет отображено "Products"
dataGridView1.Refresh;

Типичные ошибки и их решение

  • Ошибка DataGridViewComboBoxCell value is not valid – возникает, когда значение ячейки не найдено в DataSource. Решения:
    1. Убедитесь, что тип ValueMember совпадает с типом свойства, которое хранит значение.
    2. Если в источнике могут быть «пустые» значения, добавьте в DataSource строку --Select-- с Value = DBNull.Value или null.
  • ComboBox не показывает выбранное значение – часто забывают задать DataPropertyName (при привязке к объектам) или установить Value ячейки в нужный тип.
  • Список в выпадающем меню пустой – проверьте, что DataSource действительно содержит данные к моменту создания колонки. Если данные загружаются асинхронно, привязывайте колонку после завершения загрузки.
  • Дублирование колонок – при повторном вызове метода добавления колонки убедитесь, что колонка ещё не существует (проверьте Columns.Contains).

Полезные советы

  • Используйте BindingList<T> вместо List<T>, если планируете динамически добавлять/удалять элементы – тогда DataGridView будет автоматически обновляться.
  • Для больших справочников рекомендуется привязывать DataTable через DataView с фильтрацией, чтобы не перегружать UI.
  • Стилизация: свойство FlatStyle позволяет сделать выпадающий список более «плоским». Для кастомных цветов используйте DefaultCellStyle и DefaultHeaderCellStyle.
  • Объединение с DataGridViewComboBoxCell: если нужен разный набор значений в разных строках, можно в событии CellBeginEdit менять DataSource конкретной ячейки.
  • Поддержка клавиатуры: задайте DisplayStyle = DataGridViewComboBoxDisplayStyle.Nothing, если хотите, чтобы пользователь видел только значение, а не кнопку раскрытия до наведения.

Привязка ComboBox к колонке DataGridView в WinForms – мощный инструмент, позволяющий пользователям выбирать значения из справочников прямо в таблице. Ключевые моменты:

  • Подготовьте корректный источник данных (List<T>, DataTable и т.п.).
  • Установите DisplayMember, ValueMember и, при необходимости, DataPropertyName.
  • Убедитесь, что типы значений совпадают, иначе получите ошибку DataGridViewComboBoxCell value is not valid.
  • Для динамического изменения выбранного значения используйте свойство Value ячейки или изменяйте объект‑источник.

Следуя рекомендациям из этой статьи, вы сможете без проблем внедрять выпадающие списки в любые таблицы WinForms‑приложений.

Удачной разработки!