Добавил:
sergeevpavel0406@mail.ru СОВА Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Информатика в техническом университете / Информатика в техническом университете. Объектно ориентированное программирование

.pdf
Скачиваний:
105
Добавлен:
06.03.2018
Размер:
9.48 Mб
Скачать

5. Объектная модель Delphi Pascal

Implementation

Var K:array[l..S\ of Double;

Procedure TMainForm.FormActivate(Sender: TObject);

Var i: integer;

 

 

Begin

 

 

for i: =0 to ControlCount'l

do

{для всех младших управляющих

ifControls[i\ is TEdit then

 

элементов формы}

 

{если элемент - TEdit, то}

begin

 

 

Controls[i\.Enabled: =true;

{разрешить получение фокуса}

(Controls[i] as TEdit).ReadOnly:=false; {разрешить ввод}

end;

 

 

ExButton. Visible: =true;

 

{показать кнопку «Вьшолнить»}

NextButton.Enabled: =false;

{запретить передачу фокуса кнопке

ResLabel Visible: =false;

 

«Выполнить»}

{спрятать метку результата}

ResultLabel. Visible: =false;

{спрятать поле результата}

AxEdit.SetFocus; {установить фокус на первый TEdit (TabOrder=0)}

End;

Procedure TMainForm.AllEditKeyPress{Sender: TObject; var Key: Char); Var Code:integer; S:string;

Begin

ifKey-#13 then

{если нажата клавиша Enter, то}

begin

 

Key: =#0;

{отменить сигнал ошибки - звонок}

Val{{ActiveControl as Tedit). Text,

K[{ActiveControlas Tedit).TaglCode);

{преобразовать введенные данные в число и разместить его в массиве К с индексом из поля Tag данного элемента}

ifCodeoO

then {если обнаружена ошибка преобразования, то}

begin

{сформировать сообщение об ошибке}

S: =chr(ordCA') +ActiveControL TabOrder div 2);

MessageDlgCКоординаты '+*$'+

exit;

'введены неверно.',mtError,[mbOk], 0);

 

end;

 

FindNextControl{ActiveControlytruefalsefalse).SetFocus; {передать фокус следующему активному элементу}

end End;

Procedure TMainForm.ExButtonClick(Sender: TObject); Var CodeJJ:integer; PP:TEdit; S:string; . . .

240

 

5.7. Создание и обработка сообщений и событий

Begin

 

 

РР: =AxEdit;

{установить фокус на первый элемент (TabOrder = 0)}

for i: =1 to 8 do

{для 8 последовательно расположенных элементов}

begin

 

 

Val(PPText, K[PPTag\Code);

{вьшолнить преобразоваьше}

ifCodeoO

then

{если обнаружена ошибка, то}

begin

 

{вьщать сообщение об ошибке}

S: =chr(ordCA')-^PP TabOrder div 2); PPSetFocus;

MessageDlgCKoopdunambi '-^S^'введены неверно.', mtErroK [mbOk], 0);

exit;

end;

ifi<>8 then PP:=FindNextControl(PP,truefalse,true) as TEdit; {передать фокус следующему активному элементу}

end;

{если логических ошибок не обнаружено, то запретить установку фокуса ввода на элементы ввода данных и объявить их «только для чтения»}

PP:=AxEdit; for i:=l to 8 do begin

PP.Enabled: =false; PPReadOnly:=true;

ifi<>8 then PP:=FindNextControl(PP,truefalse,true) as TEdit; end;

End;

. . .

В программе использовано также свойство Tag. Это свойство класса TComponent может хранить любую информацию разработчика в виде 32разрядного целого числа. В нашем случае оно хранит индекс вводимой координаты в массиве К, используемом для записи координат.

5.7. Создание и обработка сообщений и событий

Стандартная библиотека классов Ве1рЫ VCL предлагает разработчику достаточно большой набор сообщений и методов их разработки. Однако он имеет возможность добавить новое сообщение или переопределить методы обработки существующих сообщений.

При создании нового сообщения необходимо выполнить следующие действия:

1) описать тип сообщения;

241

5.Объектная модель Delphi Pascal

2)объявить номер (или индекс) сообщения;

3)объявить метод обработки нового сообщения в классе, который должен его обрабатывать;

4)иншщализировать (передать) сообщение.

Сообщения Delphi. В Delphi определено около 100 стандартных типов сообщений. В соответствии с правилами Windows сообщение состоит из нескольких полей. Первое поле обычно называется Msg. Оно должно содержать индекс сообщения - 16-разрядное целое положительное число (тип Cardinal). Далее следуют поля, содержащие передаваемые значения. Последние поля обьршо используются для записи результата обработки сообщения. Они могут отсутствовать.

Например, основной тип сообщений, используемых в Delphi, определяется следующим образом:

Туре TMessage-record Msg:Cardinal; case Integer of

0:(WParam.'LongInt; LParam.Longlnt; Result:Longint);

1:{WParamLo:Word; WParamHi:Word;

LParamLo: Word; LParamHi: Word; ResultLo.Word; ResultHLWord);

end;

end;

Номер сообщения. Номер (или индекс) сообщения используется для идентификащш сообщения в системе: он определяет вид собыгия, о котором система уведомляет приложение (нажатие клавиш, нажатие кнопок мьппи и т.д.).

При создании собственньгс сообщений следует учитьшать, что номера с О до $399 зарезервированы за системой. Первый свободный номер обозначен константой WMUSER=$400, относительно которой обычно и определяются номера пользовательских сообщений:

Const Mesl = WMJJSER;

Mes2 = WMJJSER+1;

Методы обработки сообщений. Класс, объекты которого должны принимать и обрабатывать некоторые сообщения, должен включать спещ1альные методы обработки этих сообщений. При разработке этих методов необходимо учитьшать спещ^альные правила, существующие в Delplii.

Метод обработки сообщения по умолчанию является динамическим, причем спещ1фикаторы dinamic или override при его описании опускаются:

Procedure wm<имя метода>(уаг Message:<тип сообщения>); message <номер сообщения>;

242

5.7. Создание и обработка сообщений и событий

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

Добавление метода обработки сообщений к классу вьшолняется так же, как и любого другого метода:

type <имя класса>= class <имя класса-родителя> public

Procedure wm<имя метода>(уаг Message:<THn сообщения>); message <номер сообщения>;

end;

Если метод обработки сообщения переопределяет уже существовавший в классе-родителе, то обьршо в нем программируют только специфические действия по обработке сообщения, а затем вызьшают наследуемый метод для вьшолнения дообработки сообщения. Обращение к наследуемому методу при этом осуществляется без указания имени метода (как упоминалось вьппе, эти имена могут быть различны):

procedure <имя класса>. wm<имя метода>; begin

<специальная обработка> inherited;

end;

В том случае, если у объекта для некоторого сообщения не определен соответствующий обработчик, то, в соответствии с правилами подключения динамических методов проверяются таблицы динамических методов базовых классов. Если обработчик некоторого сообщения не определен и в базовых классах, то проводится вызов метода DefanltHandler класса TObject, который обеспечивает «обработку по умолчанию» для этих сообщений.

Генерация сообщения. Для передачи сообщений объектам Delphi могут использоваться несколько способов.

1. Для передачи сообщения оконному элементууправления через очередь сообщений с оэюиданием завершения его обработки используется функция:

function SendMessage (liWnd:Integer, Mes:Cardinal;

WParam, LParam:LongInt):LongInt;

Она возвращает результат обработки сообщения. Параметр hWnd определяет номер, под которым окно - адресат сообщения - зарегистрировано

243

5.Объектная модель Delphi Pascal

ВWindows (дескриптор окна). Для каждого оконного элемента управления этот номер хранится в свойстве Handle, определенном в классе TWinControL

2.Для передачи сообщения оконному элементууправления через очередь сообщений без оэюидания завершения его обработки используется фушщия:

function PostMessage(hWnd:Integer,Mes:Cardinal; WParam,Param:LongInt):LongBool;

Список параметров функщш совпадает со списком SendMessage, но в отличие от SendMessage PostMessage ставит сообщение в очередь сообщений и возвращает управление, не ожидая завершения обработки сообщения. Фушащя возвращает True, если сообщение поставлено в очередь, и False - в противном случае.

3. Для передачи сообщения элементу управления минуя очередь используется специальный метод этого элемента, определенный в классе TControl:

procedure Perform (Mes:Cardinal; WParam, LParam:LongInt);

Данный метод передает сообщение элементу управления непосредственно, поэтому указывать дескриптор окна не надо, и список параметров содержит только параметры, относящиеся к самому сообщению.

Пример 5.8. Передача/прием сообщения. Разработаем приложение, одна из форм которого пересылает некоторый текст другой форме. На рис. 5.20 представлен результат работы такого приложения.

Первая форма должна вводить строку, формировать сообщение, содержащее адрес этой строки, и пересылать его второй форме. Вторая ~ принимать сообщение и высвечивать в спещ1альном элемеьгге управления переданную строку.

Прежде всего, в интерфейсной части модуля Unitl определим тип нового сообщения и константу, определяющую его номер. Далее в классе TForail зарезервируем поле для хранения дескриптора второй формы и организуем пересылку сообщения в очередь по нажатию кнопки SendButton:

J^' Передача сообщения. Н И ЕЗ

Введите сообщение:

fi^

Прием сообщения

B O L X J

 

 

 

Текст сообщения

 

Текст сообщения

 

Передать | |!таедую^ | Выход

 

 

 

Рис. 5.20. Вид окон приложения «Передача/прием сообщений»

244

5.7. Создание и обработка сообщений и событий

Unit Unit1;

Interface

Type MyMessage=Record

Msg:Cardinal; {номер сообщения} PString:PChar; {адрес строки сообщения}

Result:LongInt; {поле для записи результата обработки}

End;

Const WM_MYMESSAGE=WM_USER; {первый номер из диапазона пользовательских номеров}

Туре

TForml = class(TForm)

public SecondHandle:Integer; {переменная для хранения дескриптора второй формы}

end; Implementation

Procedure TForml, SendButtonClick(Sender: TObject); Begin

SendMessage(SecondHandle, WM_MYMESSAGE, Longint(MessageEditText),0);{TQKQpaii^R и пересылка сообщения}

End; . . .

Вторая форма должна принимать и обрабатывать новое сообщение. Соответственно, класс TForm2 должен включать метод обработки этого сообщения. Кроме этого, при создании формы ее дескриптор должен запоминаться в первой форме:

Unit Unit2;

Interface

Uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dia­ logs, StdCtrls, Unitl{содержит описание типа сообщения};

Type TForm2 = class(TForm) MessageEdit: TEdit;

procedure FormCreate(Sender: TObject); public

Procedure WMMyMessage(var Msg:MyMessage);

MESSAGE WM_MYMESSAGE;{метод обработки сообщения}

end;

Var Form2: TForm2; Implementation

{$R *DFM}

Procedure TForm2.FormCreate(Sender: TObject); Begin

245

5. Объектная модель Delphi Pascal

Forml.SecondHandle:=Handle; {запомнить дескриптор окна второй формы}

End;

Procedure TForm2. WMMyMessage(var Msg:MyMessage);

Begin

MessageEdit. Text: =Msg.PString; (вывести сообщение} End;

End.

Аналогично для передачи сообщения можно было бы использовать функщпо PostMessage:

PostMessage(SecondHandle, WM_MYMESSAGE, Longint(MessageEdit. Text), 0);

Если с той же целью использовать метод Perform, то необходимо

1) в секщш реализащш модуля Unitl разрешить использование модуля Unit2:

Uses Unit2;

2) для передачи сообщеьшя использовать метод объекта Form2:

Form2.Perform (WM_MYMESSAGE,Longint(MessageEdit. Text\0);

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

обработчиков событий.

Как уже говорилось, собьггие реализуется как свойство процедурного типа. Соответственно, определив событие, мы должны предусмотреть его обработчик. В Delphi все события обычно имеют имена, начинающиеся с префикса «On»: OnClick, OnCreate и т.д. Для проверки наличия обработчика события используется специальная функция

function Assigned(var Р): Boolean;

Эта функция проверяет, присвоено ли какое-либо значение переменной процедурного типа. Функция возвращает True, если присвоено, и False - в противном случае.

Пример 5.9. Создание события. Создадим событие в обработчике сообщения, рассмотренном в предьщущем примере. Модульит11 при этом не изменится.

В модуле Unit2 определим тип обработчика события TPCharEvent, получающего два параметра: стандартный параметр Sender - адрес объекта-

246

5.7. Создание и обработка сообщений и событий

инициатора события и специальный параметр MyString - адрес строки, пересылаемый в сообщении. Затем объявим собыгие OnPChar и метод - обработчик этого собыгия PCharProc. Подключение метода - обработчика события вьшолним при создании формы FormCreate.

Unit Unit2;

Interface

Uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dia­ logs, StdCtrls, Unitl;

TypeTPCharEvent=procedure(Sender: TObject;MyString:PChar) of object; TForm2 = class(TForm)

MessageEdit: TEdit;

procedure FormCreate(Sender: TObject); private

FOnPChar:TPCharEvent; {поле процедурного типа} public

property OnPChar:TPCharEvent read FOnMessage

write FOnMessage; {свойство-собьггие OnPChar }

Procedure PCharProc(Sender: TObject; MyString:PChar);

{обработчик собыгия OnPChar}

Procedure WMMyMessage(var Msg.MyMessage); MESSAGE WM_MYMESSAGE;

end;

Var Form!: TForml; Implementation

{$R *DFM}

Procedure TForm2.FormCreate(Sender: TObject); Begin

Forml .SecondHandle: =Handle;

OnPChar:- PCharProc; {подключение обработчика события}

End;

Procedure TForm2. WMMyMessage(var Msg:MyMessage); Begin

ifAssigned(OnPChar) then {если обработчик события определен, то}

OnPChar (Self Msg.PString); {вьшолнитъ его}

End;

Procedure TForm2.PCharProc(Sender: TObject;MyString:PChar); Begin MessageEdit.Text:=MyString; End;

End.

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

247

5. Объектная модель Delphi Pascal

Например, сообщение WMKeyDoun, переданное оконному управляющему элементу класса TEdit, принадлежащему некоторой форме, обрабатьгоается следующим образом (рис. 5.21).

Сначала сообщеьше WMKeyDoun поступает в приложение. Получив это сообщение, приложение генерирует сообщение CnKeyDoun для элемента управления редактированием. Получив это сообщение элемент управления редактированием генерирует сообщерше CmKeyDoun сначала самому себе, а затем передает его элементу родителю, пока оно не будет передано форме. Если форма возвращает О (т.е. отказывается обрабатывать это сообщение), то элемент управления редактированием возвращает О сам себе, а затем О - приложеюпо.

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

Управление циклом обработки сообщений. Цикл обработки сообщений приложения в Delphi скрыт в методе Run класса TApplication. Это метод получает управление после иншщализащш приложения и построения его окон и выполняется следующим образом. Сообщение, извлеченное из очереди методом HandleMessage, передается соответствующему оконному элементу управления для обработки. После завершения обработки сообщения из очереди извлекается следующее сообщение, и т.д., до получения сообщения о

 

Инициализащ1Я внутренних сообщений

 

WMKeyDoum

Прило-

 

Cn_KeyDoun^[ Элемент

Cm ChildKe

^

жение

|WM_KeyDoim^[управления ре-

•Щ Форма

 

 

 

[дактированием

 

 

Диаграмма внутренних сообщений

 

LWM_KeyDoun

 

 

Приложение

Cn_KeyDoun

 

 

Элемент

 

 

 

 

управления

 

 

 

 

редактированием

Cm_ChildKey

 

 

WM_KeyDoun

Cm_ChildKey

го

 

 

 

 

 

Форма

 

Рис. 5.21. Генерация внутренних сообщений

248

5.7. Создание и обработка сообщений и событий

прекращении работы приложения WMQUIT. При обработке сообщения WMQUIT свойство Terminated устанавливается равным True и цикл обработки сообщений завершается:

... repeat HandleMessage until Terminated;...

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

Для отображения длительной обработки используют специальные элементы TProgressBar или анимацию. Изменения в окне приложения показывают пользователю, что оно вьшолняет какие-то действия.

Для того чтобы приложение получило возможность обрабатывать сообщение из очереди, оно должно в цикле длительной обработки периодически вызьюать метод ProcessMessages класса TAplication. Этот метод прерывает вьшолнение приложения для выборки сообщений из очереди, после чего возвращает управление прерванной программе.

Для прекращения длительной обработки приходится использовать специальные приемы, так как просто инициация сообщения WMQUIT (например, при нажатии кнопки завершения приложения) не приводит к завершению приложения до окончания обработки. Это связано с тем, что при вызове ProcessMessages не происходит возврата в цикл обработки сообщений и, следовательно, не анализируется значение свойства Termiinated, устанавливаемое при нажатии кнопки завершения приложения.

Пример 5.10. Прерывание длительной обработки. Рассмотрим при­ ложение, которое выводит в окно некоторые числа. Процесс вывода чисел отоб­ ражается специальным элементом класса TProgressBar, который показьгоает, какая часть процесса уже завершена. При желании пользователь может оста­

новить процесс нажатием

специ­

 

 

альной кнопки Прервать (рис. 5.22).

>*•' Прерывание обработки.

Кнопку

Прервать

и

окно

 

 

индика-тора будем создавать

 

 

динамически в процессе выполнения

 

^07201

программы.

Для организации

 

 

прерывания обработки добавим в

 

 

класс TForml

свойство

Cancel,

 

Прервать

которое будет устанавливаться

 

 

 

равным True при нажатии кнопки

Начать

Выход

Прервать. Проверка этого свойства

 

 

позволит организовать досрочный

Рис. 5.22. Вид главного окна приложения

выход из цикла по желанию

пользователя.

 

 

 

«Прекращение длительной обработки»

249