Читайте также:
|
|
Наиболее общее определение события – любое происшествие, вызванное вмешательством пользователя, поведением системы или логикой кода приложения. Событие в программе может быть связано с некоторым кодом, называемым обработчиком события и отвечающим на это происшествие. С точки зрения разработчика, событие – это имя, к которому обработчик события может быть присоединен в программе.
Если разработчик реализовал код обработчика события, то он выполняется, когда это событие происходит. В противном случае обычно ничего не происходит.
Имеется два вида событий: взаимодействие пользователя и изменение состояния. События взаимодействия пользователя почти всегда вызываются в соответствии с сообщениями Windows, указывая, что пользователь делал кое-что, поэтому компонент может ответить. События изменения состояния также могут быть сведены к сообщениям Windows.
Обработчики событий могут не возвращать никакой информации о событии, которое называется в таком случае событием уведомления или возвращать дополнительную специфическую информацию, связанную с событием. В этом случае его называют специфическим событием.
События уведомления просто сообщают, что некоторое событие случилось, без предоставления информации о том, где и когда. Уведомления обычно использую тип TNotifyEvent, который имеет только один параметр – отправитель события. Специфические события отличаются от событий уведомления, и используются в тех случаях, когда хотелось бы знать, не только какое событие случилось и какой компонент его вызвал, но и получить сопутствующую дополнительную информацию. В подобных случаях необходимы обработчики, которые включают параметры для дополнительной информации. В подобных случаях нужны обработчики специфических событий.
Синтаксически события объявляются почти полностью аналогично свойствам:
Property <имя события>: <указателъ на метод>
[Read <поле события метод чтения>]
[Write <поле события\метод записи>];
• Хотя в командах чтения и записи могут использоваться методы, на практике они не используются. Обычно используется прямой доступ к полю.
• Все события в Delphi принято именовать: OnCreate, OnClick и т.д.
В данном случае событие и соответствующее поле имеют тип TNotifyEvent.
Таким образом, в общем случае для объявления события необходимо сделать следующее:
• определить или выбрать тип данных события (тип указателя на метод) (процедурный тип);
• объявить поле соответствующего типа для хранения значения события;
• объявить (корреспондирующий – event-dispatching method) метод, который будет вызывать обработчик события, когда оно произойдет. Представить реализацию этого метода и определить механизм его вызова;
Стандартный синтаксис для реализации метода уведомления о событии имеет следующий вид:
Procedure <имя класса>.<имя метода>[(<объявление параметров>)];
Begin
<некий код> If Assigned(<пoлe события>)
Then <поле coбытия>(Self[,<дополнительные параметры>]);
<некий код>
End;
type TNotifyEvent = procedure(Sender: TObject) of object; TControl = class(TComponent) private FOnClick: TNotifyEvent; protected procedure Click; dynamic; property OnClick: TNotifyEvent read FOnClick write FOnClick; end; procedure TControl.Click;begin if Assigned(FOnClick) then FOnClick(Self);
end;
Класс TButton наследуется от TControl и публикует свойство OnClick. Object Inspector, видя свойство процедурного типа, размещает его на странице обработчиков событий. При нажатии на кнопку класс TButton вызывает метод Click, и тот вызывает соответствующий обработчик события, если последний присвоен.
Можно также присвоить обработчику события указатель на обычную процедуру, не являющуюся методом класса. В этом случае предыдущий пример будет выглядеть так:
procedure ButtonClick (Self: TButton; Sender: TButton);begin ShowMessageFmt ('Нажата кнопка %s', [Sender.Caption]); ShowMessageFmt ('Self = %s', [Self.Name]);end; procedure AssignButtonClick (const Button: TButton);var Method: TMethod;begin Method.Code:= @ButtonClick; Method.Data:= Button; Button.OnClick:= TNotifyEvent (Method);end; |
В этом случае в процедуре, предназначенной быть обработчиком события, необходимо явно описать еще один, дополнительный параметр, который и будет получать значение Self. Правильный метод его описания зависит от соглашения о связях (calling convention), используемого в процедурном типе и методе. Для модели register (которая используется для всех стандартных обработчиков и принята по умолчанию для всех процедур в Delphi) этот параметр должен идти первым, а за ним – все остальные, так, как они описаны в типе обработчика события. Для приведения типов используется специальный тип TMethod, описанный в модуле System; он представляет собой внутреннюю реализацию объектного процедурного типа.
В пользу такого подхода можно найти два аргумента. Во-первых, в этом случае мы легко можем передать в обработчик события любой параметр Self (так, в примере, мы передаем словно бы собственный метод кнопки). Во-вторых, зная действительные типы параметров, мы можем использовать их при декларации процедуры и избежать лишних приведений типов: так, в ButtonClick параметры Self и Sender определены как TButton, хотя, вообще говоря, корректней бы было описать их как TObject.
Операция @
Особым моментом, о котором необходимо упомянуть, является операция @ (взятие адреса). Вообще говоря, ее следовало бы использовать при всех операциях с процедурными типами. Так, процедуру Example из самого первого примера было бы в некотором смысле правильнее записать так:
procedure Example;var CalcFunction: TCalcFunction;begin CalcFunction:= @Add; ShowMessageFmt('CalcFunction (2, 3) = %d', [CalcFunction (2, 3)]); end; |
Обратите внимание на использование операции @ в присваиваниях. Результат в этом случае будет абсолютно таким же: компилятор отслеживает операции с процедурными типами и при необходимости добавляет неявную операцию взятия адреса.
Мы могли бы написать:
if CalcFunction = Add then {... } |
и тем поставить компилятор перед выбором: то ли мы хотели сравнить указатели, то ли - вызвать две функции и сравнить их результаты. В случае если бы TCalcFunction определял функцию без параметров, оба этих толкования были бы синтаксически верны. Во избежание конфликтов в таких случаях компилятор всегда использует вызов процедуры, а для сравнения указателей необходимо использовать операцию @ – то есть писать
if @CalcFunction = @Add then {... } |
Как правило, использования операции @ не требуется.
Дата добавления: 2015-11-16; просмотров: 58 | Нарушение авторских прав
<== предыдущая страница | | | следующая страница ==> |
Понятие ссылки на метод объекта | | | Ошибки и исключительные ситуации |