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).
Рис. 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