Как раскрасить строки (изменить цвет текста) в ComboBox (C# и WinForms)

Имеется комбо-бокс, связанный с источником данных. Задача: раскрасить строки в разные цвета по какому-то критерию, связанному с источником данных.

Чтобы решить эту задачу придется изменить отрисовку на экране строк комбо-бокса повесившись на событие DrawItem. По умолчанию это событие не происходит, чтобы оно начало работать надо поставить свойство ComboBox DrawMode в значение OwnerDrawVariable или OwnerDrawFixed из перечисления DrawMode

Если наш комбо-бокс заполнен простым массивом или списком строк и не привязан к сложному источнику данных с DisplayMember и ValueMember, то код раскраски внутри события (в зависимости от строки) будет примерно таким

// заполнение коллекции элементов
for (int counter = 1; counter <= 10; counter++)
{
    _testNonBindedComboBox.Items.Add(counter.ToString());
}
_testNonBindedComboBox.SelectedIndex = 3;
_testNonBindedComboBox.DrawMode = DrawMode.OwnerDrawVariable;
//private void _testComboBox_DrawItem(object sender, DrawItemEventArgs e)

e.DrawBackground();

// в событие комбобокс передается в универсальнмо типе Object, конвертируем в комбо-бокс
ComboBox currComboBox = (ComboBox)sender;

currComboBox.SelectedIndex = e.Index;
string text = currComboBox.Items[e.Index].ToString();
int textInteger = Convert.ToInt32(text);

// определяем цвет для текущей строки  
Brush brush;
if (textInteger < 3)
{
    brush = Brushes.Red;
}
else if (textInteger > 3 && textInteger < 6)
{
    brush = Brushes.Green;
}
else
{
    brush = Brushes.Blue;
}
// прорисовываем текст в строке комбо-бокса
e.Graphics.DrawString(text, currComboBox.Font, brush, e.Bounds.X, e.Bounds.Y);

Но в реальной жизни комбо-бокс чаще всего привязан к DataTable, каждая строка имеет и видимое значение и код, раскраска же может зависеть от каких-нибудь дополнительных полей, который надежнее всего получать по коду (так как на таблицу могут динамически накладываться фильтры). Здесь возникает проблема - во время отрисовки мы обходим все элементы, но напрямую получить ValueItem по интексу элемента в items невозможно, каждый item имеет тип object и возвращает DisplayValue через ToString(). В данном случае внутрии коллекции items будут на самом деле сидеть обьекти класса DataRowView, через которые можно уже достучаться до исходного DataRow.

Если таблица-источник имеет три колонки ID, TYPE и TEXT, то получить значение TYPE по ID и в зависимости от него раскрасить строки можно следующим образом

//private void _testComboBox_DrawItem(object sender, DrawItemEventArgs e)
e.DrawBackground();

// в событие комбобокс передается в универсальнмо типе Object, конвертируем в комбо-бокс
ComboBox currComboBox = (ComboBox)sender;

// вернет System.DataRow.DataRowView так как на самом деле там сидит обьект класса DataRowView
//string text = currComboBox.Items[e.Index].ToString();

DataRowView currIntemDRView = (DataRowView)currComboBox.Items[e.Index];
string text = currIntemDRView.Row["TEXT"].ToString();
int id = Convert.ToInt32(currIntemDRView.Row["ID"]);



// в реальном проекте может быть сложнее, иллюстрируем общую идею зависимости цвета от кода строки
int type = 0;
foreach (DataRow currRow in _testTable.Rows)
{
	int currId = (int)currRow["ID"];

	if (currId == id)
	{
		type = (int)currRow["TYPE"];
		break;
	}
}

// определяем цвет для текущей строки  
Brush brush;
if (type == 0)
{
	brush = Brushes.Red;
}
else if (type == 1)
{
	brush = Brushes.Green;
}
else 
{
	brush = Brushes.Blue;
}

// прорисовываем текст в строке комбо-бокса
e.Graphics.DrawString(text, currComboBox.Font, brush, e.Bounds.X, e.Bounds.Y);

Есть один интересный момент, низкоуровневое событие DrawItem происходит очень часто, намного чаще привычных логических событий вроде cмены выбранного элемента. Чтобы наглядно продемонстрировать эту частоту в тестовом проекте добавлена метка, по которой наглядно видно, что за пару секунд работы с открытием-закрытием списка событие срабатывает сотни раз. Таким образом медленные операции и операции меняющие состояние самого ComboBox (что может вызвать рекурсивный вызов этого события заново) надо вешать на него с большой осторожностью. Коллега получила переполнение буфера в mscorlib.dll по совершенно невнятным причинам.

Тестовый проект с демонстрацией вышеописанного

Тестовый проект ColorComboBox в .zip

Дополнительно:


Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *


*

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>