With TMyButton.Create(self) do begin

Parent:=self;

Left:=x;

Top:=y;

Width:=Width+60;

Caption:=Format('%d кнопка x,y= %d,%d',

[GetCount,x,y]);

end;

Класс-функция GetCount может вызываться, в отличие от обычных методов, как из объекта (в обработчикеFormMouseDown), так и из класса. Например, добавим таймер (компонент TTimer) на форму и обеспечим при срабатывании таймера изменение заголовка формы с помощью следующего обработчика события OnTimer:

procedure TForm1.Timer1Timer(Sender: TObject);

Begin

Caption:=Format('Кнопок на форме %d',

[TMyButton.GetCount]);

end;

В результате решения получим, например, следующий вариант (рис. 35).

With TMyButton.Create(self) do begin - student2.ru

Рис. 35 Форма со вторым вариантом расчета примера 14.

ОТСЛЕЖИВАНИЕ РАЗРУШЕНИЯ ОБЪЕКТОВ

Проследим за разрушением объектов на форме. Очевидно, это можно увидеть в секциях initialization и finalization, т.е. после того, как форма начнет разрушаться.

Initialization

MessageBox(0,PChar(Format('На форме %d кнопок',

[TMyButton.GetCount])),'Инициализация',mb_ok);

Finalization

MessageBox(0,PChar(Format('На форме %d кнопок',

[TMyButton.GetCount])),'Финиш',mb_ok);

В данном случае использовалась функция Windows API MessageBox, так как при разрушении формы доступ в секции finalizationко многим функциям Delphi невозможен. Запуская данную программу на выполнение, получим два сообщения до построения и после разрушения формы.

СОБЫТИЯ

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

Рассмотрим вариант переопределения методов послания уведомления о событии. Такие методы реализуются в разделе Protected. Поэтому потомки тех встроенных классов, в которых реализуются такие методы, могут делать доступными их для пользователя. Например, рассмотрим, как переопределяется метод послания уведомления о событии Click для кнопки, одновременно изменяя её заголовок строку с текущим временем.

TtimeButton=class(Tbutton)

Protected

Procedure Click; override;

End;

. . . .

Procedure TtimeButton.Click;

Inherited Click;

Caption:=DateTimeToStr(Now);

End;

Методы послания уведомления о событии являются указателями на метод, которые вызываются в случае, когда компонент получает сообщение от Windows о том, что произошло некоторое событие, воздействующее на данный компонент. Обработчики событий представляют собой свойства, которые выполняют чтение и запись в указатели на метод. Например, рассмотрим фрагмент, взятый из класса TControl, содержащегося в исходном коде VCL. Имена обработчиков событий по соглашению содержат префикс On. Вначале объявляется событие:

TNotifyEvent=procedure(Sender:TObject) of object;

Далее объявляется класс, содержащий поля данного типа и обработчики событий, работающие с этими полями.

Tcontrol=class(TComponent)

Private

FOnClick: TNotifyEvent;

. .

Ptotected

Property OnClick: TnotifyEvent read FonClick

write FOnClick;

. . .

end;

При создании собственных обработчиков необходимо научиться строить методы посылки уведомления о событии. Эти методы должны уметь получать и обрабатывать сообщения Windows, что возможно сделать с помощью методов вида Message.

УКАЗАТЕЛИ НА МЕТОДЫ

Этот тип данных представляет собой ссылку (указатель) на метод. Указатель на метод содержит два адреса: одна ссылка на адрес кода метода, другая – на адрес экземпляра объекта – представляет собой скрытый параметр self. Адрес self представляет собой в данном случае адрес расположения данных, принадлежащих конкретному объекту.

Указатели на методы реализуют один из принципов компонентной технологии – делегирование. Если кто-то написал класс, у которого есть поля в виде указателей на методы, например,

Type

TNotifyEvent=procedure(Sender:Tobject) of object;

TMyButton=class

OnClick: TNotifyEvent;

End;,

то можно менять поведение построенных (даже скомпилированных) этого типа объектов, просто присваивая этим указателям новые методы. Например,

Type

TForm1=class(TForm)

Procedure OnButton1Click(Sender:Tobject);

Button1:MyButton;

End;

Теперь при построении компонента на форме можно делегировать обработчик OnButton1Click из TForm1 в MyButton путем следующего присваивания:

MyButton.OnClick:=Form1.OnButton1Click;

ПРИМЕР 15

Продолжим рассмотрение примера 14. Попытаемся не только динамически создавать новые объекты, но и разрушать их также динамически. Выберем, что уничтожение очередного объекта будет наступать, как только пользователь нажмет на клавишу BackSpace (#8). Для реализации этой идеи понадобятся указатели на методы.

В данном случае динамически созданный объект для своего уничтожения должен отслеживать нажатие клавиши #8. Для этих целей может служить событие OnKeyPress. Этому событию надо делегировать собственное событие. Прежде всего необходимо убедиться, что оно объявлено как указатель на метод, иначе делегирование невозможно. В справочной системе Delphi можно найти следующее объявление:

TKeyPressEvent=procedure(Sender:Tobject;var key:char)

of object;

Property OnKeyPress: TKeyPressEvent;

Таким образом, для делегирования необходимо создать свою процедуру. В классе TForm1 добавим собственный обработчик: Procedure MyButtonKeyPress(Sender:Tobject;

var key:Char);

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

ToDestroy:TMyButton;

Теперь определим новую процедуру обработки события OnKeyPress:

procedure TForm1.MyButtonKeyPress(Sender: TObject; var Key: Char);

Begin

if key=#8 then ToDestroy:=Sender as TButton;

end;

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

OnKeyPress:=MyButtonKeyPress;

Допишем еще одну строку для установки фокуса на текущий объект SetFocus;,т.е. после удаления очередного объекта необходимо переместить фокус ввода на предыдущий объект.

Удаление кнопки реализуем в обработчике OnTimer. Полный текст программы приводится ниже.

unit prim15;

Interface

uses Windows, Messages, SysUtils,Classes, Graphics,

Controls, Forms, Dialogs, StdCtrls, ExtCtrls;

Type

TMyButton=class(TButton)

Public

Constructor Create(AOwner:TComponent); override;

Destructor Destroy; override;

class function GetCount:integer;

end;

TForm1 = class(TForm)

Timer1: TTimer;

procedure FormMouseDown(Sender: TObject;

Button: TMouseButton;

Shift: TShiftState; X, Y: Integer);

procedure Timer1Timer(Sender: TObject);

Private

ToDestroy:TMyButton;

Procedure MyButtonKeyPress(Sender:Tobject;

var key:Char);

end;

var Form1: TForm1;

Implementation

var CountBtns: integer = 0;

{$R *.dfm}

Constructor TMyButton.Create(AOwner: TComponent);

begin

inherited;

Inc(CountBtns);

end;

Destructor TMyButton.Destroy;

begin

dec(CountBtns);

inherited;

end;

class function TMyButton.GetCount: integer;

begin

Result:=CountBtns;

end;

procedure TForm1.MyButtonKeyPress(Sender: TObject;

var Key: Char);

Begin

if key=#8 then ToDestroy:=Sender as TMyButton;

end;

procedure TForm1.FormMouseDown(Sender: TObject;

Button: TMouseButton;

Shift: TShiftState; X, Y: Integer);

begin

with TMyButton.Create(self) do begin

Parent:=self;

Left:=x;

Top:=y;

Width:=Width+60;

Caption:=Format('%d кнопка x,y= %d,%d',

[GetCount,x,y]);

OnKeyPress:=MyButtonKeyPress;

SetFocus;

end;

end;

procedure TForm1.Timer1Timer(Sender: TObject);

begin

Наши рекомендации