- •Объектно-ориентированное программирование
- •Часть 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.4. Переопределение стандартных событий
При различных изменениях в программах, работающих в Windows, посылаются различные сообщения Windows, которых сотни. Работать с таким большим количеством сообщений нелегко, даже имея под рукой справочник. Большим достижением Delphi является избавление программистов от необходимости работать непосредственно с сообщениями Windows, хотя такая возможность у них есть. Вместо обработки большого числа сообщений Delphi предлагает программистам создавать обработчики событий. При этом стандартных событий в Delphi не более двух десятков.
События, предусмотренные в Delphi и наследующие наиболее общие случаи, возникающие в Windows, называются стандартными событиями. Как правило, поля для хранения значений этих событий защищены от разработчиков, т.е. нет возможности подключать обработчики к ним. При этом вызовы обработчиков стандартных событий происходят внутри методов, обрабатывающих сообщения Windows.
Имеется две категории стандартных событий:
• События, определенные для всех средств управления. Эти события определены в классе TControl. Список подобных событий включает восемь наименований и приведен в табл.3.
____ _____Таблица 3
,,—.::;;•::•::;:,::::•:"::;:.;:;..:••:;,:,:;
|
|
• наименование^-: i::^.- •^бйдд-пвя^^НУ
|
^^^^^^^^^^'^^^Назначе^^^ ^-^
|
OnClick
|
Пользователь щелкает по элементу управления
|
OnDragDrop
|
Завершена операция перемещения
|
OnEndDrag
|
Заканчивается или отменяется операция перемещения
|
OnMouseMove
|
Пользователь перемещает мышь над объектом
|
OnDblClick
|
Пользователь дважды щелкает мышью по элементу управления
|
OnDragOver
|
Объект перемещается над нужной областью или объектом управления
|
OnMouseDown
|
Пользователь нажимает кнопку мыши по элементу, находящемуся в фокусе
|
OnMouseUp
|
Пользователь отпускает кнопку мыши на элементе, находящемся в фокусе
|
Эти события доступны во всех элементах управления. Стандартные события имеют корреспондирующие защищенные динамические методы. Так динамический метод Click вызывается для обработки события OnClick.
• В дополнение к ним определены стандартные события для стандартных средств управления Windows, которые являются уже наследниками класса TWinControl. Список этих событий включает пять наименований и приведен в табл.4.
Эти события также имеют корреспондирующие методы.
Таблица 4
Наименование события |
Назначение события; |
OnEnter |
Элемент управления получает фокус |
OnKeyDown |
Пользователь нажимает клавишу |
OnKeyPress |
Пользователь нажимает символьную клавишу |
OnKeyUp |
Пользователь отпускает нажатую клавишу |
OnExit |
Элемент управления теряет фокус |
У пользователя есть следующие возможности переопределения событий:
• Можно переопределить событие в разделе с большей видимостью с целью сделать его доступными во время разработки и выполнения программы.
Для того, чтобы сделать событие доступным во время выполнения и разработки программы, следует объявить его повторно в разделе Public или Published класса-потомка. Синтаксически это выглядит следующим образом:
Type
<имя класса>=С\а&&(<класс-предок>) Published
Property <имя события>\ II Переопределение события End;
В самой Delphi подобное переопределение сделано неоднократно. Например, в модуле Controls объявлен класс TControl, включающий событие OnClick, пример объявления которого в разделе Protected был приведен ранее в §7.1.
В модуле StdCtrls объявлен класс TButton, в котором переопределено это событие следующим образом:
Published Property OnClick;
Переопределение события изменяет только его уровень защиты и не изменяет стандартной реакции на это событие.
• Можно переопределить корреспондирующий динамический метод с целью включения в его код действий - общих для всех экземпляров этого класса.
Для изменения стандартной реакции на события всех компонентов одного класса следует сделать замещение корреспондирующего защищенного динамического метода в классе-потомке. При этом следует вызывать унаследованный обработчик. Чаще всего это рекомендуется делать в начале кода замещения метода, хотя могут быть случаи, когда вызывать наследуемый обработчик целесообразно в конце кода. Синтаксически это может выглядеть следующим образом:
Type
<имя класса>=С\&&&(<класс-предок>) Protected
Procedure <метод>; Override; // Замещение метода End;
Procedure <имя класса>.<метод>; II Реализация замещающего метода Begin
Inherited <метод>; II Вызов наследуемого обработчика дополнительный код> II Добавление новых реакций End;
Таким образом, чтобы изменить корреспондирующий метод события, необходимо указать, когда вызывается наследованное поведение, а когда -новое действие.
Рассмотрим пример класса, в стандартную обработку которого добавлен код вывода даты вместо заголовка на кнопке.
Type
TTimeButton=Class(TButton) // Объявление класса-потомка Protected
Procedure Click; Override; // Замещение (переопределение) метода End;
Procedure TTimeButton.Click; // Реализация замещающего метода Begin
Inherited Click; // Выполнение стандартных действий кнопкой II После стандартных действий - вывод даты Caption:=DateTimeToStr(Now);
End;
В отличие от обработчика события, создаваемого для каждого конкретного компонента и влияющего только на его поведение, изменение корреспондирующего метода влияет на поведение всех компонентов этого класса.
Таким образом, стандартные действия всех компонентов созданного нами класса TTimeButton будут заключаться в "утоплении" кнопки после щелчка, выводе даты и времени вместо названия кнопки и вызове обработчика события, если он для кнопки будет создан или делегирован.
В заключение рассмотрения вопроса о переопределении событий следует сказать следующее:
• Переопределять стандартные события Delphi с целью изменения команд доступа к событию невозможно и нецелесообразно:
• Поля для хранения значений стандартных событий определены в разделе Private класса и поэтому невидимы в потомках, а для переопределения необходимо указать событие с одной из команд Read или Write. Поскольку любая из этих команд требует прямого или косвенного доступа к соответствующему полю свойства, то это невозможно.
Если же объявить новое поле, то необходимо будет переопределить корреспондирующий метод и представить его новую реализацию. При этом необходимо будет отка-
-66-
заться от наследования обработчика класса-предка, иначе корреспондирующий метод будет обращаться к обработчикам двух полей - унаследованного и нового.
• Нецелесообразно потому, что принцип использования событий существенно отличается от использования свойств. Если исключить команду Write, то нельзя будет создавать обработчик событий и возможна будет только обработка события, предусмотренная по умолчанию. Однако для этого достаточно переопределить корреспондирующий метод. Если исключить команду Read, то невозможно будет выполнить код обработчика события, и область использования такого события трудно подыскать.
• Переопределение стандартного события с целью изменения типа события потребует объявления в классе нового поля нужного типа, переопределения корреспондирующего метода или объявление нового, а также создание их реализации. Принципиально это возможно. При этом доступ к переопределенному событию возможен с помощью привидения типов. Вопрос заключается в целесообразности.
В качестве примера создадим новый компонент, включающий переопределение события OnClick, заключающееся в изменении его типа (тип TNotifyEvent заменяется на тип TMouseMoveEvent), добавлении нового поля FNewOnClick типа TMouseMoveEvent и замещении метода Click.
Type
TNewButton=Class(TButton) // Объявление нового класса Protected
FNewOnClick: TMouseMoveEvent; // Объявление нового поля Public
Procedure Click; Override; // Замещение метода Property OnClick: TMouseMoveEvent Read FNewOnClick
Write FNewOnClick; // Событие End;
Procedure TNewButton.Click; // Реализация корреспондирующего метода Begin
Inherited Click; // Включен для демонстрации приведения типа IfAssigned(FNewOnClick) Then FNewOnClick(Self, [ssAlt], 50, 50);
End;
Procedure TForml.Button !MouseMove(Sender: TObject;
Shift: TShiftState; X, Y: Integer);
Begin
MessageDlgPos('Move!', mtlnformation, [mbOK], 0, X, Y);
End;
Procedure TForml.Button2Click(Sender: TObject);
Begin
ShowMessage('Click!'); // Для демонстрации приведения типа End;
Создание компонента и делегирование ему события сделаем с помощью виртуального конструктора, который активизируем при открытии формы.
-67-
Procedure TFornil.ForinCreate(Sender: TObject);