logo
03

Отрисовка

Метод OnPaint должен заниматься только максимально быстрой отрисовкой контрола. Зачастую, в нетривиальном контроле в этом методе стоит воспользоваться предварительными расчётами, произведенными один раз. Для этого лучше создать специальный метод Recalculate, которые и рассчитает необходимые размеры, прямоугольники и местоположение элементов. В конце этого метода вызывается Invalidate для обновления внешнего вида контрола на экране. Свойства и обработчики событий, меняющие расположение составляющих контрола, вызывают Recalculate после установки новых значений. Если свойство не меняет расположения (layout), достаточно вызвать лишь Invalidate. Например, свойство BorderWidth меняет расположение частей контрола, поэтому требует вызова Recalculate. Напротив, после изменения свойства ForeColor достаточно вызвать Invalidate.

Если контрол состоит из элементов, предоставьте событие PaintItem, чтобы пользователь мог изменить рисование элементов, не прибегая к наследованию. В данных события (наследнике EventArgs) предусмотрите устанавливаемое свойство Handled, с помощью которого пользователь сможет отменить стандартное рисование. Также можно рассмотреть режим рисования вроде OwnerDraw, при котором стандартное рисование вообще игонрируется. В простом случае метод рисования элемента будет выглядеть примерно так:

public event PaintItemEventHandler PaintItem;

protected virtual void DrawItem(Graphics g, ItemType item, Rectangle rect)

{

if (PaintItem != null)

{

PaintItemEventArgs e = new PaintItemEventArgs(g, item, rect);

PaintItem(this, e);

if (e.Handled)

return;

}

... стандартная отрисовка элемента ...

}

В данном случае создавать отдельный виртуальный метод OnPaintItem для передачи события подписчикам не имеет смысла, так как метод DrawItem виртуальный и переопределять нужно именно его. В случае, если подписчиков нет, то и вся связанная с ними логика игнорируется.

Расчёт таких параметров, как ширина или высота текста, производится при помощи объекта Graphics. Его можно получить, используя метод Control.CreateGraphics(). Имеет смысл рассмотреть кэширование полученного объекта Graphics, если он используется часто и наблюдаются проблемы со скоростью.

При выводе текста можно использовать StringFormat для указания параметров выводимой строки. Например, для центрирования текста лучше использовать StringFormat, чем вручную рассчитывать координаты. Если выводимые строки могут быть длинными и не помещаться в отводимое для них место, используйте StringFormat.Trimming. Не забывайте также выставить StringFormat.HotkeyPrefix в значение HotkeyPrefix.None если не нужна специальная обработка символа “&”.

Для отрисовки стандартных элементов контрола (вроде рамок и кнопок) используйте класс ControlPaint. Если вы создаете библиотеку контролов, имеющих много общих элементов, предусмотрите собственный класс такого вида. Это не только позволит избежать ошибок и сократит код, но и позволит в будущем добавлять новые возможности, стили и виды для всей библиотеки. Учтите, однако, что ControlPaint не поддерживает стили Windows (XP-themes).