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]); // Адрес метода