Интересной может быть особенность рисования не только на поверхности компонентов, но и внутри их. У компонента ListBox есть очень интересное свойство DrawMode, которое определяет режим рисования. По умолчанию это свойство равно Normal, и в этом случае элементы списка рисуются компонентом так, как это заложено в .NET. Но в если изменить DrawMode на OwnerDrawFixed или OwnerDrawVariable, то мы сможем написать собственный код рисования элементов списка.
Так, при использовании режима рисования OwnerDrawFixed все элементы списка будут иметь один жестко определенный размер. В случае с OwnerDrawVariable размер элементов может задаваться вами.
Давайте напишем пример, в котором на форме у нас будет список ListBox, элементы которого отображают различные стили пера Pen. Создайте новое приложение, поместите на форму компонент ListBox и измените его свойство DrawMode на OwnerDrawFixed. В конструкторе класса после вызова метода InitializeComponent() пишем следующий код:
foreach (String str in Enum.GetNames(typeof(DashStyle))) { if (str != "Custom") listBox1.Items.Add(str); }
Здесь у нас цикл, который перебирает все имена стилей перечисления DashStyle, с помощью которого можно изменять стиль рисуемой линии. Внутри цикла проверяем, равен ли текущий стиль Custom, и если он не равен Custom, то добавляем имя в список ListBox. Стиль Custom добавлять нельзя, потому что для него нужно явно указывать маску линии, а мы этого не делаем, и его добавление в список ListBox приведет к ошибке во время рисования.
Теперь создаем для компонента списка ListBox обработчик события DrawItem. Этот обработчик события вызывается каждый раз, когда нужно нарисовать очередной элемент списка. В этом обработчике события пишем код, показанный в листинге 13.5.
Листинг 13.5. Рисование элемента списка
private void listBox1_DrawItem(object sender, DrawItemEventArgs e) { // прорисовка фона e.DrawBackground(); // определяю имя текущего элемента string currName = listBox1.Items[e.Index].ToString(); // определяю стиль DashStyle dashStyle = (DashStyle)Enum.Parse(typeof(DashStyle), currName); // создаю перо Pen p = new Pen(Color.Red, 2); p.DashStyle = dashStyle; // рисую прямоугольник e.Graphics.DrawRectangle(p, e.Bounds.X + 2, e.Bounds.Y + 2, e.Bounds.Width - 4, e.Bounds.Height - 4); e.Graphics.DrawString(currName, e.Font, new SolidBrush(e.ForeColor), e.Bounds.X + 2, e.Bounds.Y); // рисую рамку выделения e.DrawFocusRectangle(); }
Самым интересным у обработчика события DrawItem является второй параметр, который имеет тип DrawItemEventArgs. Этот класс содержит необходимые свойства и методы, с помощью которых мы можем узнать, какой элемент списка нуждается в прорисовке. Давайте посмотрим на свойства этого класса, потому что они пригодятся для понимания нашего кода:
Состояния важны, если вы самостоятельно захотите прорисовывать рамку фокуса элемента, а не воспользуетесь значением по умолчанию.
Вы не обязаны использовать цвета и шрифт, указанные в свойствах. Они нужны, чтобы вы могли отрисовать элемент в палитре по умолчанию, но вы можете использовать собственные цвета и шрифт, чтобы получить элементы любой расцветки.
Обратите внимание на тип свойства State, а точнее, на имя возможного значения перечисления: ComboBoxEdit. В имени присутствует фрагмент ComboBox! Да, это состояние относится к выпадающему списку. Мы можем использовать точно такой же подход к рисованию элементов в выпадающем списке. Подход идентичен, поэтому я рассматривать его не буду. Попробуйте сами сделать ComboBox, элементы которого будут рисоваться вами самостоятельно.
Теперь посмотрим на два метода класса DrawItemEventArgs, которые могут упростить вам создание собственного метода рисования элементов:
Вернемся к нашему коду обработчика события DrawItem. В самом начале вызывается метод рисования фона по умолчанию: DrawBackground(). Это необходимо, иначе могут возникнуть проблемы с рамкой фокуса, она просто не будет стираться при перемещении с одного элемента списка на другой.
Теперь определяем имя элемента списка, который нужно прорисовать. Это не так уж и сложно, ведь мы знаем его индекс: e.Index. Зная имя, мы можем получить стиль DashStyle с помощью статичного метода Parse() класса Enum. Подобные трюки мы уже проделывали не один раз.
Следующим этапом создается перо красного цвета и толщиной в два пиксела. Бабушка, а зачем тебе такое большое перо? Это чтобы лучше его видеть, внученька! Созданному перу изменяем стиль (DashStyle) на соответствующий текущему элементу.
Теперь можно рисовать прямоугольник, который будет показывать пример использования пера, и сверху рисовать строку с названием стиля. Напоследок вызывается метод DrawFocusRectangle(), который по необходимости нарисует рамку фокуса.
В общем-то, мы могли бы нарисовать ее самостоятельно, но я не стал усложнять код проверкой состояния текущего элемента. Результат работы примера показан на рис. 13.3.
Рис. 13.3. Результат работы программы