ComboBox в C# WinForms: руководство
ComboBox в Windows Forms - выпадающий список, в котором пользователь выбирает один вариант из набора и, при желании, вводит собственный текст. Здесь разобраны добавление элементов, привязка через DataSource, DisplayMember и ValueMember, событие SelectedIndexChanged, режимы DropDownStyle, автодополнение и очистка списка.
Примеры рассчитаны на .NET 8 и проект Windows Forms.

Содержание
Что такое ComboBox и из чего он состоит
ComboBox объединяет текстовое поле и раскрывающийся список. Он экономит место на форме: вместо десятка переключателей показывается одна строка, а варианты появляются по клику на стрелку. Элемент подходит для выбора страны, категории товара, единицы измерения, режима работы и любых других наборов значений.
Главные свойства: Items хранит коллекцию элементов, SelectedIndex отдаёт номер выбранного пункта (-1, если ничего не выбрано), SelectedItem возвращает сам объект, Text содержит строку в поле, а SelectedValue работает вместе с привязкой данных. Похожий по идее элемент ListBox показывает все варианты сразу, а ComboBox прячет их до раскрытия.
Добавление элементов
Заполнять список можно в конструкторе формы, в обработчике Load или из любого другого места. Есть несколько способов: по одному, пачкой и из готовой коллекции.
Add, AddRange и Insert
Метод Items.Add добавляет значение в конец списка и возвращает его индекс. AddRange принимает массив объектов и работает быстрее при большом числе элементов, а Insert вставляет значение в указанную позицию.
private void Form1_Load(object sender, EventArgs e)
{
comboBox1.Items.Add("Москва");
comboBox1.Items.Add("Казань");
comboBox1.Items.AddRange(new object[] { "Самара", "Тула", "Омск" });
comboBox1.Items.Insert(0, "Выберите город");
comboBox1.SelectedIndex = 0; // показать первый пункт
}
Для заполнения большого списка оберните вызовы в BeginUpdate и EndUpdate: пока обновление приостановлено, элемент не перерисовывается после каждого добавления, и загрузка тысяч строк проходит заметно быстрее.
comboBox1.BeginUpdate();
for (int i = 1; i <= 5000; i++)
comboBox1.Items.Add($"Пункт {i}");
comboBox1.EndUpdate();
Добавление объектов
В Items можно класть не только строки, но и любые объекты. Для показа ComboBox вызывает у каждого объекта ToString, поэтому переопределите этот метод или укажите DisplayMember. Так в списке видно название, а за ним скрыт целый объект с идентификатором и другими свойствами.
public class City
{
public int Id { get; set; }
public string Name { get; set; } = "";
public override string ToString() => Name;
}
comboBox1.Items.Add(new City { Id = 1, Name = "Москва" });
comboBox1.Items.Add(new City { Id = 2, Name = "Казань" });
if (comboBox1.SelectedItem is City city)
MessageBox.Show($"Код города: {city.Id}");
Привязка данных: DataSource, DisplayMember и ValueMember
Когда данные уже лежат в коллекции или таблице, удобнее привязать её целиком, чем добавлять элементы вручную. Свойство DataSource принимает список, массив или DataTable. DisplayMember задаёт имя свойства, текст которого виден пользователю, а ValueMember имя свойства со значением, которое вы получаете через SelectedValue.
var cities = new List<City>
{
new City { Id = 1, Name = "Москва" },
new City { Id = 2, Name = "Казань" },
new City { Id = 3, Name = "Самара" }
};
comboBox1.DataSource = cities;
comboBox1.DisplayMember = "Name";
comboBox1.ValueMember = "Id";
// позже
int id = (int)comboBox1.SelectedValue;
string name = comboBox1.Text;
Порядок присваивания важен: сначала задайте DisplayMember и ValueMember, потом DataSource, либо наоборот, но в рамках одного блока кода и до обращения к SelectedValue. Иначе возможна ситуация, когда SelectedValue возвращает объект целиком, а не значение свойства. Так бывает при первом срабатывании SelectedIndexChanged во время привязки.
С DataTable привязка выглядит так же: в DisplayMember и ValueMember указываются имена столбцов. Подробнее о загрузке таблиц и показе записей рассказано в статьях про DataGridView.
var table = new DataTable();
table.Columns.Add("Id", typeof(int));
table.Columns.Add("Title", typeof(string));
table.Rows.Add(1, "Новый");
table.Rows.Add(2, "В работе");
table.Rows.Add(3, "Завершён");
comboBox2.DataSource = table;
comboBox2.DisplayMember = "Title";
comboBox2.ValueMember = "Id";
Привязанный список нельзя изменять через Items.Add: при установленном DataSource коллекция Items доступна только для чтения. Добавляйте элементы в сам источник. Если источник - List, то для автоматического обновления используйте BindingList или BindingSource.
Привязка перечисления enum
Для выбора значения из перечисления проще всего передать в DataSource массив его значений. Так список всегда совпадает с кодом и не нужно дублировать варианты вручную.
enum Priority { Низкий, Средний, Высокий }
comboBox3.DataSource = Enum.GetValues(typeof(Priority));
comboBox3.SelectedItem = Priority.Средний;
var chosen = (Priority)comboBox3.SelectedItem!;
Console.WriteLine(chosen);
Если нужно по строке получить значение перечисления, поможет статья о преобразовании строки в enum.
Событие SelectedIndexChanged
Событие SelectedIndexChanged срабатывает, когда пользователь выбирает другой пункт или значение меняется из кода. Именно в нём обычно обновляют зависимые поля: например, после выбора страны загружают список городов. В обработчике проверяйте, что выбор действительно сделан, потому что SelectedIndex может быть равен -1.
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
if (comboBox1.SelectedIndex < 0) return;
var selected = comboBox1.SelectedItem;
label1.Text = $"Вы выбрали: {selected} (индекс {comboBox1.SelectedIndex})";
}
Обработчик можно подключить двойным щелчком по элементу в дизайнере или вручную в конструкторе формы: comboBox1.SelectedIndexChanged += comboBox1_SelectedIndexChanged. Есть и родственные события: SelectionChangeCommitted срабатывает только при выборе пользователем (не из кода), TextChanged при изменении текста, DropDown при раскрытии списка, а Validating при выходе из поля.
Зависимые списки
Классический пример - два связанных ComboBox, где содержимое второго зависит от выбора в первом. Чтобы не получить лишних срабатываний при заполнении, используйте SelectionChangeCommitted или временно отписывайтесь от события.
private readonly Dictionary<string, string[]> _cities = new()
{
["Россия"] = new[] { "Москва", "Казань", "Самара" },
["Беларусь"] = new[] { "Минск", "Гомель" },
["Казахстан"] = new[] { "Астана", "Алматы" }
};
private void comboCountry_SelectionChangeCommitted(object sender, EventArgs e)
{
comboCity.DataSource = null;
if (comboCountry.SelectedItem is string country)
comboCity.DataSource = _cities[country];
}
Свойство DropDownStyle
От режима DropDownStyle зависит, можно ли вводить собственный текст и как выглядит список. Значение выбирается в окне свойств или задаётся из кода.
| Значение | Ввод текста | Список | Когда применять |
|---|---|---|---|
| DropDown | Разрешён | Раскрывается по стрелке | Выбор из набора с возможностью ввести своё |
| DropDownList | Запрещён | Раскрывается по стрелке | Строгий выбор только из готовых вариантов |
| Simple | Разрешён | Всегда показан под полем | Редко, когда есть место на форме |
В режиме DropDownList свойство Text нельзя присвоить произвольной строкой: значение, которого нет в списке, не отобразится. Если нужно показать подсказку вроде "Выберите город", добавьте её первым элементом, как сделано в примере выше. В режиме DropDown пользователь может ввести текст, которого нет в списке, поэтому при сохранении проверяйте SelectedIndex: значение -1 означает ввод вручную.
comboBox1.DropDownStyle = ComboBoxStyle.DropDownList;
comboBox1.DropDownHeight = 200; // высота раскрытой части
comboBox1.MaxDropDownItems = 8; // сколько пунктов видно сразу
comboBox1.DropDownWidth = 250; // ширина списка, может быть шире самого поля
// режим DropDown: проверка, ввёл ли пользователь своё значение
if (comboBox1.SelectedIndex == -1 && !string.IsNullOrWhiteSpace(comboBox1.Text))
MessageBox.Show($"Введено вручную: {comboBox1.Text}");
Автодополнение
ComboBox умеет подсказывать варианты по мере набора текста. Для этого используются три свойства: AutoCompleteMode определяет способ показа (Suggest показывает выпадающую подсказку, Append дописывает остаток строки, SuggestAppend делает и то и другое), AutoCompleteSource указывает источник данных, а AutoCompleteCustomSource хранит собственный набор строк.
comboBox1.DropDownStyle = ComboBoxStyle.DropDown;
comboBox1.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
// вариант 1: подсказки из самих элементов списка
comboBox1.AutoCompleteSource = AutoCompleteSource.ListItems;
// вариант 2: собственный набор строк
var source = new AutoCompleteStringCollection();
source.AddRange(new[] { "Москва", "Мурманск", "Магадан", "Минск" });
comboBox1.AutoCompleteSource = AutoCompleteSource.CustomSource;
comboBox1.AutoCompleteCustomSource = source;
Источник ListItems подходит, когда подсказывать нужно то же, что есть в списке. В режиме DropDownList автодополнение не работает, так как ввод текста запрещён. Поиск по началу строки регистронезависим, а искать подстроку из середины стандартный механизм не умеет: для этого придётся фильтровать источник самостоятельно в событии TextChanged.
Поиск и выбор элемента из кода
Чтобы выбрать нужный пункт программно, присвойте SelectedIndex или SelectedItem. Для поиска строки по префиксу служит FindString, для точного совпадения FindStringExact. Оба метода возвращают индекс или -1. Если список привязан к данным, значение по ключу выбирают присвоением SelectedValue.
int idx = comboBox1.FindStringExact("Казань");
if (idx >= 0) comboBox1.SelectedIndex = idx;
int part = comboBox1.FindString("Сам"); // первый, начинающийся с "Сам"
comboBox1.SelectedItem = "Тула"; // выбор по значению
comboBox2.SelectedValue = 2; // выбор по ValueMember при привязке
bool exists = comboBox1.Items.Contains("Омск");
Очистка и удаление элементов
Для полной очистки вручную заполненного списка вызывают Items.Clear(). Отдельные пункты удаляют методами Remove и RemoveAt. Чтобы просто снять выбор и оставить варианты, достаточно присвоить SelectedIndex = -1. А если нужно сбросить введённый текст в режиме DropDown, выполните Text = string.Empty.
comboBox1.Items.Remove("Тула"); // по значению
comboBox1.Items.RemoveAt(0); // по индексу
comboBox1.SelectedIndex = -1; // снять выбор
comboBox1.Text = string.Empty; // очистить текстовую часть
comboBox1.Items.Clear(); // удалить все элементы
// список с привязкой: сначала сбрасываем источник
comboBox2.DataSource = null;
comboBox2.Items.Clear();
Помните, что при установленном DataSource вызов Items.Clear() приведёт к исключению: "Невозможно изменить коллекцию элементов, если задан DataSource". Сначала присвойте DataSource значение null, а затем очищайте Items или задайте новый источник. Метод ResetText тоже сбрасывает отображаемый текст.
Типичные ошибки
Частая проблема - обращение к SelectedItem.ToString() без проверки: если ничего не выбрано, SelectedItem равен null и возникает NullReferenceException, причины которого разобраны в статье про NullReferenceException. Вторая ошибка - кастинг SelectedValue в int, когда привязка ещё не завершена; в этот момент свойство может вернуть объект целиком или DataRowView. Проверяйте тип через is либо подписывайтесь на SelectionChangeCommitted.
Третья проблема - лишние срабатывания SelectedIndexChanged при начальном заполнении: установка DataSource тоже поднимает событие, и обработчик получает неполные данные. Решение - флаг "идёт загрузка" или отписка на время заполнения. Четвёртая - пользователь в режиме DropDown вводит произвольный текст, а код рассчитывает на выбранный элемент. Если ввод вручную не нужен, сразу ставьте DropDownList. Наконец, не пытайтесь хранить в строках составные значения вида "1|Москва": лучше завести класс и использовать DisplayMember с ValueMember.
Частые вопросы
Как получить выбранное значение ComboBox?
Для строк используйте comboBox1.Text или comboBox1.SelectedItem. Для привязанного списка с ValueMember берите SelectedValue. Если нужен номер позиции, читайте SelectedIndex.
Как запретить пользователю вводить свой текст?
Установите DropDownStyle = ComboBoxStyle.DropDownList. В этом режиме можно только выбрать готовый вариант, а поле ввода становится недоступным для редактирования.
Как выбрать пункт по умолчанию при загрузке формы?
В обработчике Load присвойте SelectedIndex = 0 или нужному индексу. Для привязанных данных подойдёт SelectedValue с известным ключом. Выбор по умолчанию задаётся и в окне свойств через Text.
Почему SelectedIndexChanged срабатывает при запуске формы?
Событие поднимается при любом изменении выбора, включая начальную привязку DataSource. Добавьте проверку SelectedIndex < 0 либо используйте SelectionChangeCommitted, которое реагирует только на действия пользователя.
Как сделать ComboBox с цветами или картинками?
Переключите DrawMode на OwnerDrawFixed и обработайте событие DrawItem. В нём вы сами рисуете текст и значок каждого пункта методами Graphics. Для простого списка этого не требуется.
Как отсортировать элементы в ComboBox?
Для ручного списка включите свойство Sorted = true. Для привязанного источника отсортируйте саму коллекцию заранее, например через OrderBy, потому что Sorted при DataSource не работает.





