Команды DispID и Implements

а) КомандаDispID используется для присвоения свойству значения Dispatch ГО, которое применяется в автоматизации OLE. Это целочисленное значение обычно используется при объявлении свойств Dispatch интерфей­сов. Эта команда может быть указана только у свойства, объявляемого в раз­деле Automated класса. Более подробно эта команда будет рассмотрена в гла­ве, посвященной интерфейсам.

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

Переопределение свойств при наследовании

При наследовании свойств можно в классе-потомке заменить унаследо­ванную или добавить отсутствующую команду, изменить видимость и даже

повторно объявить свойство.

а) Для изменения области видимости необходимо в классе-потомке объ­явить имя свойства без указания типа и команд в разделе с большей видимо­стью, обычно в Public или Published, т.е. в сторону увеличения видимости.

Public Property MyProperty;

б) Для изменения или добавления команды следует объявить свойство без указания типа с дополнительной и/или измененной командой (Read] Write|Stored|Default|NoDefault). Одновременно можно изменить и область ви­димости.

в) Для повторного объявления свойства следует указать имя свойства вместе с новым типом. Обязательно должна быть хотя бы одна команда дос­тупа (Read|Write). При переопределении свойства можно вместо старых мето­дов доступа объявить и использовать новые.

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

Объявим два класса, включающих свойства и методы доступа. При этом во втором классе свойство переопределим заново, изменив одновременно и тип свойства и методы доступа.

Type

TlstClass=Class // Первый класс

FFId: Integer; // Поле целого типа

Function Getint: Integer; // Метод чтения

Procedure Setlnt(lnt: Integer); // Метод записи

Property Prop: Integer Read Getint Write Setint; // Свойство целого типа

End;

T2ndClass=Class(TlstClass) II Второй класс

Function GetStr: String; II Метод чтения

Procedure SetStr(St: String); II Метод записи

Property Prop: String Read GetStr Write SetStr; // Свойство строкового типа

End;

Var Objl: TIstClass; II Переменная первого класса

Obj2: TZndClass; // Переменная второго класса

После объявления реализации указанных методов и инициализации эк­земпляров указанных классов и допустимы следующие операторы:

Procedure TForml.ButtonlClick(Sender: TObject);

Begin

Editl.Text:=IntToStr(Objl.Prop); II Доступ к свойству 1-го объекта Edit2.Text:=T2ndClass(Obj l).Prop; II Доступ к свойству 1-го объекта

End;

Procedure TFonnl.Button2Click(Sender: TObject);

Begin

Edit3.Text:=Obj2.Prop; II Доступ к свойству 2-го объекта Edit4.Text:=IntToStr(TlstClass(Obj2).Prop); // К свойству 2-го объекта

End;

Procedure TForinl.Button3Click(Sender: TObject);

Begin

Edit5.Text:=T2ndClass(Objl).Prop; II Доступ к свойству 1-го объекта

End;

Третий обработчик показывает, что в поле ввода Edit5 заносится значе­ние свойства строкового типа. Однако с помощью приведения типов вызыва­ется объект первого класса, у которого одноименное свойство имеет целый тип. Тем не менее все корректно работает. Обращение к свойству приводит к вызову соответствующего ему метода.

г) Команда Implements позволяет для наследованных свойств добавить список интерфейсов, не меняя других характеристик свойства.

СОБЫТИЯ (EVENTS)

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

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

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

Если разработчик реализовал код обработчика события, то он выполня­ется, когда это событие происходит. В противном случае обычно ничего не происходит. Компоненты Delphi имеют достаточно большое количество со­бытий, однако в приложениях создаются обработчики только для некоторых из них.

Имеется два вида событий: взаимодействие пользователя и изменение состояния. События взаимодействия пользователя почти всегда вызываются в соответствии с сообщениями Windows, указывая, что пользователь делал кое-что, поэтому компонент может ответить. События изменения состояния так­же могут быть сведены к сообщениям Windows.

Обработчики событий могут не возвращать никакой информации о со­бытии, которое называется в таком случае событием уведомления или воз­вращать дополнительную специфическую информацию, связанную с событи­ем. В этом случае его называют специфическим событием.

События уведомления просто сообщают, что некоторое событие случи­лось, без предоставления информации о том, где и когда. Уведомления обычно использую тип TNotifyEvent, который имеет только один параметр -отправитель события. Так событие OnClick - уведомление. Уведомление -односторонний процесс. Нет никакого механизма для обеспечения обратной связи или предотвращения дальнейшей обработки уведомления. Когда созда­ется обработчик для обработки такого события, то все, что мы знаем - то, что это событие произошло, и какой компонент вызвал событие.

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

Объявление событий

Синтаксически события объявляются почти полностью аналогично свойствам:

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