Вообще говоря, сам термин Visual Component Library (VCL) при разном переводе может ввести в заблуждение, так как не все компоненты этой библиотеки являются визуальными. Путаница добавляется еще и тем, что некоторые классы VCL вообще не являются компонентами. Давайте разберемся, в чем же тут дело.
На рис. 4.1 приведена иерархия основных (базовых) классов, формирующих структуру VCL. Рассмотрим ее подробней.
На вершине иерархии находится класс TObject
, являющийся предком всех классов;
От него происходит TPersistent
, обеспечивающий методы потоковых объектов. Потоковый объект – это такой объект,
который, как ни странно это звучит, может сохраняться в потоке. Это необходимо для
сохранения объекта в файле формы;
Класс TComponent
, как можно догадаться по его названию, по существу, представляет собой вершину
иерархии компонентов и является простейшим базовым классом для создания новых
компонентов;
Класс TControl
реализует код, необходимый для визуальных компонентов;
Существует два базовых типа визуальных элементов управления: графические и оконные.
Каждый тип представляется своей иерархией, графические элементы управления происходят от
TGraphicControl
а оконные – от TWinControl
. Принципиальное отличие этих типов состоит в том, что оконные элементы имеют
идентификатор (хендл) окна и могут принять фокус ввода, а графические – нет.
Оконные компоненты так же разбиваются на две категории. Первая категория это – прямые
потомки TWinControl
, являющиеся оболочками вокруг элементов управления, реализованных в Windows и,
следовательно уже знающие как себя рисовать. Примером такого компонента является редактор
TEdit
;
Для компонентов же, которые нуждаются в идентификаторе окна, но не имеет реализации в
Windows, имеется класс TCustomControl
, обеспечивающий возможность пользовательского рисования с использованием
свойства Canvas подобно TGraphicControl
.
Прямые потомки TComponent
представляют собой невизуальные
компоненты, такие, как, например, TOpenDialog
,
TTimer
.
В VCL существует несколько вспомогательных классов, не являющихся компонентами
(поскольку они не происходят от TComponent
), но которые, тем не менее очень полезны для создания компонентов. Некоторые из
них потоковые, такие как TStrings
и TFont
,
могут быть использованы как типы опубликованных свойств и сохранены в файле формы.
Непотоковые классы, такие, например, как TList
и
TIniFile
используются в реализации алгоритмов обработки данных
внутри методов.
Рассмотрим базовые классы подробней с точки зрения разработчика компонентов.
Класс TComponent
обеспечивает свойства и методы, которые обеспечивают управление компонентами в
дизайнере форм. Помимо свойств Name (Имя) и Tag (Дескрипотор), класса TComponent
вводит еще
несколько свойств, полезных для создания компонентов. Одним из наиболее полезных и, как
следствие, часто используемых является свойство ComponentState,
представляющее собой множество, определяющее состояние компонента.
Наиболее часто используемые элементы множества ComponentState приведены в Таблица 4.1.
Элемент | Описание состояния компонента |
csDesigning | Компонент находится в режиме проектирования на форме. |
csDestroying | Компонент сейчас будет разрушен. |
csLoading | Компонент загружается из файла формы. |
csReading | Компонент считывает свои свойства из файла формы. |
csWriting | Компонент записывает свои свойства в файл формы. |
Распространенное использование свойства ComponentState заключается в определении, находится ли компонент во время разработки. Например
(внимание на экран), компонент TImage
использует это свойство
для того, чтобы узнать, нужно ли рисовать пунктирную линию вокруг элемента
управления:
Пример 4.1. Использование свойства ComponentState
procedure TImage.Paint; begin if csDesigning in ComponentState then with inherited Canvas do begin Pen.Style := psDash; Brush.Style := bsClear; Rectangle(0, 0, Width, Height); end; ... end;
Класс TComponent
так же вводит два свойства, отвечающие за принадлежность компонента другим
компонентам. Во-первых, это свойство Owner, определяющее компонент-владелец, а, во-вторых,
это свойство Components, представляющее собой массив ссылок на
дочерние компоненты.
Самым важным фактом, который обеспечивают эти два свойства, заключается в том, что за разрушение объекта ответственен владелец.
Переходя к методам TComponent
следует, прежде всего,
упомянуть метод Notification
(дословно – «Уведомление»),
вызывающийся каждый раз, когда компонент располагается на форме или удаляется с нее.
Перекрытие метода Notification
позволяет избежать ошибок,
которые могли бы возникнуть в случае удаления свойств, являющихся ссылкой на
компонент.
Например, метка, а точнее ее предок TCustomLabel
в
переопределенном методе Notification
проверяет не удаляется
ли компонент, на который она ссылается в своем поле
FFocusControl. Если это так, то ссылка устанавливается в
Nil, что позводяет избежать ошибки защиты памяти:
Пример 4.2. Перекрытие метода Notification
procedure TCustomLabel.Notification(AComponent: TComponent; Operation: TOperation); begin inherited Notification(AComponent, Operation); if (Operation = opRemove) and (AComponent = FFocusControl) then FFocusControl := nil; end;
В TComponent
определен еще один весьма полезный виртуальный
метод Loaded
, вызывающийся в тот момент, когда все значения
компонента считаны из файла формы. Вызов Loaded
происходит
раньше, чем отображается форма и компонент, поэтому его обработка не
связана с перерисовкой компонента.
Пример использования метода Loaded можно найти в компоненте
TMediaPlayer
:
Пример 4.3. Реализация метода Loaded
procedure TMediaPlayer.Loaded; begin inherited Loaded; if (not (csDesigning in ComponentState)) and FAutoOpen then Open; end;
Суть переопределенного метода заключается в проверке считаны ли все свойства из файла
формы и если да, то если компонент не находится в состоянии проектирования и установлено в
свойство AutoOpen в True. Если
эти два условия выполняются, то TMediaPlayer
пытается открыть
заданный файл.
Следующим в иерархии классов после TComponent
идет
родоначальник всех визуальных компонент TControl
.
Класс TControl
вводит ряд важных свойств, необходимых для
визуальных компонентов. Это, прежде всего, свойство Parent,
определяющее компонент, на поверхности которого находится текущий объект. По аналогии с
Owner, ответственным за разрушение объекта,
Parent в ответе за передачу сообщения на прорисовку
объекта.
Второе представляющее интерес свойство – это свойство
ControlStyle. Наиболее важные элементы множества
ControlStyle приведены в таблице. Обычно множество
ControlStyle изменяют в конструкторе
Create
для изменения поведения компонента.
Элемент | Поведение элемента управления |
csAcceptsControls | Становится родителем для всех элементов управления, помещенных на него во время проектирования |
csCaptureMouse | Перехватывает события мыши |
csDesignInteractive | Во время проектирования щелчки правой кнопки мыши преобразуются в щелчки левой кнопки |
csClickEvents | Генерируется событие OnClick. |
csFramed | Элемент управления имеет трехмерную рамку. |
csSetCaption | Свойство Caption устанавливается таким же, как и Name, если в Caption не содержится какого-либо оригинального значения, установленного явно. |
csOpaque | Заполняет всю клиентскую область. |
csDoubleClicks | Принимает и передает двойные щелчки мышью. |
csFixedWidth | Масштабирование не влияет на ширину. |
csFixedHeight | Масштабирование не влияет на высоту. |
csNoDesignVisible | Не видим во время проектирования. |
csReplicatable | Может быть отрисован методом PaintTo на другом Canvas |
csNoStdEvents | Игнорирует события мыши и клавиатуры. Ускоряет работу приложения, если нет необходимости в обработке этих событий. |
Кроме того, в классе TControl
объявлено множество методов
диспетчеризации событий мыши.
Класс TGraphicControl
является основой для создания
графических элементов управления, не имеющего собственного идентификатора окна, но
способного реагировать на события мыши. Пользовательская отрисовка происходит на свойстве
Canvas
в виртуальном методе Paint
,
переопределяемом в классе-наследнике.
Класс TWinControl
используется в качестве базового для создания
компонентов, инкапсулирующих существующие элементы управления, которые сами себя рисуют.
Такие компоненты включают стандартные элементы управления Windows.
Класс TWinControl
реализует свойство Handle,
являющееся ссылкой на идентификатор окна базового элемента управления. Это значит, что
наследники TWinControl
могут обрабатывать события клавиатуры и
изменения фокуса.
При создании наследников TWinControl
в большинстве случаев
требуется переопределять виртуальные методы CreateParams
и
CreateWnd
. Метод CreateWnd
вызывается
при создании окна, при этом CreateWnd
вызывает
CreateParams
для инициализации записи параметров создания окна и
затем вызывает CreateWindowsHandle
для создания реального
идентификатора окна. Затем CreateWnd
устанавливает размер окна и
устанавливает шрифт элемента управления.
На практике это выглядит не так страшно, как звучит.
Пример 4.4.
TSmartListBox = class(TListBox) ... implementation procedure TSmartListBox.CreateParams(var Params: TCreateParams); begin inherited; Params.Style:=Params.Style or WS_HSCROLL; end; procedure TSmartListBox.CreateWnd; begin inherited; Perform(LB_SETHORIZONTALEXTENT,2,0); ShowScrollBar(Handle,SB_HORZ,true) end;
В приведенном примере создается элемент управления, наследуемый от
TListBox
. В методе CreateParams в стиль окна добавляется флаг
WS_HSCROLL, определяющий наличие полосы горизонтальной прокрутки. В методе
CreateWnd
текущему объекту посылается сообщение
LB_SETHORIZONTALEXTENT, заставляющее установить небольшой отступ в прокрутке и включается
видимость полосы прокрутки.
В результате элемент управления получает горизонтальную полосу прокрутки.
В ряде случаев требуется элемент управления, обладающий идентификатором окна и в то же
время позволяющий использовать Canvas для пользовательской
отрисовки компонента. К счастью такой класс существует и называется он
TCustomControl
. Он является удачной комбинацией оконного
класса TWinControl
и графического элемента управления
TGraphicControl
.