Команды Default и NoDefault
Итак, мы уже умеем создавать свойства произвольного типа для собственного компонента. Осталось заметить, что многим свойствам можно присвоить конкретное значение, которое будет установлено по умолчанию. Для этого достаточно присвоить это значение полю компонента, например:
FMyProperty := 10;
В результате чего, при каждом добавлении компонента на форму свойство Myproperty будет принимать значение 10.
Команды Default и NoDefault применяются для ускорения процесса загрузки формы при работе приложения. Например,
property MyCount: Integer read FMyCount write FmyCount Default 0;
Данный код не присваивает значение о свойству MyCount. При выполнении вышеприведенного кода команда Default о означает следующее: если при сохранении формы, содержащей компонент, значение свойства MyCount не будет равно нулю, то новое значение сохранится в файле формы, иначе значение данного свойства не будет сохранено.
Примечание
Рекомендуется использовать команду Default во всех случаях, когда это возможно, если вы хотите создать быстро работающее приложение.
Команда NoDefault предназначена для нейтрализации команды Default. Команда применяется для отмены команды Default компонентов-предков. Пример использования команды NoDefault:
TSecondComponent = class (TMyButton)
published
property MyCount NoDefault 0;
Пример создания нового события компонента
Попробуем теперь создать собственное событие. Для этого нужно сначала убедиться, что такого события нет в VCL Delphi. Предположим, возникла необходимость создания события, которое возникает каждые 30 секунд. Естественно, для этого случая можно воспользоваться компонентом Timer, который расположен на вкладке System палитры компонентов Delphi. Но, предположим, что наш компонент должен иметь такое событие для удобства работы с ним. Код для создания события представлен на листинге 2.20.
Листинг 2.20
unit halfmin; interface
uses
Windows, Messages, SysUtils, Classes,Graphics, Controls, Forms,
Dialogs,
ExtCtrls;
type
TTimeEvent = procedure (Sender: TObj'ect; TheTime: TDateTime) of object;
THalfMinute = class (TComponent)
private
FTimer: TTimer;
FOnHalfMinute: TTimeEvent;
FOldSecond, FSecond: Word;
procedure FTimerTimer (Sender: TObject);
protected
procedure DoHalfMinute (TheTime: TDateTime); dynamic;
public
constructor Create (AOwner: TComponent); override;
destructor Destroy; override;
published
property OnHalfMinute: TTimeEvent read FOnHalfMinute write FOnHalf-Minute;
end;
implementation
constructor THalfMinute.Create (AOwner: TComponent);
begin
inherited Create (AOwner);
if not (csDesigning in ComponentState) then begin
FTimer:=TTimer.Create(self);
FTimer.Enabled:=True;
FTimer.Interval:=500;
FTimer.OnTimer:=FTimerTimer;
end;
end;
destructor THalfMinute.Destroy;
begin
FTimer.Free;
inherited Destroy;
end;
procedure THalfMinute.FTimerTimer (Sender: TObject);
var
DT: TDateTime;
Temp: Word;
begin
DT:=Now;
FOldSecond:=FSecond;
DecodeTime (DT,Temp,Temp,FSecond,Temp);
if FSecond <> FOldSecond then
if ((FSecond=30) or (FSecond=0)) then
DoHalfMinute (DT);
end;
procedure THalfMinute.DoHalfMinute(TheTime: TDateTime);
begin
if Assigned (FOnHalfMinute) then
FOnHalfMinute (Self, TheTime);
end;
end.
Для проверки работоспособности вышеприведенного кода вы можете добавить еще одну процедуру для регистрации нового компонента с именем ThalfMinute, предварительно добавив в interface-часть программы строку:
procedure Register;
Ниже представлен код для регистрации компонента:
procedure Register;
begin
RegisterComponents('Samples', [THalfMinute]);
end;
Для просмотра работоспособности нового компонента после его регистрации создадим новую форму и разместим на ней новый компонент. Добавим на форму компонент TEdit. Затем добавим обработчик события onHaifMinute для формы (листинг 2.21).
Листинг 2.21
procedure TForml.HalfMinutelHalfMinute(Sender: TObject; TheTime: TDateTime);
begin
Editl.Text: = ('Время '+TimeToStr(TheTime)) ; Editl. Refresh;
end;
В результате работы данной программы в компоненте Editl будет выводиться текущее время каждые 30 секунд.
Регистрация компонента в среде Delphi
Регистрация компонента необходима для размещения компонента в палитре компонентов.
При использовании эксперта компонентов для создания нового компонента Delphi самостоятельно создает процедуру регистрации компонента в модуле-заготовке. Создателю компонента в данном случае ничего не нужно более делать, кроме выполнения следующих шагов:
1. Выбрать пункт меню Options/Install Components (Настройки/Установка компонентов).
2. Нажать кнопку Add.
3. Указать имя подключаемого модуля (естественно, предварительно нужно сохранить модуль компонента).
После компиляции на выбранной вкладке палитры компонентов появится новый компонент.
Если же вы создаете компонент без использования эксперта компонентов, вам придется самостоятельно дописывать процедуру регистрации компонента. В разделе interface модуля компонента нужно дописать строку:
procedure Register;
А в разделе implementation добавить процедуру регистрации, например:
procedure Register;
begin
RegisterComponent ('Samples', [TMyComponent]);
end;
В результате, компонент с именем TMyComponent будет размещен на вкладке samples палитры компонентов.
Обратите внимание на значок, которым обозначается новый компонент. Его можно поменять на любой другой. Для создания собственного значка разумно воспользоваться стандартной программой Image Editor, которая входит в комплект поставки Delphi. Можно использовать и любой другой редактор растровых изображений.
Создайте значок для вашего компонента размером 24x24 пиксела. Данное изображение сохраните в файле формата DCR. Имя файла - это имя вашего компонента, в котором все буквы - заглавные. Например, для компонента TMyButton имя файла картинки будет TMYBUTTON.DCR. Затем поместите файл картинки в ту папку, в которой находится файл с модулем компонента. Перекомпилируйте модуль, и ваш компонент будет изображаться в палитре компонентов вашим рисунком.
Создание методов компонента
Добавление методов в новый компонент - операция несложная. Однако нужно обратить внимание на некоторые особенности, которые в дальнейшем облегчат взаимодействие пользователя с вашим компонентом.
Во-первых, необходимо, чтобы методы не были взаимозависимыми, т. е. каждый метод должен быть самостоятельным и законченным. Во-вторых, метод не должен блокировать компонент. И, в-третьих, метод должен иметь имя, соответствующее выполняемым действиям.
Как вы уже знаете, методы объявляются в секциях private, public и protected. При создании нового метода важно учитывать, как он будет использоваться в дальнейшем, сможет ли данный компонент быть предком для других компонентов, и, в зависимости от этого, разместить методы в нужных секциях. Табл. 2.10 поможет вам при выборе секций для методов компонента.
Таблица 2.10. Размещение методов компонента в различных секциях
Секция |
Размещаемые методы |
||
Private |
В данной секции лучше всего размещать те методы, которые не могут изменяться в компонентах-потомках. Эти методы не доступны вне данного компонента |
||
Protected |
В этой секции размещают методы, которые будут доступны для изменения в компонентах-потомках |
||
Public |
Данная секция предназначена для размещения методов, которые доступны любому пользователю компонента. Доступ полный во время работы приложения, но не во время разработки, т. е. данные методы недоступны в окне инспектора объектов |
||
Published |
В этой секции размещаются свойства компонента, которые доступны во время разработки приложения в окне инспектора объектов |
||
Создание перечисляемых свойств компонента
К свойствам перечисляемого типа относятся такие свойства компонента, которые при их редактировании в окне инспектора объектов вызывают выпадающий список, содержащий возможные значения данного свойства. К числу подобных свойств относятся Align, Borderstyle, color и др. Для того чтобы самостоятельно добавить в новый компонент перечисляемое свойство, необходимо сначала определить новый перечисляемый тип, например:
TMyEnumType = (eFirst, eSecond, eThird);
После этого нужно определить поле компонента, которое будет хранить значение данного перечисляемого типа, затем определить свойство. Пример добавления перечисляемого типа в новый компонент TMyButton приведен на листинге 2.9.
Листинг 2.9
type
TMyEnumType = (eFirst, eSecond, eThird);
type
TMyButton = class(TButton)
private
{ Private declarations }
FMyEnum: TMyEnumType;
protected
{ Protected declarations }
public
{ Public declarations }
published
{ Published declarations }
property MyEnumProp: TMyEnumType read FMyEnum write FMyEnum;
end;
Таким образом, в окне инспектора объектов, при изменении свойства MyEnumProp, будет выдан выпадающий СПИСОК, Содержащий Три пункта: eFirst, eSecond И eThird (рис. 2.15).
Рис. 2.15. Перечисляемое свойство MyEnumProp в новом компоненте TMyButton
Создание собственных редакторов свойств
Перед тем как создавать собственный редактор свойств компонента, рассмотрим сначала имеющиеся в Delphi редакторы свойств. Их достаточно легко видеть в окне инспектора объектов, когда вы пытаетесь изменить какое-либо свойство компонента. Например, когда вы изменяете свойство Color для того или иного компонента, вы видите редактор свойства цвета. Важно заметить, что все свойства компонентов имеют свои редакторы, даже такие простейшие свойства, как Caption, Height, Enabled. Особенностью этих редакторов является то, что компоненты "не знают", какие редакторы вы используете для изменения их свойств. Это может навести на мысль взять собственный редактор свойств вместо предопределенного. Например, можно написать редактор свойства width, который будет ограничен каким-либо числом.
Редакторы свойств имеют свою иерархию. Рассмотрим ее.
Базовым классом в иерархии редакторов свойств является TPropertyEditor. Названия классов говорят сами за себя. Например, класс TColorProperty отвечает за свойство цвета компонента, класс TlntegerProperty связан с теми свойствами, которые имеют тип integer и т. д. На листинге 2.15 приведен код, определяющий базовый класс TPropertyEditor.
Листинг 2.15
TPropertyEditor = class
private
FDesigner: TFormDesigner;
FPropList: PInstPropList;
FPropCount: Integer;
constructor Create(ADesigner: TFormDesigner; APropCount: Integer);
function GetPrivateDirectory: string;
procedure SetPropEntry(Index: Integer; AInstance: TComponent;
APropInfo: PPropInfo};
protected
function GetPropInfо: PPropInfo;
function GetFloatValue: Extended;
function GetFloatValueAt(Index: Integer): Extended;
function GetMethodValue: TMethod;
function GetMethodValueAt(Index: Integer): TMethod;
function GetOrdValue: Longint;
function GetOrdValueAt(Index: Integer): Longint;
function GetStrValue: string;
function GetStrValueAt(Index: Integer): string;
procedure Modified;
procedure SetFloatValue(Value: Extended);
procedure SetMethodValue(const Value: TMethod);
procedure SetOrdValue(Value: Longint);
procedure SetStrValue(const Value: string);
public
destructor Destroy; override;
procedure Activate; virtual;
function AllEqual: Boolean; virtual;
procedure Edit; virtual;
function GetAttributes: TPropertyAttributes; virtual;
function GetComponent(Index: Integer): TComponent;
function GetEditLimit: Integer; virtual;
function GetName: string; virtual;
procedure GetProperties(Proc: TGetPropEditProc);virtual;
function GetPropType: PTypelnfo;
function GetValue: string; virtual;
procedure GetValues(Proc: TGetStrProc); virtual;
procedure Initialize; virtual;
procedure SetValue(const Value: string); virtual;
property Designer: TFormDesigner read FDesigner;
property PrivateDirectory: string read GetPrivateDirectory;
property PropCount: Integer read FPropCount;
property Value: string read GetValue write SetValue;
end;
Методы данного класса, которые приведены ниже, можно переопределять для изменения поведения редактора свойств.
Метод Activate вызывается, когда данное свойство выбирается в окне инспектора объектов.
Метод AllEqual вызывается при выборе на форме более одного компонента.
Метод Edit вызывается при нажатии кнопки (...) или при двойном щелчке мыши на свойстве. Данный метод может вызвать диалоговое окно для редактирования свойства, например, как это происходит при редактировании свойства Font.
Метод GetAttributes возвращает множество значений типа TProperyAttributes, определяющих, каким образом свойство будет отображаться в окне инспектора объектов.
Метод Getcomponent предназначен для определения компонента по его номеру (параметр index), в случае, когда на форме выбрано несколько компонентов одновременно.
Метод GetEditLimit возвращает максимальное число символов в строке, которые пользователь может ввести при редактировании свойства.
Метод GetName предназначен для получения имени свойства. Данный метод целесообразно изменять только в том случае, когда имя свойства отличается от имени, отображаемом в окне инспектора объектов.
Метод GetPropType применяется для определения указателя на информацию о типе редактируемого свойства.
Метод Getvalue возвращает значение свойства в виде строки.
Метод initialize вызывается при создании (инициализации) редактора свойств.
Метод setvalue применяется для установки значения свойства.
В большинстве случаев, при создании нового редактора свойств нет необходимости использовать в качестве класса-предка базовый класс TPropertyEditor. Часто разработчик просто переделывает уже существующий для данного свойства редактор, переопределяя некоторые его методы.
Рассмотрим в качестве примера переработанное свойство Hint, которое применяется для показа всплывающей подсказки при задержании указателя мыши над компонентом. В стандартном случае такая подсказка имеет всего одну строку. Попробуем сделать свойство Hint многострочным. Нижеприведенный листинг 2.16 показывает, как создать новый редактор свойств THintProperty. В качестве класса-предка для данного редактора свойств выберем редактор TStringProperty.
Листинг 2.16
THintProperty = class(TStringProperty)
public
function GetAttributes: TPropertyAttributes; override;
function GetValue : String; override;
procedure Edit; override;
end;
function THintProperty.GetAttributes: TPropertyAttributes;
begin
Result := inherited GetAttributes + [paDialog, paReadOnly];
end;
function THintProperty.GetValue : string;
var i : Byte;
begin
result:=inherited GetValue;
for i:=l to Byte(resultt0]) do
if result[i]<#32 then result[i]: = '>';
end;
procedure THintProperty.Edit; var
HintEditDlg : TStrEditDlg; s : string;
begin
HintEditDlg:=TStrEditDlg.Create(Application);
with HintEditDlg do
try
Memo.MaxLength := 254;
s:=GetStrValue+#0;
Memo.Lines.SetText(@s[1]);
UpdateStatus(nil);
ActiveControl := Memo;
If ShowModal = mrOk then begin
s:=StrPas(Memo.Lines.GetText);
if s[0]>#2 then Dec(Byte(s[0]),2);
SetStrValue(s);
end; finally
Free;
end;
end;
Рассмотрим методы нового класса:
- функция GetAttributes добавляет к унаследованному множеству атрибуты paDialog (при этом появляется кнопка (...) в окне инспектора объектов) и paReadOnly (который применяется для того, чтобы редактирование данного свойства было возможно только через диалог);
- функция Getvalue заменяет символы перевода каретки (#10) -и переход на новую строку (#13) на символ больше (>).
Наконец, процедура Edit применяется для вызова диалога для ввода строк всплывающей подсказки.
Для регистрации нового редактора нужно в интерфейсной части модуля поместить объявление процедуры Register. После чего В части implementation модуля написать саму процедуру регистрации (листинг 2.17).
Листинг 2.17.
procedure Register;
begin
RegisterPropertyEditor (Typelnfо(String), TControl, 'Hint', THintProperty);
end;
Данная процедура позволяет привязать один и тот же редактор к свойствам, в зависимости от их названия или типа. Это определяется параметрами, которые передаются процедуре RegisterPropertyEditor. Первый параметр определяет тип свойства (в нашем примере - это string), второй параметр - класс компонента. Третий параметр позволяет задать имя свойства. Четвертый параметр указывает имя редактора свойства.
Для того чтобы установить новый редактор свойств в Delphi, нужно выполнить следующие шаги:
1. Выбрать пункт меню Options/Install Components (Настройки/Установка компонентов).
2. Нажать кнопку Add.
3. Указать имя подключаемого модуля.
После того как произойдет компиляция библиотеки, можно создать новую форму и разместить на ней какой-либо компонент. После чего установим у этого компонента свойство showHint в true и нажмем кнопку (...) в свойстве Hint. Вы видите на экране новый многострочный редактор для свойства Hint.
Создание событий компонента
Стандартные события мы с вами уже рассматривали в предыдущих главах. В настоящий момент нам предстоит дать четкое определение событию, а также обработчику события.
Итак, событие - это любое действие, произошедшее благодаря операционной системе, действиям пользователя, работе программы.
Событие можно "перехватить" и обработать с помощью программы-обработчика события. Связь между событием и программой-обработчиком называется свойством-событием. Таким образом, когда происходит какое-либо событие компонента, он может обработать данное событие. Для этого сначала происходит проверка наличия кода обработки события. Если подобный код есть - он выполняется.
Рассмотрим в качестве примера такое часто возникающее событие, как нажатие левой кнопки мыши Onclick. Данное событие, как и многие другие, имеет так называемые методы диспетчеризации событий (event-dispatching methods). Эти методы нужны как раз для того, чтобы определять, создан ли код обработки произошедшего события для данного компонента. Эти методы объявляются как защищенные (protected). Таким образом, для свойства Onciick определен метод диспетчеризации события click (листинг 2.18).
Листинг 2.18
TControl = class (TComponent)
private
FOnClick: TNotifyEvent;
protected
procedure Click; dynamic;
property OnClick: TNotifyEvent read FOnClick write FOnClick;
end;
implementation
procedure TControl.Click;
begin
if Assigned (FOnClick) then FOnClick (Self);
end;
Листинг 2.19
procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
Canvas.TextOut(X, Y, '('+IntToStr(X)+', '+IntToStr(Y)+')');
end;
Рис. 2.18. Результат обработки события OnMouseDown
Создание свойств компонента
Добавление новых свойств в компонент осуществляется очень просто. Достаточно задать поля и свойства, определив при этом их тип и доступ (чтение, запись). Пример простого задания свойств в новом компоненте представлен на листинге 2.8.
Листинг 2.8
TMyButton = class(TButton)
private
{ Private declarations }
FMyCount: Integer;
FStirngOfText: String;
protected
{ Protected declarations }
public
{ Public declarations }
published
{ Published declarations }
property MyCount: Integer read FMyCount write FMyCount;
property StringOfText: String read FStringOfText write FStringOfText;
end;
На приведенном выше листинге мы задаем два новых поля для компонента TMyButton и определяем два новых свойства компонента: одно типа integer, другое string. Для простоты, значения данных свойств считываются и записываются в одноименные поля. Задание этих свойств будет гарантировать доступ к ним в окне инспектора объектов (благодаря описанию свойств в разделе published) и не потребует написания дополнительных методов для доступа к свойствам.
Примечание
По взаимной договоренности принято названия полей начинать с буквы F (поле, field), а названия компонентов и любых объектов с буквы т (тип, type).
Создание свойств-множеств в компоненте
Тип множества часто фигурировал в Object Pascal, и некоторые свойства компонентов Delphi имеют данный тип. Когда вы используете свойство типа set, вы должны учитывать, что каждый элемент множества будет являться отдельным свойством, имеющим логический тип в инспекторе объектов.
Для создания свойства-множества сначала зададим нужный тип:
TMySetTypeFirst = (poFirst, poSecond, poThird); TMySetType = set of TMySetTypeFirst;
Первая строка задает перечисляемый тип TMySetTypeFirst, который определяет диапазон множества, а вторая строка - само множество TMySetType.
Пример добавления свойства-множества в компонент TMyButton приведен на листинге 2.10.
Листинг 2.10.
type
TMyEnumType = (eFirst, eSecond, eThird); TMySetTypeFirst = (poFirst, poSecond, poThird); TMySetType = set of TMySetTypeFirst;
type
TMyButton = class(TButton)
private
{ Private declarations } FMyEnum: TMyEnumType; FMyOptions: TMySetType;
protected
{ Protected declarations }
public
{ Public declarations }
published
{ Published declarations }
property MyEnumProp: TMyEnumType read FMyEnum write FMyEnum;
property MyOptions: TMySetType read FMyOptions write FMyOptions;
end;
Для удобства, мы не стали исключать определение перечисляемого свойства в компоненте TMyButton.
В результате, в окне инспектора объектов свойство-множество будет отображаться, как представлено на рис. 2.16.
Рис. 2.16. Свойство-множество MyOptions в новом компоненте TMyButton
Создание свойства-массива в компоненте
Свойства компонента могут быть практически любого типа, которые поддерживает язык Object Pascal. Некоторые свойства могут быть массивами. Яркими примерами свойств такого типа являются тмето. Lines, TDBGrid. columns и др. Подобные свойства требуют собственных редакторов. Мы пока остановимся на создании простого свойства, которое представляет из себя массив (о создании собственных редакторов свойств читайте далее в этой главе). Создадим новый компонент Tweek, содержащий два свойства: Month и Number. Свойство Month будет представлять собой массив, возвращающий название месяца по переданному целому числу от 1 до 12. Свойство Number - тоже массив, который возвращает число, соответствующее названию месяца.
Итак, на листинге 2.14 приведен код компонента TWeek.
Листинг 2.14
unit Week; interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;
type
TWeek = class(TComponent)
private
{ Private declarations }
function GetMonthName (const AIndex: Integer): String;
function GetMonthNumber (const AMonthNarae: String): Integer;
protected
{ Protected declarations }
public
{ Public declarations }
property MonthName[const AIndex: Integer]: String read GetMonthName; default;
property MonthNumber[const AMonthName: String]: Integer read GetMonthNumber;
published
{ Published declarations }
end;
procedure Register;
implementation
const
MonthNames: array[1..12] of String[8]= ('Январь','Февраль','Март','Апрель','Май', 'Июнь','Июль','Август','Сентябрь', 'Октябрь', 'Ноябрь','Декабрь');
function TWeek.GetMonthName(const AIndex: Integer): String;
begin
if (AIndex<=0) or (AIndex>12) then
raise Exception.Create('Номер месяца должен быть числом от 1 до 12')
else Result:- MonthNames[AIndex];
end;
function TWeek.GetMonthNumber(const AMonthName: String): Integer;
var i:integer;
begin
Result:=0;
for i:=l to 12 do begin
if Uppercase(AMonthName)=UpperCase(MonthNames[i]) then
Result:=1;
end;
end;
procedure Register;
begin
RegisterComponents('Samples', [TWeek]);
end;
end.
Рассмотрим вышеприведенный код более подробно. Как вы можете видеть, свойства типа Array объявляются вместе с индексами. Для свойства MontnName мы определили индекс Aindex, а для свойства MonthNumber - индекс AMonthName. Для доступа к свойствам такого типа необходимо использовать методы. Внутренних полей здесь нет. Если свойство типа Array многомерно, то свойство-массив объявляется сразу с несколькими индексами. При вызове методов доступа к ним нужно передавать параметры в том же порядке, в каком вы их указали в свойстве.
Функция GetMonthName возвращает строку, содержащую имя месяца, соответствующего целому числу от 1 до 12, переданному в качестве параметра данной функции. В случае передачи функции числа, не принадлежащему данному диапазону, будет сгенерировано исключение командой raise (об исключениях читайте главу 2).
Наконец, функция GetMonthNumber возвращает число от 1 до 12, которое соответствует названию месяца, переданного в качестве параметра в данную функцию. В случае если ни один месяц не соответствует названию, определенному массивом MonthNames, результатом выполнения данной функции будет ноль.
Таким образом, если поместить на форму экземпляр компонента TWeek с именем weeki, при выполнении строки ShowMessage (Weekl.MonthName[5]}; будет выдано окно с сообщением Май.
Создание свойства-объекта в компоненте
Каждый компонент может содержать в себе свойство-объект. В качестве свойства-объекта может выступать любой компонент или объект Delphi. Кроме того, свойствами-объектами нового компонента могут быть новые компоненты или объекты, которые вы создали самостоятельно. Важным условием является тот факт, что свойства-объекты должны быть потомками класса TPersistent. Это необходимо для того, чтобы свойства объекта-свойства отображались в окне инспектора объектов. Приведем пример создания свойства-объекта в нашем компоненте TMyButton.
Для начала создадим произвольный новый объект, являющийся прямым потомком класса TPersistent (листинг 2.11).
Листинг 2.11
type
TMyObject = class (TPersistent}
private
{ Private declarations }
FPropertyl: Real;
FProperty2: Char;
protected
{ Protected declarations }
public
{ Public declarations }
procedure Assign (Source: TPersistent);
published
{ Published declarations }
property Propertyl: Real read FPropertyl write FPropertyl;
property Property2: Char read FProperty2 write FProperty2;
end;
В качестве предка нового класса может выступать не только класс TPersistent, но любой его потомок. В вышеприведенном листинге мы создаем новый класс TMyObject, в котором присутствует два простых свойства - Propertyl и Property2. Кроме того, в новый объект включена процедура Assign. Данная процедура необходима для обеспечения правильного доступа к свойству нашего будущего компонента TMyButton. Ниже приведен листинг 2.12, в котором мы добавляем в компонент TMyButton новое свойство-объект.
Листинг 2.12
type
TMyButton = class (TButton)
private
{ Private declarations }
FMyObject: TMyObject;
procedure SetMyObject (Value: TMyObject);
protected
{ Protected declarations }
public
{ Public declarations }
constructor Create (AOwner: TComponent); override;
destructor Destroy; override;
published
{ Published declarations }
property MyObject: TMyObject read FMyObject write SetMyObject;
end;
Как вы можете видеть, мы добавляем в код нового компонента конструктор и деструктор объекта.
Теперь осталось дописать конструктор и деструктор для компонента TMyButton, в которых необходимо соответственно создать и уничтожить, кроме компонента TMyButton, еще и объект TMyObject. А также нужно написать код метода setMyObject, который будет помещать новое значение в свойства объекта TMyObject. Ну и, конечно, написать код метода Assign для объекта TMyObject. Полную версию кода представляет листинг 2.13. Здесь, кроме ранее описанного, приводится код ко всем этим методам.
Листинг 2.13
type
TMyObject = class (TPersistent)
private
{ Private declarations }
FPropertyl: Real;
FProperty2: Char;
protected
{ Protected declarations }
public
{ Public declarations }
procedure Assign (Source: TPersistent);
published
{ Published declarations }
property Propertyl: Real read FPropertyl write FPropertyl;
property Property2: Char read FProperty2 write FProperty2;
end;
type
TMyButton = class (TButton)
private
{ Private declarations }
FMyObject: TMyObject;
procedure SetMyObject (Value: TMyObject);
protected
{ Protected declarations }
public
{ Public declarations }
constructor Create (AOwner: TComponent); override;
destructor Destroy; override;
published
{ Published declarations }
property MyObject: TMyObject read FMyObject write SetMyObject;
end;
procedure Register;
implementation
procedure TMyButton.SetMyObject (Value: TMyObject);
begin
if Assigned (Value) then FMyObject.Assign (Value);
end;
constructor TMyButton.Create (AOwner; TComponent);
begin
inherited Create (AOwner);
FMyOb j ect:=TMyObject.Create;
end;
destructor TMybutton.Destroy;
begin
FMyObject.Free;
inherited Destroy;
end;
procedure TMyObject.Assign (Source: TPersistent);
begin
if Source is TMyObject then
begin
FPropertyl:=TmyObject (Source).Property1;
FProperty2:=TmyObject (Source).Property2;
inherited Assign (Source);
end;
end;
Обратите внимание на реализацию метода TMyObject.Assign. Здесь сначала выполняется проверка на то, что передается правильный экземпляр объекта TMyObject. Если он правильный, то значения свойств (Source) Переносятся в поля FPropertyl и FProperty2 объекта TMyButton. Результатом исполнения вышеприведенного кода будет новый компонент TMyButton, содержащий в себе свойство-объект TMyObject. В окне инспектора объектов это будет выглядеть, как представлено на рис. 2.17.
Рис. 2.17. Свойство-объект MyObject в новом компоненте TMyButton