- •Объектно-ориентированное программирование
- •Часть 1 классы и объекты
- •Введение
- •1. Классы и объекты
- •Var aLine: tLine;
- •Var aColorLine: tColorLine;
- •2. Методы
- •2.1. Методы-функции и методы-процедуры
- •2.2. Конструкторы и деструкторы
- •Inherited Create;
- •Var TmpFrm: tForm;
- •Var Mem: tMemo;
- •2.3. Классовые процедуры и функции
- •Var s: String;
- •2.4. Скрытый Self
- •3. Видимость компонентов класса
- •4. Наследование
- •4.1. Основные понятия
- •4.2. Наследование полей
- •4.3. Поведение методов при наследовании
- •Var SomeObject: t1;
- •Virtual;
- •Virtual; Abstract;
- •4.4. Иерархия классов
- •4.5. Rtti
- •4.6. Проверка типа
- •4.7. Приведение типа
- •4.8. Указатели на класс
- •Var ObjRef: tObjRef;
- •Implementation
- •X, y, w, h: Integer): tControl;
- •5. Полиморфизм
- •6. Свойства (properties)
- •6.1. Объявление свойств
- •6.2. Объявления свойств-массивов
- •Var I: Byte;
- •6.3. Раздел Read
- •6.4. Раздел Write
- •Inherited Create;
- •Inherited Destroy;
- •6.5. Команды Default, NoDefault и Stored
- •6.6. Команда Index
- •Var aYear, aMonth, aDay: Word;
- •Var aYear, aMonth, aDay: Word;
- •6.7. Команды DispId и Implements
- •6.8. Переопределение свойств при наследовании
- •7. События (events)
- •7.1. Объявление событий
- •IfAssigned(fOnMouseMove) Then fOnMouseMove(Self, Shift, X, y);
- •Vk_return: NumMemo.DoNumStr(l);
- •Vk_return: PostMessage(NumMemo.Handle, wm_user1,1, 0);
- •7.2. Обработчики событий
- •7.3. Делегирование событий
- •Var Objl: tIstClass;
- •7.4. Переопределение стандартных событий
- •Var NewBtn: tNewButton;
7. События (events)
Наиболее общее определение события - любое происшествие, вызванное вмешательством пользователя, поведением системы или логикой кода приложения. Событие в программе может быть связано с некоторым кодом, называемым обработчиком события и отвечающим на это происшествие, что позволяет настраивать поведение компонентов без необходимости модернизировать классы.
С точки зрения разработчика, событие - это имя, к которому обработчик события может быть присоединен в программе.
События имеются почти у всех компонентов. Компоненты используют свойства, чтобы определить и выполнить события. При этом обычно используется прямой доступ к полям. Кроме того, пользователь может создавать новые события, переопределять стандартные события и определять код обработчика события.
Если разработчик реализовал код обработчика события, то он выполняется, когда это событие происходит. В противном случае обычно ничего не происходит. Компоненты Delphi имеют достаточно большое количество событий, однако в приложениях создаются обработчики только для некоторых из них.
Имеется два вида событий: взаимодействие пользователя и изменение состояния. События взаимодействия пользователя почти всегда вызываются в соответствии с сообщениями Windows, указывая, что пользователь делал кое-что, поэтому компонент может ответить. События изменения состояния также могут быть сведены к сообщениям Windows.
Обработчики событий могут не возвращать никакой информации о событии, которое называется в таком случае событием уведомления или возвращать дополнительную специфическую информацию, связанную с событием. В этом случае его называют специфическим событием.
События уведомления просто сообщают, что некоторое событие случилось, без предоставления информации о том, где и когда. Уведомления обычно использую тип TNotifyEvent, который имеет только один параметр -отправитель события. Так событие OnClick - уведомление. Уведомление -односторонний процесс. Нет никакого механизма для обеспечения обратной связи или предотвращения дальнейшей обработки уведомления. Когда создается обработчик для обработки такого события, то все, что мы знаем - то, что это событие произошло, и какой компонент вызвал событие.
Специфические события отличаются от событий уведомления, и используются в тех случаях, когда хотелось бы знать, не только какое событие случилось и какой компонент его вызвал, но и получить сопутствующую дополнительную информацию. Например, если событие - нажатая клавиша, то, вероятно, хотелось бы знать, какая клавиша нажата. В подобных случаях необходимы обработчики, которые включают параметры для дополнительной информации. В подобных случаях нужны обработчики специфических событий. Специфических событий достаточно много.
7.1. Объявление событий
Синтаксически события объявляются почти полностью аналогично свойствам:
Property <имя события>: <указателъ на метод> [Index <целое число} [Read <поле события метод чтения>] [Write <поле события\метод записи>} [Stored Логическое выражение>} [Default Nil|NoDefault];
Примечания:
• Хотя в командах чтения и записи могут использоваться методы, на практике они не используются. Обычно используется прямой доступ к полю.
• Команда Index используется вместе с методами записи или чтения. Поскольку методы практически не используются, то и команда Index также.
• Команда Default фактически допускает только одно значение по умолчанию - Nil, которое и так устанавливается компилятором. Поэтому использование команды Default и, соответственно, NoDefault нецелесообразно.
• Все события в Delphi принято именовать с "On ": OnCreate, OnClick и т.д.
Событие OnClick объявлено в классе TControl следующим образом:
Type
TControl=Class(TComponent) Private
FOnClick; TNotifyEvent; // Объявляется none типа указатель на метод
Function IsOnClickStored: Boolean;
II Другие объявления Protected
Procedure Click; Dynamic; // Корреспондирующий метод события
Property OnClick: TNotifyEvent Read FOnClick Write FOnClick
Stored IsOnClickStored; // Событие
End;
В данном случае событие и соответствующее поле имеют тип TNotifyEvent.
Таким образом, в общем случае для объявления события необходимо сделать следующее:
• определить или выбрать тип данных события (тип указателя на метод);
• объявить поле соответствующего типа для хранения значения события;
• объявить (корреспондирующий - event-dispatching method) метод, который будет вызывать обработчик события, когда оно произойдет. Представить реализацию этого метода и определить механизм его вызова;
• объявить собственно событие.
Синтаксис объявления события уже рассмотрен. Синтаксис объявления соответствующего поля не отличается от объявления любого другого поля, если к этому моменту известен тип данных поля. Рассмотрим поэтому как следует решать остальные проблемы.
а) Объявление типа события.
Событие - это механизм, с помощью которого связывается некоторое происшествие с некоторым кодом. Код оформляется в виде метода, тип которого объявляется до объявления класса, включающего объявление события. Этот метод должен принадлежать не классу, а экземпляру класса, поскольку даже для объектов одного класса исполняемый код обычно бывает различным. Создание кода метода, реагирующего на событие - это привилегия программиста, Delphi же обеспечивает связь этого метода с событием для вызова этого метода.
Связь события и метода, выполняющего в ответ на это событие, осуществляется в Delphi с помощью указателя на метод. Точнее - это указатель на метод в экземпляре класса. Указатели на метод работают точно также как любой другой процедурный тип, но еще они поддерживают скрытый указатель на объект. Как и всякий указатель, это фактически адрес, в данном случае, адрес метода, и он требует четыре байта для размещения в памяти. Поскольку эти методы предназначены для обработки событий, их называют обработчиками событий (event handler), а управление объектами - управлением по событиям (event driven).
Таким образом, для объявления события необходимо предварительно объявить или выбрать стандартный тип обработчика события. От обычного процедурного типа указатели на метод отличаются тем, что используют специальный синтаксис:
Type <имя типа>=Procedure(Sender: T0bject[; <параметры>}) Of Object;
Дополнительные ключевые слова Of Object указывают компилятору о том, что это указатель на метод объекта, а не самостоятельная процедура. Таким образом, для обработки событий в Delphi предусмотрено объявление указателей на метод, обеспечивающих неявную передачу указателя на экземпляр (переменная Self), вызвавший то или иное событие.
В большинстве случаев указатели на метод объявляются как процедуры. Хотя компилятор Delphi позволяет объявлять функции-указатели на метод использовать их для обработчиков событий следует осторожно. Программист может не создать код обработчика для события, а поскольку пустая функция возвращает неопределенный результат, то пустой обработчик события недопустим.
Несмотря на то, что обработчики события нецелесообразно объявлять функциями, можно получать информацию из них, используя Var параметр.
Type TKeyPressEvent=Procedure(Sender: TObject; Var Key: Char) Of Object;
Обработчик события для такого указателя на метод может иметь в дальнейшем, например, следующий вид:
Procedure TForml.EditlKeyPressed(Sender: TObject; Var Key: Char);
Begin
Key:=UpCase(Key);
End;
Можно использовать Var параметры и для того, чтобы отменять заданную по умолчанию обработку.
Используются, хотя и редко, функции - указатели на метод. Например, в самой Delphi объявлен указатель на метод в виде функции:
Type THelpEvent=Function(Command: Word; Data: Longint;
Var CallHelp: Boolean): Boolean Of Object;
Property OnHelp: THelpEvent;
Список событий в Delphi достать 'то велик. Однако для простых средств управления он небольшой и соответствующие им указатели на метод приведены в табл.2.
TCloseEvent=Procedure(Sender: TObject; Var Action: TCloseAction) Of Object;
|
OnClose
|
TDockDropEvent=Procedure(Sender: TObject; Source: TDragDockObject; X, Y: Integer) Of Object;
|
OnDockDrop
|
TDockOverEvent=Procedure(Sender: TObject; Source: TDragDockObject; X, Y: Integer; State: TDragState;Var Accept: Boolean) Of Object;
|
OnDockOver
|
TStartDockEvent=Procedure(Sender: TObject; Var DragObject: TDragDockObject) Of Object;
|
OnStartDock
|
TStartDragEvent=Procedure(Sender: TObject; Var DragObject: TDragObject) Of Object;
|
OnStartDrag
|
TActionEvent=Procedure(Action: TBasicAction; Var Handled: Boolean) Of Object;
|
OnActionExecute, OnUpdate, OnExecute, OnActionUpdate,
|
TContextPopupEvent=:Proceduгe(Sendeг: TObject; MousePos: TPoint, Var Handled: Boolean) Of Object;
|
OnContextPopup
|
Есть группа специальных событий, используемых при работе с базами данных. Internet и т.п. С ними можно познакомиться в справочной системе Delphi. Прежде, чем объявлять собственный тип указателя на метод следует познакомиться с уже имеющимися.
б) Определение механизма возбуждения события.
Важным моментом в создании пользовательского события является определение того, как будет вызываться (возбуждаться) событие. Кроме того, если это не уведомительное событие, то следует знать, как будут определяться значения дополнительных параметров.
Этот механизм должен фактически реагировать на какие-то ситуации, происшествия, в ответ на возникновение которых и должно возбуждаться события. Например, событие OnClick реагирует на щелчок мыши по компоненту или форме.
Для инициализации события можно использовать либо непосредственный вызов соответствующего метода, либо использовать для этого механизм сообщений. Для присвоения значений дополнительным параметрам необходимо вставлять соответствующий код, который будет определять их значения и передавать в обработчик пользовательского события.
в) Объявление и реализация метода, вызывающего обработчик события. После определения механизма возбуждения пользовательского события, следует решить, какие реакции (видимые и невидимые) должны быть у объекта, и как будет вызываться обработчик события. Во многих случаях обработчики могут представлять собой простые уведомления определенного типа. В особых случаях может также потребоваться возврат значения из обработчика.
Обычно для реализации кода реакции объекта на событие и вызова обработчика события объявляется и создается реализация соответствующего (корреспондирующего) виртуального защищенного метода.
Так, в задачу динамического метода Click, помимо изменения внешнего представления кнопки (кнопка "утопает"), входит проверка наличия обработчика события, и если он есть, передача управления ему.
Синтаксис объявления такого метода следующий:
Procedure <имя метода>[(<парсшетры>)]; Dynamic;
Например, для события OnMouseMove корреспондирующий метод с параметрами объявлен в классе TControl следующим образом:
Procedure MouseMove(Shift: TShiftState; X, Y: Integer); Dynamic;
После объявления метода необходимо в разделе Implementation модуля представить его реализацию. Реализация должна позволить компоненту отвечать на действия, активизируемые вашим методом сообщения о событии.
Внутри библиотеки RTL Delphi вызовы обработчиков событий находятся в методах, обрабатывающих сообщения Windows. Необходимо при создании события создать и свой метод диспетчеризации вызова события. В принципе его код может быть достаточно прост. Фактически необходимо проверить наличие обработчика события и, если он реализован, то вызвать этот обработчик, передав ему в качестве параметра объект, вызвавший событие, а при необходимости и дополнительные параметры.
Стандартный синтаксис для реализации метода уведомления о событии имеет следующий вид:
Procedure <гшя класса>.<гшя метода>[(<объявление параметров>)];
Begin
<некий код> If Assigaed(<none события>)
Then <поле co6bimw>(Self[, '^дополнительные параметры>]);
<некий код> End;
Например, вызов обработчика события OnMouseMove в модуле Controls сделан следующим образом:
Procedure TControl.MouseMove(Shift: TShiftState; X, Y: Integer);
Begin