Главная » Delphi » Структура компонентов

0

Как  уже  было   сказано   ранее,  компоненты  представляют собой   классы   Object Pascal,  инкапсулирующие функции и поведение элементов, добавляемых разработчи ком в приложение для придания ему необходимого поведения и свойств. Все компо ненты имеют определенную структуру, которая обсуждается далее в этой главе.НА ЗАМЕТКУ

Класс (class) и компонент (component) — это не одно и то же! Компонент — это визу- альное представление класса для использования в интегрированной среде разработки Delphi, а собственно класс — это структура Object Pascal, подробно описанная в главе

2, “Язык программирования Object Pascal”.

Свойства

1

 

Более  подробная информация о свойствах приведена в главе 2, “Язык программи рования Object Pascal”. Свойства предоставляют пользователю интерфейс к внутрен ним  полям  компонентов: используя свойства, пользователь компонента способен считывать и модифицировать значения, хранимые полем.  Как правило, пользователь не имеет  прямого доступа к этим полям,  поскольку  в разделе определения класса ком понента они объявлены как private (закрытые).

Свойства: доступ к полям компонента

Свойства обеспечивают доступ к данным, хранящимся в полях  компонента, либо непосредственно, либо  с помощью специальных методов доступа (access  method или accessor). Рассмотрим следующее определение свойства:

TCustomEdit = class(TWinControl)

private

FMaxLength: Integer;

protected

procedure SetMaxLength(Value: Integer);

published

property MaxLength: Integer read FMaxLength

write SetMaxLength default 0;

… end;

Свойство MaxLength предоставляет доступ к содержимому поля  FMaxLength. Оп ределение свойства состоит из его имени, типа,  объявлений read и write и необяза тельного значения default. Ключевое слово  read определяет метод,  позволяющий получить значение поля. Свойство MaxLength возвращает значение поля FMaxLength непосредственно. Ключевое слово  write определяет метод,  позволяющий присвоить полю  необходимое значение. В данном  случае  в свойстве MaxLength для  этого  ис пользуется  метод  SetMaxLength(). Конечно,  свойство может   содержать  и  метод чтения значения поля. В таком  случае свойство MaxLength могло бы быть объявлено следующим образом:

property MaxLength: Integer read GetMaxLength

write SetMaxLength default 0;

Метод чтения значения поля GetMaxLength() можно было бы объявить так:

1 Здесь и далее под пользователем авторы подразумевают не только человека, но и процедуру, функцию или участок кода, использующий компонент или объект. — Прим. ред.function GetMaxLength: Integer;

Методы доступа к свойствам

Методы  доступа обладают  одним  параметром того  же типа,  что и у самого  свойст ва. Метод  доступа  для записи (write) присваивает значение своего  параметра соот ветствующему  полю объекта. Промежуточный уровень  в виде метода  для присвоения значений полю предназначен как для защиты поля от недопустимых данных, так и для реализации, при необходимости, различных побочных эффектов. Например, рас смотрим реализацию метода SetMaxLength():

procedure TCustomEdit.SetMaxLength(Value: Integer);

begin

if FMaxLength <> Value then begin

FMaxLength := Value;

if HandleAllocated then SendMessage(Handle,

EM_LIMITTEXT, Value, 0);

end;

end;

Этот  метод  сначала   проверяет,  не  пытается  ли  пользователь присвоить  полю прежнее значение. Если нет,  то внутреннему полю  FMaxLength присваивается необ ходимое значение, а затем  вызывается функция SendMessage() для передачи сооб щения  Windows  EM_LIMITTEXT в окно  элемента TCustomEdit. Это  сообщение опо вещает  о размере текста, вводимого в поле редактирования. Вызов SendMessage() в методе  доступа  write свойства называется побочным эффектом (side  effect)  присвое ния значения свойству.

Побочные эффекты — это любые действия, вызванные присвоением свойству  значе ния.  При  присвоении значения свойству  MaxValue побочный эффект заключается в том, что элементу  управления “поле редактирования” передается максимальный размер вводимого поля. Естественно, побочные эффекты могут быть значительно сложнее.

Основным преимуществом использования методов для доступа  к значениям внут ренних полей  компонента является то, что разработчик может изменять внутреннюю реализацию компонента без необходимости вносить изменения в код, использующий этот компонент.

Метод доступа на чтение способен приводить возвращаемое значение к типу, от

личному от типа реального значения поля, хранящегося в компоненте.

Другая  немаловажная причина использования свойств —  это  возможность их  ре дактирования в процессе разработки. Свойство, объявленное в разделе published (публикуемые) компонента, отображается также  и в окне  инспектора объектов, а пользователь компонента может модифицировать его значение.

Более  подробная информация о свойствах, их создании и методах  доступа  к ним приведена в главах 11, “Разработка компонентов VCL”, и 13, “Разработка компонентов CLX”, для библиотек VCL и CLX соответственно.

Типы свойств

К свойствам применимы стандартные правила, используемые  в отношении типов

Object Pascal. Важным моментом является то, что тип свойства определяет, каким обра зом это свойство будет редактироваться в окне инспектора объектов. Типы  свойств пе речислены в табл. 10.2. Более  подробная информация по этой  теме приведена в инте рактивной справочной системе Delphi  в разделе “Properties”.

Таблица 10.2. Типы свойств

Тип свойства                          Интерпретация инспектором объектов

Простой (simple)     Числовые, символьные и строковые свойства отображаются в окне Object Inspector как числа,  символы и строки соответ ственно. Пользователь может  вводить и редактировать их значения непосредственно

Перечислимый

(enumerated)

Свойства перечислимых типов  (включая Boolean) выводятся в том же виде, что и в исходном коде. Пользователь может циклически перебирать их значения, дважды щелкнув в столб це Value, либо выбирать их в раскрывающемся списке

Множество (set)        Свойства этого  типа  отображаются в окне  инспектора объек тов именно в виде множества. Редактируя его, пользователь рассматривает каждый  элемент как логический: если  элемент присутствует, то он соответствует True, а если нет — то False

Объект (object)        Свойства, которые сами  являются объектами,  иногда  имеют свои собственные редакторы свойств. Впрочем, если  у свойст ва, являющегося объектом, в свою очередь, есть свойства, объявленные как published, окно инспектора объектов по зволяет расширить за их счет список  свойств исходного объек та, а затем редактировать их в обычном порядке. Свойства объекты должны  происходить от класса TPersistent

Массив (array)            Свойства, имеющие тип  массива,  обязаны иметь  собственные редакторы. В окне  Object Inspector нет встроенных возмож ностей для редактирования таких свойств

Методы

Поскольку компоненты — это всего  лишь объекты, у них могут быть  свои  методы. Методы  объектов уже рассматривались в главе 2, “Язык программирования Object Pascal”,  поэтому  не будем останавливаться на них.  Ниже, в разделе “Иерархия визу альных  компонентов”,  находится информация о некоторых основных методах  ком понентов различных уровней иерархии.

События

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

Назначение кода событию во время разработки

Если посмотреть на страницу событий компонента TEdit, то можно  увидеть  со бытия OnChange (при  изменении), OnClick (при  щелчке) и OnDblClick (при  двой ном щелчке). Для разработчика компонента событие — это просто указатель  на соот ветствующий метод.  Когда  пользователи компонентов  назначают событию некото рый  код,  то  тем  самым  они  создают  обработчик события. Например, если  дважды щелкнуть на некотором событии компонента во вкладке  событий окна инспектора объектов, то  интегрированная среда  разработки Delphi  создаст  заготовку  метода,  в которую  можно  будет добавить собственный программный код,  как это  сделано  для события OnClick компонента TButton в следующем примере:

TForm1 = class(TForm) Button1: TButton;

procedure Button1Click(Sender: TObject);

end;

procedure TForm1.Button1Click(Sender: TObject);

begin

{ Здесь находится код обработчика события }

end;

Подобный фрагмент кода интегрированная среда разработки Delphi создает само

стоятельно.

Динамическое назначение кода события

Тот факт, что события — это указатели  на методы, наиболее наглядно проявляется при  динамическом назначении обработчика событию. Например,  чтобы  назначить собственный обработчик событию OnClick компонента TButton, следует  вначале объявить и определить метод,  который будет отвечать за это  событие. Такой  метод должен  принадлежать форме, владеющей компонентом TButton, как показано в сле дующем фрагменте кода:

TForm1 = class(TForm) Button1: TButton;

… private

MyOnClickEvent(Sender: TObject); // Объявление метода

end;

{ Определение метода }

procedure TForm1.MyOnClickEvent(Sender: TObject);

begin

{ Собственно код находится здесь }

end;

Определенный пользователем метод  MyOnClickEvent() должен  быть  назначен обработчиком события Button1.OnClick. Следующая  строка показывает, как назна чить   этот   метод   событию  Button1.OnClick динамически  (подобное  назначение обычно выполняется в обработчике события OnCreate формы):

procedure TForm1.FormCreate(Sender: TObject);

beginButton1.OnClick := MyOnClickEvent;

end;

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

Button1.OnClick := nil;

Динамическое назначение обработчика мало  отличается от назначения обработ чика события в окне  инспектора объектов, за исключением того  факта, что в послед нем случае Delphi  создает объявление метода. Нельзя назначить обработчику события абсолютно произвольный метод.  Поскольку свойства событий — это  указатели  на оп ределенные методы, они  обладают  специфическими признаками, зависящими от ти па события. Например, типом  метода  для обработки события OnMouseDown является TMouseEvent, определенный как:

TMouseEvent = procedure (Sender: TObject; Button: TMouseButton; Shift: TShiftState;

X, Y: Integer) of object;

Следовательно, методы, назначаемые  обработчику события,  должны   удовлетво рять  некоторым условиям, зависящим от типа  события. Обработчики должны  иметь определенное количество параметров заданных типов  (с определенным порядком их следования).

Как  уже отмечалось, события являются свойствами. Подобно свойствам данных, события ссылаются на  закрытые поля  данных  компонента. Эти  поля  всегда  имеют процедурный тип,  похожий на TMouseEvent. Обратите внимание на следующий фрагмент кода:

TControl = class(TComponent)

private

FOnMouseDown: TMouseEvent;

protected

property OnMouseDown: TMouseEvent read FOnMouseDown

write FOnMouseDown;

public end;

Вспомните: свойства обеспечивают доступ к полям  данных  компонента. Как видите, событие, будучи свойством, так же предоставляет доступ к закрытому (private) полю процедурного типа.

Более  подробная информация о создании событий и их обработчиках приведена в главах 11, “Разработка компонентов VCL”, и 13, “Разработка компонентов CLX”.

Работа с потоками данных

Важной  характеристикой компонентов является их способность работать с пото ками  (streaming), которая позволяет хранить компонент и значения относящихся к нему свойств в файле. При  этом Delphi берет  работу с потоками данных  на себя, одна ко разработчикам компонентов иногда  могут понадобиться возможности работы с потоками данных, выходящие за рамки  автоматически предоставляемых Delphi. Фак тически создаваемый Delphi  файл  .DFM —  не более  чем  файл  ресурсов, содержащийинформацию о потоках данных  в форме и ее компонентах в виде  ресурса  RCDATA. Механизм  работы  с  потоками  данных   в  Delphi  подробно  изложен  в  главе   12, “Создание расширенного компонента VCL”.

Отношения владения

Компоненты могут владеть  другими компонентами. Владелец (owner) компонента оп ределяется его свойством Owner. При  закрытии компонента владельца принадлежащие ему компоненты также  закрываются, а занимаемая ими память  освобождаются. В каче стве примера можно  привести форму,  которая обычно владеет  всеми расположенными на ней  компонентами. При  помещении некоторого компонента в форму  в окне  конст руктора форм  она автоматически становится владельцем этого  компонента. Если ком понент создается динамически, то в его конструктор (метод  Create) потребуется пере дать параметр, указывающий владельца компонента. Это  значение и будет присвоено свойству  Owner вновь  созданного компонента. В следующей  строке кода показано, как передать неявную  переменную self в конструктор TButton.Create(), в результате чего форма становится владельцем создаваемого компонента:

MyButton := TButton.Create(self);

Когда форма закрывается, экземпляр класса  TButton, на который ссылается ком понент MyButton, также  освобождается. Это  один  из  краеугольных камней  фунда мента  VCL —  форма определяет компоненты, которые подлежат уничтожению при закрытии, исходя из свойства Components, представляющего собой массив.

Можно  также  создать  “ничейный” компонент, без владельца, передав в метод ком понента Create() параметр nil. Но  в этом  случае о последующем удалении  такого компонента придется позаботится самостоятельно. Этот  подход  можно  проиллюст рировать следующим кодом:

MyTable := TTable.Create(nil)

try

{ Здесь выполняются действия с компонентом MyTable }

finally

MyTable.Free;

end;

Здесь можно  использовать конструкцию try..finally, обеспечивающую осво бождение ресурсов при возникновении исключения. Однако  желательно избегать ис пользования описанной технологией “ничейных” компонентов, за исключением тех редких  случаев, когда компоненту просто невозможно назначить владельца.

Другим  свойством, связанным с владением, является свойство Components. Это свойство представляет собой  массив,  содержащий список  всех компонентов, принад лежащих  данному  компоненту владельцу.  Приведенный ниже  код выводит информа цию о классах всех компонентов, перечисленных в свойстве Components:

var

i: integer;

begin

for i := 0 to ComponentCount – 1 do

ShowMessage(Components[i].ClassName);

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

Отношения наследования

Не путайте  понятия владелец  и родитель (parent) компонента — это совершенно раз ные отношения. Некоторые компоненты могут быть родительскими для других компо нентов. Обычно родительскими компонентами являются оконные компоненты, напри мер потомки класса TWinControl. Родительские компоненты отвечают за функциони рование методов отображения дочерних компонентов, а также  за корректность этого отображения. Родительский компонент задается значением свойства Parent.

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

чие разных родителя и владельца — совершенно нормальная ситуация для компонента.

Источник: Тейксейра, Стив, Пачеко, Ксавье.   Borland Delphi 6. Руководство разработчика. : Пер.  с англ. — М. : Издательский дом “Вильямс”, 2002. —  1120 с. : ил. — Парал. тит. англ.

По теме:

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