Одна из ключевых целей среды
визуального программирования -
скрыть от пользователя сложность
программирования в Windows. При этом,
однако, хочется, чтобы такая среда
не была упрощена слишком, не до
такой степени, что программисты
потеряют доступ к самой
операционной системе.
Программирование,
ориентированное на события -
неотъемлемая черта Windows. Некоторые
программные среды для быстрой
разработки приложений (RAD) пытаются
скрыть от пользователя эту черту
совсем, как будто она настолько
сложна, что большинство не могут ее
понять. Истина заключается в том,
что событийное программирование
само по себе не так уж сложно.
Однако, есть некоторые особенности
воплощения данной концепции в Windows,
которые в некоторых ситуациях
могут вызвать затруднения.
Delphi предоставляет полный доступ к
подструктуре событий,
предоставляемой Windows. С другой
стороны, Delphi упрощает
программирование обработчиков
таких событий.
В данном уроке приводится
несколько примеров того, как
обрабатывать события в Delphi, дается
более детальное объяснение работы
системы, ориентированной на
события.
События в Delphi
Объекты из библиотеки визуальных
компонент (VCL) Delphi, равно как и
объекты реального мира, имеют свой
набор свойств и свое поведение -
набор откликов на события,
происходящие с ними. Список событий
для данного объекта, на которые он
реагирует, можно посмотреть,
например, в Инспекторе Объектов на
странице событий. (На самом деле, на
этой странице представлен список
свойств, которые имеют тип вроде
TMouseMoveEvent и представляют из себя
процедуры-обработчики событий.
Существует соглашение по названиям
данных свойств. Например, OnDblClick
соответствует двойному щелчку
мыши, а OnKeyUp - событию, когда нажатая
клавиша была отпущена.) Среди
набора событий для различных
объектов из VCL есть как события,
портируемые из Windows (MouseMove, KeyDown), так
и события, порождаемые
непосредственно в программе (DataChange
для TDataSource).
Поведение объекта определяется
тем, какие обработчики и для каких
событий он имеет. Создание
приложения в Delphi состоит из
настройки свойств используемых
объектов и создания обработчиков
событий.
Простейшие события, на которые
иногда нужно реагировать - это,
например, события, связанные с
мышкой (они есть практически у всех
видимых объектов) или событие Click
для кнопки TButton. Предположим, что вы
хотите перехватить щелчок левой
кнопки мыши на форме. Чтобы сделать
это - создайте новый проект, в
Инспекторе Объектов выберите
страницу событий и сделайте
двойной щелчок на правой части для
свойства OnClick. Вы получите
заготовку для обработчика данного
события:
procedure TForm1.FormClick(Sender: TObject);
begin
end;
Напишите здесь следующее:
procedure TForm1.FormClick(Sender: TObject);
begin
MessageDlg('Hello', mtInformation, [mbOk], 0);
end;
Каждый раз , когда делается щелчок
левой кнопки мыши над формой будет
появляться окно диалога (см. рис.1).
Рис.1: Диалог, появляющийся при щелчке мыши на форме.
Код, приведенный выше,
представляет из себя простейший
случай ответа на событие в
программе на Delphi. Он настолько
прост, что многие программисты
могут написать такой код и без
понимания того, что они на самом
деле отвечают на сообщение о
событии, посланное им операционной
системой. Хотя программист
получает это событие через третьи
руки, тем не менее он на него
отвечает.
Опытные программисты в Windows знают,
что при возникновении события,
операционная система передает вам
не только уведомление о нем, но и
некоторую связанную с ним
информацию. Например, при
возникновении события “нажата
левая кнопка мыши” программа
информируется о том, в каком месте
это произошло. Если вы хотите
получить доступ к такой информации,
то должны вернуться в Инспектор
Объектов и создать обработчик
события OnMouseDown:
procedure TForm1.FormMouseDown(Sender: TObject;
Button: TMouseButton;
Shift: TShiftState;
X, Y: Integer);
begin
Canvas.TextOut(X, Y, 'X='+IntToStr(X)+' Y='+IntToStr(Y));
end;
Запустите программу, пощелкайте
мышкой на форме:
Рис.2
Как видите, в Delphi очень просто
отвечать на события. И не только на события, связанные с
мышкой. Например, можно создать
обработчик для OnKeyDown (нажата
клавиша):
procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;Shift: TShiftState);
begin
MessageDlg(Chr(Key), mtInformation, [mbOk], 0);
end;
Теперь, когда вы имеете начальные
знания о программировании событий
в Delphi, самое время вернуться назад и
посмотреть на теорию, стоящую за
тем кодом, что вы написали. После
получения представления о том, как
работает система, можно вернуться к
среде Delphi и посмотреть, как
использовать полностью имеющиеся
возможности.
Понимание событий
Событийное программирование есть
не только в Windows, и данную черту
можно реализовать не только в
операционной системе. Например,
любая DOS программа может быть
основана на простом цикле,
работающем все время жизни
программы в памяти. Ниже вы найдете
гипотетический пример, как данный
код может выглядеть:
begin
InitializeMemory;
repeat
CheckForMouseEvent(Events);
CheckForKeyPress(Events)
HandleEvents(Events);
until Done := True;
DisposeMemory;
end.
Это типичный пример программы,
ориентированной на события. Она
начинается и заканчивается
инициализацией и освобождением
памяти. В программе присутствует
простой цикл repeat..until, который
проверяет появление событий от
мыши и клавиатуры и затем дает
возможность программисту ответить
на эти события.
Переменная Events может быть записью
с простой структурой:
TEvent = record
X, Y: Integer;
MouseButton: TButton;
Key: Word;
end;
Тип TButton, указанный выше, можно
декларировать так:
TButton = (lButton, rButton);
Эти структуры позволяют вам
проследить, где находится мышь,
каково состояние ее кнопок, и
значение нажатой клавиши на
клавиатуре. Конечно, это пример
очень простой структуры, но
заложенные здесь принципы отражают
то, что происходит внутри Windows или
внутри других систем,
ориентированных на события, вроде
Turbo Vision. Если бы программа,
приведенная выше, была редактором
текста, то обработчик HandleEvent для
такой программы мог бы иметь вид:
procedure HandleEvent(Events: TEvent);
begin
case Events.Key of
’A’..’z’: Write(Events.Key);
EnterKey: Write(CarriageReturn);
EscapeKey: Done := True;
end;
end;
Согласно коду выше, программа
будет печатать букву ‘a’ при
нажатии этой клавиши и перейдет на
новую строку, если нажата клавиша
‘Enter’. Нажатие ‘Esc’ приведет к
завершению программы.
Код такого типа может быть очень
удобным, в частности, если вы пишете
программу, в которой требуется
анимация. Например, если нужно
перемещать несколько картинок по
экрану, то может понадобиться
сдвинуть их на несколько точек,
затем проверить, нажимал ли
пользователь кнопки. Если такое
событие было, то его можно
обработать, если нет, то двигать
дальше.
Надеюсь, что приведенный пример
дает некоторое понимание работы
ориентированной на события
системы. Единственное, что осталось
пропущенным - почему система Windows
так спроектирована.
Одной из основных причин, почему
Microsoft сделал Windows по такой схеме,
является тот факт, что множество
задач работает в среде
одновременно. В многозадачных
системах операционная система
должна знать, щелкнул ли
пользователь мышкой на
определенное окно. Если это окно
было частично перекрыто другим, то
это становится известно
операционной системе и она
перемещает окно на передний план.
Понятно, что неудобно заставлять
само окно выполнять эти действия.
Операционной системе лучше
обрабатывать все нажатия клавиш и
кнопок на мыши и затем передавать
их в остальные программы в виде
событий.
Если кратко, программист в Windows
почти никогда не должен напрямую
проверять “железо”. Система
выполняет эту задачу и передает
информацию программе в виде
сообщений.
Когда пользователь щелкает
мышкой, операционная система
обрабатывает это событие и
передает его в окно, которое должно
обработать данное событие.
Созданное сообщение, в этом случае,
пересылается в некую процедуру
DefWindowProc окна (default window procedure). DefWindowProc
- аналог процедуры HandleEvent из
примера, приведенного выше.
Каждое окно в Windows имеет свою
DefWindowProc. Чтобы полностью понять
данное утверждение, представьте,
что каждая кнопка, каждый ListBox,
каждое поле ввода и т.д. на самом
деле являются окнами и имеют свою
процедуру DefWindowProc. Это очень гибкая
и мощная система, но она может
заставить программиста писать
очень сложный код. Delphi дает
возможность быть защищенным от
такой структуры программы.
Почти все, что происходит в Windows
принимает форму сообщений и, если
вы хотите их использовать в Delphi (в
большей мере это необходимо при
написании своих собственных
компонент), то нужно понять, как эти
сообщения работают.
Если посмотреть на DefWindowProc в
справочнике по Windows API, то можно
увидеть следующее определение:
function DefWindowProc(Wnd: HWnd; Msg, wParam: Word; lParam: LongInt);
Каждое сообщение, посылаемое в
окно, состоит из четырех частей:
первая часть - handle окна, получающего
сообщение, Msg сообщает, что
произошло а третья и четвертая
части (wParam и lParam) содержат
дополнительную информацию о
событии. Вместе эти четыре части
являются аналогом показанной выше
структуры TEvent.
Вторая часть сообщения имеет
длину 16 бит и сообщает, что за
событие произошло. Например, если
нажата левая кнопка на мыши, то
переменная Msg содержит значение
WM_LBUTTONDOWN. Существуют десятки
различного типа cообщений и они
называются вроде WM_GETTEXT, WM_HSCROLL,
WM_GETTEXTLENGTH и т.п. Список всех
сообщений можно видеть в
справочнике по Windows API (on-line help).
Последние две переменные, длиной
16 и 32 бита, называются wParam и lParam. Они
сообщают программисту важную
дополнительную информацию о каждом
событии. Например при нажатии
кнопки мыши, lParam содержит
координаты указателя мыши.
Одна из хитростей заключается в
том, как выделить нужную информацию
из этих переменных. В большинстве
случаев Delphi освобождает вас от
необходимости выполнять данную
задачу. Например, в обработчике
события OnMouseDown для формы вы просто
используете координаты X и Y. Как
программисту, вам не нужно
прилагать усилия для получения
сообщения и связанных с ним
параметров. Все, что связано с
событиями, представлено в простом и
непосредственном виде:
procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
Итак, если подвести итог, то
должно стать ясным следующее:
Windows является системой
ориентированной на события;
События в Windows принимают форму
сообщений;
В недрах VCL Delphi сообщения Windows
обрабатываются и
преобразуются в более простую
для программиста форму;
Обработка событий в Delphi
сводится к написанию для
каждого объекта своих
обработчиков;
События в программе на Delphi
вызываются не только
сообщениями Windows, но и
внутренними процессами.
Обработка сообщений Windows в Delphi
Конечно, нельзя придумать такую
библиотеку объектов, которые бы
полностью соответствовали
потребностям программистов. Всегда
возникнет необходимость
дополнения или изменения свойств и
поведения объектов. В этом случае,
так же, как и при создании своих
собственных компонент в Delphi, часто
требуется обрабатывать сообщения
Windows. Поскольку Object Pascal является
развитием и продолжением Borland Pascal
7.0, то это выполняется сходным с BP
способом.
Общий синтаксис для декларации
обработчика сообщений Windows:
procedure Handler_Name(var Msg : MessageType);
message WM_XXXXX;
Handler_Name обозначает имя метода; Msg -
имя передаваемого параметра; MessageType
- какой либо тип записи, подходящий
для данного сообщения; директива
message указывает, что данный метод
является обработчиком сообщения;
WM_XXXXX - константа или выражение,
которое определяет номер
обрабатываемого сообщения Windows.
Рассмотрим обработку сообщений
на примере. Например, при нажатии
правой кнопки мыши на форме в
программе появляется всплывающее
меню (pop-up menu, если оно было
привязано к этой форме).
Программист может захотеть
привязать к правой кнопке
какое-нибудь другое событие. Это
можно сделать так:
type
TForm1 = class(TForm)
PopupMenu1: TPopupMenu;
MenuItem1: TMenuItem;
MenuItem2: TMenuItem;
MenuItem3: TMenuItem;
private
{ Private declarations }
procedure WMRButtonDown(var Msg : TWMMouse); message WM_RBUTTONDOWN;
public
{ Public declarations }
end;
Подчеркнут код, добавленный в
декларацию объекта TForm1 вручную.
Далее, в секции implementation нужно
написать обработчик:
procedure TForm1.WMRButtonDown(var Msg : TWMMouse);
begin
MessageDlg('Right mouse button click.', mtInformation, [mbOK],0);
end;
В данном случае при нажатии
правой кнопки мыши будет
появляться диалог.
Вообще-то, у класса TForm уже есть
унаследованный от дальнего предка
обработчик данного события,
который называется точно также и
вызывает то самое pop-up меню. Если в
новом обработчике сообщения нужно
выполнить действия, которые
производились в старом, то для
этого применяется ключевое слово
inherited. Если слегка модифицировать
наш обработчик, то после диалога
будет появляться pop-up меню:
procedure TForm1.WMRButtonDown(var Msg : TWMMouse);
begin
MessageDlg('Right mouse button click.', mtInformation, [mbOK],0);
inherited;
end;
Однако, есть еще способ обработки
всех сообщений, которые получает
приложение. Для этого используется
свойство OnMessage объекта Application,
который автоматически создается
при запуске программы. Если
определен обработчик события OnMessage,
то он получает управление при любом
событии, сообщение о котором
направлено в программу. Следующий
код будет приводить к появлению
диалога при двойном щелчке мыши на
любом объекте в приложении.
procedure TForm1.FormCreate(Sender: TObject);
begin
Application.OnMessage:=AOM;
end;
procedure TForm1.AOM(var Msg: TMsg; var Handled: Boolean);
begin
Handled:=False;
if Msg.Message = WM_LBUTTONDBLCLK then begin
MessageDlg('Double click.', mtInformation, [mbOK], 0);
Handled:=True;
end;
end;
Конечно, в обработчике нельзя
выполнять операции, требующие
длительного времени, поскольку это
приведет к замедлению выполнения
всего приложения.
[ Предыдущий урок | Содержание | Следующий урок ]
|