1. Иерархия классов VCL

Вообще говоря, сам термин Visual Component Library (VCL) при разном переводе может ввести в заблуждение, так как не все компоненты этой библиотеки являются визуальными. Путаница добавляется еще и тем, что некоторые классы VCL вообще не являются компонентами. Давайте разберемся, в чем же тут дело.

На рис. 4.1 приведена иерархия основных (базовых) классов, формирующих структуру VCL. Рассмотрим ее подробней.

Иерархия базовых классов VCL

Рис. 4.1. Иерархия базовых классов VCL


  1. На вершине иерархии находится класс TObject, являющийся предком всех классов;

  2. От него происходит TPersistent, обеспечивающий методы потоковых объектов. Потоковый объект – это такой объект, который, как ни странно это звучит, может сохраняться в потоке. Это необходимо для сохранения объекта в файле формы;

  3. Класс TComponent, как можно догадаться по его названию, по существу, представляет собой вершину иерархии компонентов и является простейшим базовым классом для создания новых компонентов;

  4. Класс TControl реализует код, необходимый для визуальных компонентов;

  5. Существует два базовых типа визуальных элементов управления: графические и оконные. Каждый тип представляется своей иерархией, графические элементы управления происходят от TGraphicControl а оконные – от TWinControl. Принципиальное отличие этих типов состоит в том, что оконные элементы имеют идентификатор (хендл) окна и могут принять фокус ввода, а графические – нет.

  6. Оконные компоненты так же разбиваются на две категории. Первая категория это – прямые потомки TWinControl, являющиеся оболочками вокруг элементов управления, реализованных в Windows и, следовательно уже знающие как себя рисовать. Примером такого компонента является редактор TEdit;

  7. Для компонентов же, которые нуждаются в идентификаторе окна, но не имеет реализации в Windows, имеется класс TCustomControl, обеспечивающий возможность пользовательского рисования с использованием свойства Canvas подобно TGraphicControl.

  8. Прямые потомки TComponent представляют собой невизуальные компоненты, такие, как, например, TOpenDialog, TTimer.

  9. В VCL существует несколько вспомогательных классов, не являющихся компонентами (поскольку они не происходят от TComponent), но которые, тем не менее очень полезны для создания компонентов. Некоторые из них потоковые, такие как TStrings и TFont , могут быть использованы как типы опубликованных свойств и сохранены в файле формы.

  10. Непотоковые классы, такие, например, как TList и TIniFile используются в реализации алгоритмов обработки данных внутри методов.

Рассмотрим базовые классы подробней с точки зрения разработчика компонентов.

1.1. Базовый класс компонентов TComponent

Класс TComponent обеспечивает свойства и методы, которые обеспечивают управление компонентами в дизайнере форм. Помимо свойств Name (Имя) и Tag (Дескрипотор), класса TComponent вводит еще несколько свойств, полезных для создания компонентов. Одним из наиболее полезных и, как следствие, часто используемых является свойство ComponentState, представляющее собой множество, определяющее состояние компонента.

Наиболее часто используемые элементы множества ComponentState приведены в Таблица 4.1.

Таблица 4.1. Часто используемые элементы множества ComponentState
Элемент Описание состояния компонента
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.

1.2. Базовый класс визуальных компонентов TControl

Класс TControl вводит ряд важных свойств, необходимых для визуальных компонентов. Это, прежде всего, свойство Parent, определяющее компонент, на поверхности которого находится текущий объект. По аналогии с Owner, ответственным за разрушение объекта, Parent в ответе за передачу сообщения на прорисовку объекта.

Второе представляющее интерес свойство – это свойство ControlStyle. Наиболее важные элементы множества ControlStyle приведены в таблице. Обычно множество ControlStyle изменяют в конструкторе Create для изменения поведения компонента.

Таблица 4.2. Элементы множества ControlStyle
Элемент Поведение элемента управления
csAcceptsControls Становится родителем для всех элементов управления, помещенных на него во время проектирования
csCaptureMouse Перехватывает события мыши
csDesignInteractive Во время проектирования щелчки правой кнопки мыши преобразуются в щелчки левой кнопки
csClickEvents Генерируется событие OnClick.
csFramed Элемент управления имеет трехмерную рамку.
csSetCaption Свойство Caption устанавливается таким же, как и Name, если в Caption не содержится какого-либо оригинального значения, установленного явно.
csOpaque Заполняет всю клиентскую область.
csDoubleClicks Принимает и передает двойные щелчки мышью.
csFixedWidth Масштабирование не влияет на ширину.
csFixedHeight Масштабирование не влияет на высоту.
csNoDesignVisible Не видим во время проектирования.
csReplicatable Может быть отрисован методом PaintTo на другом Canvas
csNoStdEvents Игнорирует события мыши и клавиатуры. Ускоряет работу приложения, если нет необходимости в обработке этих событий.

Кроме того, в классе TControl объявлено множество методов диспетчеризации событий мыши.

1.3. Базовый класс графических элементов управления TGraphicControl

Класс TGraphicControl является основой для создания графических элементов управления, не имеющего собственного идентификатора окна, но способного реагировать на события мыши. Пользовательская отрисовка происходит на свойстве Canvas в виртуальном методе Paint, переопределяемом в классе-наследнике.

1.4. Базовый класс оконных элементов управления TWinControl

Класс 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, заставляющее установить небольшой отступ в прокрутке и включается видимость полосы прокрутки.

В результате элемент управления получает горизонтальную полосу прокрутки.

1.5. Базовый класс комбинированных элементов управления TCustomControl

В ряде случаев требуется элемент управления, обладающий идентификатором окна и в то же время позволяющий использовать Canvas для пользовательской отрисовки компонента. К счастью такой класс существует и называется он TCustomControl. Он является удачной комбинацией оконного класса TWinControl и графического элемента управления TGraphicControl.