Forml.NumMemo.DoNumStr(Msg.wParam);

End;

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

Procedure TForml.FormKeyDown(Sender: TObject; Var Key: Word;

Shift: TShiftState);

Begin

Case Key Of

VK_DOWN: IfNuniMeino.NuinStr<NumMeino.Lines.Count+l Then NuniMemo.Perfornt(WM_USERl, 1, 0);

VK_UP: IfNumMemo.NuniStr>l Then

SendMessage(NumMemo.Handle, WM_USER1, -1, 0);

VK_RETURN: PostMessage(NumMemo.Handle, WM_USER1,1, 0);

End;

End;

Более подробно использование сообщений будет рассмотрено во второй части пособия в главе, посвященной сообщениям Windows.

Обработчики событий

События - это свойства процедурного типа, предназначенные для созда­ния пользовательских реакций на те или иные входные воздействия. Присво­ить такому свойству значение - это указать адрес метода, который будет вы­зываться в момент наступления события (содержит также и адрес объекта с данными). Программист может написать код обработчика события, a Delphi должна адрес этого обработчика события поместить в соответствующее со­бытию поле.

Для создания обработчика событий необходимо дважды щелкнуть мы­шью по соответствующему событию компонента на странице Events инспек­тора объектов. Delphi создаст заготовку обработчика события, включая объявление метода в классе TForm и его реализацию с операторными скобками Begin End.

События имеют разные типы в зависимости от происхождения и предна­значения. Многие события Delphi возникают, когда поступает соответствую­щее сообщение Windows. Событие выполняет предварительную обработку сообщения и предоставляет информацию о том, какой объект вызвал это со­бытие, а для специфических событий и дополнительную информацию. Таким образом, вся необходимая информация для написания обработчика имеется в руках программиста.

Фактически процедуры-обработчики событий различаются набором па­раметров, при этом общим для всех является параметр Sender, указывающий на объект-источник события. Когда происходит событие, связанное с некото­рым обработчиком событий, этот обработчик событий получает сообщение для объекта, который породил это событие. Это сообщение передается обра­ботчику события через параметр Sender. По существу, когда обработчик вы­зван, Sender содержит объект, породивший событие. Поскольку все классы являются потомками от TObject, то экземпляр любого класса может быть присвоен переменной или параметру TObject (полиморфное присвоение). Ис­пользование Sender - это способ позволить объекту любого класса переда­ваться в процедуру обработки события.

С одной стороны, вы не обязаны создавать обработчик для того или ино­го события, но с другой - вы не можете сделать больше, чем вам позволено.

Чтобы быть совместимым с событием данного типа, обработчик события должен иметь тоже число и тип параметров, что и у указанного типа. Для ин­сталлированных компонентов Delphi автоматически генерирует обработчик нужного типа для своих событий, а программисту остается только написать код тела обработчика.

Все компоненты, как правило, могут реагировать на события. Хотя мно­гие простые объекты, например текстовые метки (TLabel), способны реагиро­вать только на немногие события, однако все равно есть возможность вы­брать следующие действия:

• проигнорировать событие. В этом случае поведение объекта будет оп­ределяться обработчиком события по умолчанию и его можно переопреде­лить;

• перехватить событие (trap the event). Тогда необходимо написать соб­ственный обработчик события, изменяющий поведение объекта, принятое по умолчанию.

я) Обработчики уведомительных событий.

Самый простой тип стандартного события TNotifyEvent является уведо­мительным и не имеет других параметров, кроме Sender, указывающего на объект, вызвавший событие. Тип объявлен в модуле Classes следующим об­разом:

Type TNotifyEvent=Procedure(Sender: TObject) Of Object;

Наиболее часто используемое событие этого типа OnClick - щелчок мы­шью по компоненту предусмотрен у большинства компонентов управления. В простейшем случае обработчик этого события может выглядеть следую­щим образом:

Procedure TForml.FormClick(Sender: TObject);

Begin

ShowMessage('Щелчок мышью по форме!');

End;

б) Обработчики специфических событий.

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

Type TMouseEvent^Procediu^Sender: TObject; Button: TMouseButton;

Shift: TShiftState; X, Y: Integer) Of Object;

Если вы хотите создать обработчик этого события, то вы должны обра­титься к инспектору объектов, найти событие OnMouseDown на странице Events и написать код обработчика этого события. Эту процедуру-обработчик можно использовать, например для вывода на поле формы координат мест

нажатия на левую клавишу мыши, если определить тело этой процедуры сле­дующим образом:

Procedure TForml.FormMouseDown(Sender: TObject; Button: TMouseButton;

Shift: TShiftState; X, Y: Integer);

Begin

Forml.Canvas.Font.Color:=clBIue; // Установка цвет текста Forml.Canvas.Brush.CoIor:=cIBtnFace; // Установка цвета фона Forml.Canvas.TextOut(X, Y, 'X='+IntToStr(X)+' Y='+IntToStr(Y));

End;

Как видно из приведенного примера, программист не обязан использо­вать всю предоставленную событием информацию.

Делегирование событий

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

<oбъeкmI>.<coбblmue>'.=:<oбьeкm2>.<coбыmlte>;

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

<обьект>.<событие>:=!^11;

Принцип делегирования позволяет избежать трудоемкого процесса по­рождения новых дочерних классов для каждого конкретного случая, заменяя его простой подстановкой процедур. Для реализации делегирования как нельзя кстати, подходит введенный в Delphi указатель на метод. Поскольку. помимо явно описанных параметров, методу передается еще и указатель на вызывающий его экземпляр (Self), то можно описать тип процедуру, которая будет совместима по присвоению с методом (т.е. будет предусматривать пе­редачу Self).

Type TAnyEvent=Procedure(Sender: TObject; VarAValue; Integer) Of Object;

TlstClass=Class // Объявление нового класса

FOnMyEvent: TAnyEvent;

Property OnMyEvent: TAnyEvent Read FOnMyEvent Write FOnMyEvent;

End;

T2ndClass=CIass // Класс - носитель методов

Procedure SetValuel(Sender: TObject; Var AValue: Integer);

Procedure SetValue2(Sender; TObject; VarAValue: Integer);

End;

Var Objl: TIstClass;

Obj2: T2ndClass;

Begin

Objl:=TlstClass.Create;

Obj2:=T2ndClass.Create;

Obj 1.0nMyEvent:=Obj2.SetValuel; II Делегирование первого метода

Obj 1.0nMyEnent:=Obj2.SetValue2; It Делегирование второго метода

Obj 1 .OnMyEnent:=Nil; // Отключение всех обработчиков

Напомним, что как в этом примере, так и повсюду в Delphi за свойства­ми-событиями стоят поля, являющиеся указателями на метод. Таким образом, при делегировании этим полям можно присвоить указатели на методы других классов. Главное, они должны быть одного типа.

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

Если внутри таких методов, которые используются несколькими объек­тами, необходимо определить, кто его вызвал, то можно воспользоваться оператором проверки типа Is:

If Sender Is <клас0Then <onepamop>;

Если объекты относятся к одному классу, то можно использовать свой­ство Tag компонентов, присвоив им различные целочисленные значения и используя приведение типов:

• If </cnacc>(Sender).Tag==l Then <onepamop>\

• If (Sender As<^acc>).Tag=l Then <onepamop>\

Делегирование стандартных событий между компонентами при разра­ботке программы в среде Delphi выполнить очень просто. Для этого следует выбрать компонент, в инспекторе объектов перейти на страницу Events, вы­брать нужное событие и с помощью раскрывающегося списка делегировать обработчик события другого компонента. При этом Delphi автоматически просмотрит все реализованные обработчики и предоставит для доступа спи­сок обработчиков только однотипных событий.

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

Var AnyEvent, NameEvent: <mun события>;

Begin

AnyVlVeat'.xs:<oб•ьeкm>.<coбыmue>•, NameEvent:=<ayKfl метода>;

If@AnyEvent=@NameEvent Then <onepamop>\

Можно получить адрес и имя метода следующим простым способом:

Var AnyEvent: <mun события>;

Begin

AnyEvent:=<o6'beKm>.<co6btmue>',

Labell.Caption:=MethodName(@AnyEvent); // Имя метода Label2.Caption:=Format('%p', [@AnyEvent]); // Адрес метода