В Windows Forms DataGridView часто используется для отображения табличных данных. Иногда в одной из колонок необходимо предоставить пользователю список значений – для этого существует DataGridViewComboBoxColumn. В этой статье мы подробно разберём, как правильно привязать данные к такому выпадающему списку, какие бывают варианты источников данных и как избежать типичных ошибок.
Содержание
- Что такое
DataGridViewComboBoxColumn - Подготовка источника данных
- Привязка столбца к источнику
- Установка выбранного значения
- Полные примеры
- Типичные ошибки и их решение
- Полезные советы
Что такое 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. Решения:- Убедитесь, что тип
ValueMemberсовпадает с типом свойства, которое хранит значение. - Если в источнике могут быть «пустые» значения, добавьте в
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‑приложений.
Удачной разработки!