Главная » C#, Компоненты » Рендеринг компонентов

0

В предыдущих главах мы уже сталкивались с отрисовкой визуальных компонентов, а именно с методами onPaint о, OnPaintBackgroundo. Также мы рассматривали метод OnPa int Adornments () KJiaCCa Control Designer, ПОЗВОЛЯЮЩИЙ проводить отрисовку компонента только в режиме дизайнера. Теперь пришла пора поговорить об этом более подробно.

Базовый класс визуальных компонентов Control имеет событие Paint, которое вызывается при необходимости обновить вид компонента. Виртуальный метод onPaint () позволяет переопределить отрисовку компонента:

protected virtual void OnPaint(PaintEventArgs pe);

Единственный аргумент этого метода передает всю необходимую информацию, требуемую для отрисовки компонента. Класс Paint Event Args имеет всего два свойства:

public class PaintEventArgs : EventArgs, IDisposable (

public Rectangle ClipRectangle ( get; } public Graphics Graphics ( get; }

}

Свойство ClipRectangle возвращает прямоугольную область, в рамках которой компонент может рисовать. Свойство Graphics предоставляет доступ к графическим методам, например, рисования линии, текста или круга. При реализации своего обработчика OnPaint можно вызывать базовый обработчик base.onpaint, если нужно, чтобы компонент принимал сообщения о необходимости перерисовки. Или, базовый метод можно не вызывать, если компонент постоянно перерисовывается. В последнем случае игнорирование базового метода позволит избежать мерцания.

Как я уже говорил, при необходимости вызывать обновление вида компонента, не следует вызывать метод paint о напрямую. Более правильный путь — вызвать метод invalidate о, который позволит Windows обновить компонент, когда у операционный системы дойдет до него очередь. Кроме того, метод invalidate о позволяет указать конкретную область компонента, которую нужно обновить, что может значительно повысить скорость отрисовки (например, если компонент рисует цифры на фоне, то обновлять следует только тот участок компонента, на котором рисуются цифры).

Еще один метод, относящийся к рендерингу, — это метод onpaintBackgroundo: protected virtual void OnPaintBackground(PaintEventArgs pevent) ;

Этот метод позволяет переопределить отрисовку фона компонента. Метод можно использовать, например, для рисования градиентной заливки.

Замечу, что некоторые компоненты, такие как TextBox, рисуются напрямую операционной системой, и метод Paint О (и, соответственно обработчик OnPaint) никогда не вызовутся.

Стандартный рендеринг

Наверное, можно придумать ситуацию, когда компонент должен выглядеть как стандарный (например, кнопка), но нет возможности наследовать его от соответствующего базового класса. Рисовать вид компонента вручную слишком сложно.

Класс System.Windows.Forms.ControlPaint предоставляет набор методов для рисования стандартных компонентов Windows и их элементов, например: D метод void DrawBorder(Graphics graphics, Rectangle bounds, Color color, ButtonBorderstyie style) рисует рамку компонента;

?   метод void DrawButton(Graphics graphics, Rectangle rectangle, Button- State state) рисует кнопку;

О метод void DrawCheckBox(Graphics graphics, Rectangle rectangle, Button-

State state) рисует переключатель; О метод void DrawComboButton(Graphics graphics, Rectangle rectangle, ButtonState state) рисует выпадающий список.

Это только несколько примеров. В классе controiPamt множество методов, и приводить их все в рамках этой книги не имеет смысла.

Кроме того, библиотека .NET 2.0 предоставляет несколько классов, позволяющих рисовать стандартные компоненты с учетом стилей Windows: О класс ButtonRenderer рисует кнопку;

?  класс CheckBoxRenderer рисует переключатель;

?  класс GroupBoxRenderer рисует групповую панель;

0   класс RadioButtonRenderer рисует переключатель выбора.

Проверить, доступна ли отрисовка с использованием стилей, можно с помощью свойства Application.Renderwithvisuaistyies, которое возвращает true, если стили доступны. Включить использование стилей можно с помощью метода EnableVisualStyles ()':

[STAThread] static void Main()

t

Application.EnableVisualStyles(); Application. Run (new Forml"());

1          

Получить информацию о стилях операционной системы можно с помощью класса System.Windows.Forms.VisualStyles.

Еще несколько классов позволяют рисовать компоненты, только если стили доступны, иначе вызов методов этих классов вызовет исключение: 0 ComboBoxRenderer — рисует выпадающий список;

?  ProgressBarRenderer— рисует индикатор выполнения; 0 ScrollBarRenderer — рисует ползунок;

О TabRenderer — рисует закладку;

D TextBoxRenderer — рисует элемент ввода текста; D TrackBarRenderer — рисует индикатор для выбора значения. Эти классы имеют метод isSupportedo, позволяющий проверить возможность их использования:

protected override void OnPaint(PaintEventArgs e)

{

base.OnPaint(e);

// Стили поддерживаются?

if {IComboBoxRenderer.IsSupported)

{

// Рисуем без стилей

ControlPaint.DrawComboButton(e.Graphics, this.ClientRectangle, ButtonState.Normal)-

}

else

{

// Рисуем со стилями

ComboBoxRenderer.DrawDropDownButton(e.Graphics, this.ClientRectangle, ComboBoxState.Normal);

}

)

Кроме методов, рисующих сразу элемент управления, в .NET Framework есть множество методов, рисующих графические примитивы. Это, например, FillRectangle(), DrawString(), DrawLineO, Drawlmage{) И множество других. Описывать их все я не буду. Полное и подробное описание этих методов можно найти почти в любой книге, посвященной приложениям Windows Forms.

Управление отрисовкой и поведением компонентов

Кроме собственно самой отрисовки, библиотека .NET предоставляет дополнительный метод setstyle ()1 управления отрисовкой и поведением компонента:

protected void SetStyle(ControlStyles flag, bool value)

Класс controistyies представляет собой флаги, каждый из которых соответствует некоторой характеристике компонента. Параметр value либо устанавливает соответствующий флаг (если он равен true), либо сбрасывает его.

Вот список наиболее полезных флагов.

О AllPaintinginWmPaint. Если этот флаг выставлен, то компонент игнорирует сообщение wm epasebkgnd. Метод onPaintBackground в этом случае вызывается прямо из обработчика сообщения wm_paint. Этот флаг может использоваться для уменьшения мерцания. Флаг можно выставлять только, если установлен флаг UserPaint.

О containercontroi. Если этот флаг выставлен, то компонент ведет себя как контейнер для других компонентов.

О DoubleBuffer. При установке этого флага отрисовка производится сначала в специальный буфер, а затем, после полного завершения отрисовки, результат копируется на экран. Такое поведение уменьшает мерцание во время перерисовки компонентов. Если этот флаг установлен, то нужно также установить флаги UserPaint И AllPaintinginWmPaint.

?    FixedHeight. Если этот флаг установлен, компонент блокирует все попытки изменить высоту во время автоматического выравнивания. Например, если при изменении размера шрифта система попытается изменить высоту компонента, то она не изменится.

?    Fixedwidth. Блокировка изменения ширины.

О Opaque. Если этот флаг выставлен, то компонент рисуется непрозрачным и без фона.

?    optimizedDoubleBuffer. Если этот флаг выставлен, то отрисовка сначала производится в специальный буфер и затем на экран. Это помогает предотвратить мерцание. Если этот флаг выставлен, то необходимо также выставить флаг AllPaintinginWmPaint.

?    ResizeRedraw. Если этот флаг выставлен, то компонент перерисовывается во время изменения размера.

О Selectable. Если этот флаг выставлен, то компонент может получать фокус.

?    standardciick. Если этот флаг выставлен, то компонент реализует стандартное поведение по щелчку.

?    standardDoubleClick. Если флаг выставлен, то компонент реализует стандартное поведение по двойному щелчку. Этот флаг игнорируется, если не выставлен флаг Standardciick.

?    SupportsTransparentBackColor. Если этот флаг выставлен, то компонент может иметь значение поля Backcoior с составляющей alpha меньшей, чем

255, т.е. прозрачный цвет. При этом должен быть установлен флаг Userpaint, а сам компонент должен быть наследником класса Control. D UserMouse. Если этот флаг выставлен, то события мыши не обрабатываются операционной системой. Считается, что всю необходимую обработку производит сам компонент. П userPaint. Если этот флаг выставлен, то компонент рисует себя до того, как это сделает операционная система. Если флаг не выставлен, то метод paint не вызывается. Установка нужного стиля производится либо из конструктора компонента, либо из нужного метода. В последнем случае может потребоваться вызов метода updatestyies о для немедленного применения нового значения флага. При изменении стиля вызывается событие styiechanged. Метод Getstyleo позволяет получить текущее состояние флагов.

Компонент, невидимый в режиме выполнения

При установке свойства visible в значение false компонент перестает быть видим в режиме выполнения:

textBoxl.Visible = false;

Но при этом он также перестает принимать сообщения мыши и пользовательский ввод. Если компонент должен быть невидим, но продолжать функционировать, его следует сделать прозрачным:

SetStyle(ControlStyles.SupportsTransparentBackColor, true); this.BackColor = Color.Transparent;

Правда, это, скорее, "эмуляция" прозрачности, чем реальная реализация, т.к. прозрачность достигается вызовом метода OnPaintBackgroundO компонента-владельца, что срабатывает далеко не всегда.

Управление расположением элементов

Каждый визуальный элемент управления, на котором расположены другие компоненты, может самостоятельно управлять их расположением, как это делает, например, компонент FiowLayoutPanei. В режиме выполнения можно обойтись обработкой события Resize, но в режиме разработки такой способ не подходит.

Свойство LayoutEngine класса Control возвращает объект типа LayoutEngine, позволяющий управлять расположением элементов, как в режиме разработки, так и в режиме выполнения. Посмотрим, как это выглядит на практике. Сам класс LayoutEngine очень простой и имеет всего два виртуальных метода:

public abstract class LayoutEngine {

protected LayoutEngine(); public virtual void InitLayout(

object child, BoundsSpecified specified); public virtual bool Layout(

object container, LayoutEventArgs layoutEventArgs);

}

Попробуем реализовать собственный механизм управления, который будет располагать элементы управления построчно1. Все, что для этого нужно,— ЭТО перекрыть СВОЙСТВО LayoutEngine, которое должно возвращать экземпляр нового класса управления AutoFiowLayout (листинг 11.1). Реализацию самого класса я привел в листинге 11.2. Основные действия управления расположением элементов происходят в методе Layout о. Подробно обсуждать алгоритм распределения элементов, думаю, не стоит. Главный принцип: элементы располагаются в одну "строку" до тех пор, пока они помещаются по ширине. Если больше элементов в одну строку не помещается, то панель начинает распределение на следующую "строку" (рис. 11.1). Замечу, что этот механизм перестраивает элементы и при добавлении, и при удалении элементов на панели, а также при изменении размеров панели как в режиме разработки, так и в режиме выполнения.

Листинг 11.1. Класс пэнспи с автоматическим упраилиниом расположении элементов

public class AutoFlowPanel : Panel

{

private AutoFiowLayout layoutEngine;

public AutoFlowPanel{)

{

}

// Механизм управления расположением элементов public override LayoutEngine LayoutEngine

1

get

}

if (layoutEngine — null)

}

layoutEngine = new AutoFlowLayout ();

}

return layoutEngine;

1

}

I

Листинг 11.2 Класс автоматического рзсполсжония djiumuhtom

lie class AutoFlowLayout : LayoutEngine

}

public override bool Layout(object container,

LayoutEventArgs layoutEventArgs)

}

// Панель, на которой располагаются элементы Control parent – container as Control;

// Область для расположения элементов

Rectangle parentDisplayRectangle = parent.DisplayRectangle;

// Пробуем расположить элементы //на панели так, чтобы они все были видимы Point nextControlLocation = parentDisplayRectangle.Location; nextControlLocation.Offset(parent.Margin.Left, parent.Margin.Top);

foreach (Control с in parent.Controls) (

// Невидимые элементы пропускаем

if (!c.Visible}

{

continue;

}

// Элемент не будет виден в этой "строке". // Переходим к следующей.

if ( (nextControlLocation.X + с.Width) > parent.Width) {

nextControlLocation.X =

parentDisplayRectangle.X + parent.Margin.Right; nextControlLocation.Y +=

c.Height + parent.Margin.Bottom;

}

// Располагаем компонент с.Location = nextControlLocation;

// Передвигаемся к следующей позиции в "строке" nextControlLocation.X += с.Width + parent.Margin.Right;

}

return false;

1

}

Литература:

Агуров П. В. C#. Разработка компонентов в MS Visual Studio 2005/2008. – СПб.: БХВ-Петербург, 2008. — 480 е.: ил.

По теме:

  • Комментарии