События в Delphi

События в программировании, как и события реального мира - это какие-либо операции, происходящие в установленное время. Установленное время - это не конкретное время с точки зрения часов, это просто отклики на какие-то другие действия и изменения. События присущи объектно-ориентированному программированию и являются одной из важных черт операционной системы Windows. Работа программ с визуальным (графическим) интерфейсом немыслима без событий. Delphi предоставляет полный доступ для работы с событиями и немного упрощает программирование обработчиков событий. Целью урока является знакомство с наиболее часто используемыми событиями, а также с приёмами написания обработчиков для них.

Где найти события?

В одном из первых уроков было упомянуто, что каждый объект имеет свойства и события и всё это доступно через Object Inspector . Список всех событий, которые поддерживает объект, находится на вкладке Events Инспектора Объектов. Формат данных аналогичен закладке со свойствами: слева указывается название события, а справа - обработчик, который присвоен событию. Обратите внимание, что всем событиям обычно даются названия, начинающиеся со слова "On " (англ. - "на "). Это хороший тон и довольно устоявшаяся традиция. Такие названия позволяют быстро отличить события от свойств. По аналогии со свойствами, событиям дают вполне логичные имена. Увидев незнакомое событие, в 95% случаев только по названию можно догадаться, когда событие происходит. Если же событие не совсем понятно, либо хочется больше узнать о нём, можно воспользоваться встроенной справочной системой Delphi . Для этого нужно установить курсор на строку с интересующим событием и просто нажать F1 - справочная система откроется на странице с информацией о выбранном событии (если, конечно, оно описано в справке). Для всех стандартных компонент в справочной системе информации предостаточно. Если используется какой-либо сторонний компонент, то описание следует искать в документации, которая идёт вместо с самим компонентом. Некоторые компоненты (в основном - крупные пакеты из большого числа компонент) самостоятельно интегрируют свою документацию в справочную систему Delphi при установке.

Автоматическое создание обработчиков событий

Обработчик события - это набор команд, которые выполняются при вызове события. Создавать обработчики можно двумя способами: прямо в среде Delphi - автоматически, и программным путём, т.е. во время выполнения программы. Создать обработчик очень просто: достаточно дважды щёлкнуть по полю рядом с названием события в Инспекторе Объектов и откроется редактор кода с заготовкой обработчика. Остаётся лишь написать требуемый код между begin и end (курсор уже будет стоять в том месте) и обработчик готов. Как видите, всё довольно просто и быстро. Однако в предыдущих уроках Вы видели, что обработчик создавался и просто при двойном щелчке по объекту на форме... Да, действительно, в этом случае тоже создаётся обработчик. Но этот способ ничем не отличается от только что описанного, просто для каждого объекта задано событие по умолчанию, для которого создаётся обработчик при двойном щелчке. Как правило, это самые используемые события. Например, для кнопки это конечно же будет щелчок по ней. Для нестандартных компонент, если событие по умолчанию не обозначено, берётся первое попавшееся из всех, а если событие всего одно, то выбор тем более очевиден.
После того, как обработчик создан, на вкладке Events рядом с названием события появится название обработчика для него. Это название также можно вписать и вручную или выбрать из списка, в котором содержатся названия всех обработчиков. Имена обработчикам даются также не случайно: берётся имя объекта и к нему дописывается название события. Например, если есть кнопка Button1 и её событие OnClick , то обработчик получит название Button1Click . Опять-таки, по имени обработчика можно догадаться о событии, к которому он относится.

Событие может иметь только один обработчик! Тем не менее, если требуется задать несколько обработчиков, можно сделать следующим образом: единственный указанный обработчик просто программно запускает другие обработчики. Такой приём часто используется.
Однако, обратного запрета не существует: один обработчик может быть привязан к разным событиям объекта и даже к разным объектам . Для этого нужно в строке интересующего события у объекта выбрать имя обработчика из списка.

Обработчик с точки зрения программного кода

Теперь посмотрим, что из себя представляет обработчик в программном коде. А представляет он из себя процедуру, т.е. набор команд и является мини-программой.
Обработчик содержит раздел реализации - он расположен между begin и end (на рисунке выделен рамкой цвета ). В этом блоке, как Вы уже знаете, следует писать код, который и будет выполнен.
Самая верхняя строка обработчика - это его заголовок (выделен цветом ). Элементы заголовка:
- Ключевое слово procedure (процедура);
- Имя объекта, которому принадлежит данная процедура (в данном случае она принадлежит форме - поэтому TForm1 );
- Имя обработчика (цвет );
- Переданные параметры (цвет ).

Между именем объектам (TForm1 ) и именем обработчика (Button1Click ) находится точка - это символ указания на то, что Button1Click принадлежит TForm1 . При работе с любыми структурами точка указывает на принадлежность элемента этой структуре. Сейчас это не играет большой роли - просто небольшое отступление. К примеру, в языке C++ таким разделителем служит комбинация "минус и знак больше": ->

Раздел описаний здесь тоже присутствует, просто он является пустым. Как и положено, расположен он между заголовком обработчика и разделом реализации, т.е. между первой и второй строкой. При необходимости объявления переменных или констант этот раздел описывается обычным образом - добавление ключевого слово var или const и описанием переменных или констант.


Пример обработки событий №1

procedure
begin
ShowMessage("Нажата Button1" )
end ;

procedure TForm1.Button2Click(Sender: TObject);
begin
ShowMessage("Нажата Button2" )
end ;

procedure TForm1.Button3Click(Sender: TObject);
begin
ShowMessage("Нажата Button3" )
end ;

Конечно, работать будет... Но настоящие программисты так не делают... Судите сами: а если бы было 100 кнопок - что тогда? 100 обработчиков? Конечно нет!

Можно создать один обработчик сразу для всех кнопок. Выше было сказано, что это можно сделать, если выбрать в Инкспекторе Объектов обработчик события из списка... Но в случае, если 100 кнопок, такой способ тоже непригоден - уйдёт много времени. Есть ещё один: если выбрать на форме сразу несколько объектов, то в Инспекторе Объектов среди свойств и событий останутся только те, которые есть у всех выбранных объектов. Этим и следует воспользоваться.

Итак, выбираем на форме все 3 кнопки (очерчивая область мышью, либо удерживая Shift), далее переходим на вкладку Events в ИО и создаём обработчик события OnClick . Что происходит: открывается редактор кода, но создаётся только один обработчик, хотя он присваивается всем трём кнопкам.

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

procedure TForm1.Button1Click(Sender: TObject);
begin
ShowMessage("Нажата " +(Sender as TButton).Name)
end ;

Как именно это работает и что делает оператор as сейчас объяснить довольно сложно. Всему своё время. Пример создан с целью показать, что дублирования всегда можно каким-то способом избежать.

Параметры, передаваемые обработчикам

У событий есть параметры. У многих событий параметр один, но у некоторых их бывает и больше - зависит от типа события. Из переданных параметров можно узнать дополнительные данные и условия, при которых произошло событие. Например, если мы знаем, что пользователь передвинул курсор мыши, это ещё ни о чём не говорит и полезной информации практически не несёт. А вот если мы знаем, что курсор был передвинут в точку экрана с некоторыми известными координатами (x;y) - это уже что-то.
Параметр, который встречается практически во всех событиях - Sender . Он указывает на объект, который данное событие инициировал. Использовать его удобно в случае с несколькими объектами, у которых установлен один обработчик одного и того же события (как в нашем примере №1).

События

Вот теперь, когда мы достаточно много знаем о событиях, можно наконец рассмотреть стандартные и наиболее часто используемые из них.

OnClick - щелчок мышью по объекту. У кнопки событие также срабатывает, если нажать Enter или Пробел в тот момент, когда фокус ввода находится на кнопке.

OnDblClick - двойной щелчок мышью.

OnContextPopup - при вызове контекстного меню объекта, т.е. при щелчке правой кнопкой мыши. Среди параметров есть MousePos - координаты курсора в момент щелчка. Координата по X доступна как MousePos.X , а по Y - как MousePos.Y .

OnEnter - момент, когда объект получает фокус ввода (фокус ввода обычно изображается пунктирной рамкой на объекте; в поле ввода - это курсор; фокус один на всё приложение, т.е. работать одновременно можно лишь с одним объектом).

OnExit - момент, когда объект теряет фокус ввода.

OnMouseDown - при нажатии кнопки мыши (не обязательно левой). Параметр Button - нажатая кнопка (mbLeft - левая кнопка, mbRight - правая, mbMiddle - средняя). Shift - множество, указывающее, какие функциональные клавиши были зажаты при щелчке. Таким образом можно отслеживать, например, нажатия при зажатых Ctrl +Alt и т.п. X , Y - координаты курсора во время нажатия (относительно левого верхнего угла самого компонента, а не формы!).

OnMouseUp - событие, аналогичное OnMouseDown . Происходит при отпускании кнопки мыши. Пример комбинации этих двух событий - графический редактор: когда кнопка мыши нажата, происходит рисование, а когда отпущена - не происходит.

OnMouseMove - ещё одно событие мыши, происходящее при перемещении курсора над объектом. X , Y - координаты нового положения, Shift - множество нажатых функциональных клавиш.

OnKeyDown - при нажатии какой-либо клавиши клавиатуры в тот момент, когда фокус ввода находится на объекте. Key - код нажатой клавиши, Shift - всё то же множество функциональных клавиш (этот параметр встречается во многих событиях).

OnKeyUp - при отпускании клавиши (антипод OnKeyDown ).

OnKeyPress - при нажатии клавиши, которая печатает какой-либо символ (буква, цифра, знак). Key - уже не код клавиши, а сам символ (тип данных: Char - один символ).

OnResize - при изменении размеров объекта.

OnPaint - при отрисовке объекта на экране (например, формы).

У самой формы также есть множество событий. Отметим некоторые из них:

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

OnClose - при закрытии формы.

OnCloseQuery - при попытке закрыть форму. Это событие можно использовать, если нужно заблокировать закрытие формы. В обработчике присутствует параметр CanClose (англ. - "можно закрыть "): логическое значение, тип данных - Boolean . Если после выполнения обработчика значение переменной CanClose окажется False , то закрытие формы произведено не будет. Значение этой переменной устанавливается программно. Пример использования этого события - текстовый редактор. Если пользователь ввёл текст, но не сохранил его в файл, при выходе из программы нужно спросить, следует ли сохранить изменения.

Пример обработки событий №2

Цель: запретить "прямое" закрытие формы, а спрашивать, действительно ли пользователь хочет закрыть программу. Используем событие формы OnCloseQuery . В обработчике выведем диалоговое окно и, если пользователь ответит "Нет ", заблокируем закрывание. Для вывода окна диалога воспользуемся функцией MessageDlg . Подробнее о ней и о её параметрах Вы узнаете в одном из следующих уроков. Наш обработчик:

procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
if MessageDlg("Вы действительно хотите выйти?" ,mtWarning,,0) = mrNo then
CanClose:= False
end ;

Проверяемое условие - нажата ли кнопка "Нет ". Если она нажата, блокирум закрытие присвоением переменной CanClose значения False . В результате при закрытии увидим следующее:


События (продолжение)

OnDestroy - при уничтожении формы (после закрытия форма уничтожается и освобождается занятая ей память).

OnShow - при показе формы на экран.

OnHide - при скрытии формы с экрана.

OnActivate - момент, когда форма становится активной, т.е. когда ей передаётся фокус ввода (например, когда пользователь щёлкнул по форме).

OnDeactivate - соответственно, когда форма теряет фокус.

OnChange - при изменении чего-либо (например, у поля ввода TEdit это событие срабатывает, когда изменяется текст в этом поле).

OnDragDrop , OnDragOver , OnEndDock , OnEndDrag , OnStartDock , OnStartDrag - все эти события связаны с технологией Drag&Drop (т.е. когда объект "захватывается" мышью и переносится в другое место), а также с Dock -технологией. Суть этой (Dock) технологии во встраивании одних объектов в другие. Например, если взять Object Inspector за заголовок и переместить на нижнюю часть Object TreeView , а затем отпустить, то окна объединятся, а если переместить его в центр Object TreeView , то закладок станет 3 (Object TreeView станет третьей закладкой).

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

Заключение

Мы рассмотрели работу с событиями - одни из принципов объектно-ориентированного программирования. Таким образом, разработка визуальной программы для Windows сводится к написанию обработчиков на события, в которых описывается взаимодействие объектов друг с другом.

События в программировании, как и события реального мира - это какие-либо операции, происходящие в установленное время. Установленное время - это не конкретное время с точки зрения часов, это просто отклики на какие-то другие действия и изменения. События присущи объектно-ориентированному программированию и являются одной из важных черт операционной системы Windows. Работа программ с визуальным (графическим) интерфейсом немыслима без событий. Delphi предоставляет полный доступ для работы с событиями и немного упрощает программирование обработчиков событий. Целью урока является знакомство с наиболее часто используемыми событиями, а также с приёмами написания обработчиков для них.

Где найти события?

В одном из первых уроков было упомянуто, что каждый объект имеет свойства и события и всё это доступно через Object Inspector . Список всех событий, которые поддерживает объект, находится на вкладке Events Инспектора Объектов. Формат данных аналогичен закладке со свойствами: слева указывается название события, а справа - обработчик, который присвоен событию. Обратите внимание, что всем событиям обычно даются названия, начинающиеся со слова "On " (англ. - "на "). Это хороший тон и довольно устоявшаяся традиция. Такие названия позволяют быстро отличить события от свойств. По аналогии со свойствами, событиям дают вполне логичные имена. Увидев незнакомое событие, в 95% случаев только по названию можно догадаться, когда событие происходит. Если же событие не совсем понятно, либо хочется больше узнать о нём, можно воспользоваться встроенной справочной системой Delphi. Для этого нужно установить курсор на строку с интересующим событием и просто нажать F1 - справочная система откроется на странице с информацией о выбранном событии (если, конечно, оно описано в справке). Для всех стандартных компонент в справочной системе информации предостаточно. Если используется какой-либо сторонний компонент, то описание следует искать в документации, которая идёт вместо с самим компонентом. Некоторые компоненты (в основном - крупные пакеты из большого числа компонент) самостоятельно интегрируют свою документацию в справочную систему Delphi при установке.

Автоматическое создание обработчиков событий

Обработчик события - это набор команд, которые выполняются при вызове события. Создавать обработчики можно двумя способами: прямо в среде Delphi - автоматически, и программным путём, т.е. во время выполнения программы. Создать обработчик очень просто: достаточно дважды щёлкнуть по полю рядом с названием события в Инспекторе Объектов и откроется редактор кода с заготовкой обработчика. Остаётся лишь написать требуемый код между begin и end (курсор уже будет стоять в том месте) и обработчик готов. Как видите, всё довольно просто и быстро. Однако в предыдущих уроках Вы видели, что обработчик создавался и просто при двойном щелчке по объекту на форме... Да, действительно, в этом случае тоже создаётся обработчик. Но этот способ ничем не отличается от только что описанного, просто для каждого объекта задано событие по умолчанию, для которого создаётся обработчик при двойном щелчке. Как правило, это самые используемые события. Например, для кнопки это конечно же будет щелчок по ней. Для нестандартных компонент, если событие по умолчанию не обозначено, берётся первое попавшееся из всех, а если событие всего одно, то выбор тем более очевиден.
После того, как обработчик создан, на вкладке Events рядом с названием события появится название обработчика для него. Это название также можно вписать и вручную или выбрать из списка, в котором содержатся названия всех обработчиков. Имена обработчикам даются также не случайно: берётся имя объекта и к нему дописывается название события. Например, если есть кнопка Button1 и её событие OnClick , то обработчик получит название Button1Click . Опять-таки, по имени обработчика можно догадаться о событии, к которому он относится.

Событие может иметь только один обработчик! Тем не менее, если требуется задать несколько обработчиков, можно сделать следующим образом: единственный указанный обработчик просто программно запускает другие обработчики. Такой приём часто используется.
Однако, обратного запрета не существует: один обработчик может быть привязан к разным событиям объекта и даже к разным объектам . Для этого нужно в строке интересующего события у объекта выбрать имя обработчика из списка.

Обработчик с точки зрения программного кода

Теперь посмотрим, что из себя представляет обработчик в программном коде. А представляет он из себя процедуру, т.е. набор команд и является мини-программой.
Обработчик содержит раздел реализации - он расположен между begin и end (на рисунке выделен рамкой цвета ). В этом блоке, как Вы уже знаете, следует писать код, который и будет выполнен.
Самая верхняя строка обработчика - это его заголовок (выделен цветом ). Элементы заголовка:
- Ключевое слово procedure (процедура);
- Имя объекта, которому принадлежит данная процедура (в данном случае она принадлежит форме - поэтому TForm1 );
- Имя обработчика (цвет );
- Переданные параметры (цвет ).

Между именем объектам (TForm1 ) и именем обработчика (Button1Click ) находится точка - это символ указания на то, что Button1Click принадлежит TForm1 . При работе с любыми структурами точка указывает на принадлежность элемента этой структуре. Сейчас это не играет большой роли - просто небольшое отступление. К примеру, в языке C++ таким разделителем служит комбинация "минус и знак больше": ->

Раздел описаний здесь тоже присутствует, просто он является пустым. Как и положено, расположен он между заголовком обработчика и разделом реализации, т.е. между первой и второй строкой. При необходимости объявления переменных или констант этот раздел описывается обычным образом - добавление ключевого слово var или const и описанием переменных или констант.


Пример обработки событий №1

procedure TForm1.Button1Click(Sender: TObject);
begin
ShowMessage("Нажата Button1" )
end ;

procedure TForm1.Button2Click(Sender: TObject);
begin
ShowMessage("Нажата Button2" )
end ;

procedure TForm1.Button3Click(Sender: TObject);
begin
ShowMessage("Нажата Button3" )
end ;

Конечно, работать будет... Но настоящие программисты так не делают... Судите сами: а если бы было 100 кнопок - что тогда? 100 обработчиков? Конечно нет!

Можно создать один обработчик сразу для всех кнопок. Выше было сказано, что это можно сделать, если выбрать в Инкспекторе Объектов обработчик события из списка... Но в случае, если 100 кнопок, такой способ тоже непригоден - уйдёт много времени. Есть ещё один: если выбрать на форме сразу несколько объектов, то в Инспекторе Объектов среди свойств и событий останутся только те, которые есть у всех выбранных объектов. Этим и следует воспользоваться.

Итак, выбираем на форме все 3 кнопки (очерчивая область мышью, либо удерживая Shift), далее переходим на вкладку Events в ИО и создаём обработчик события OnClick . Что происходит: открывается редактор кода, но создаётся только один обработчик, хотя он присваивается всем трём кнопкам.

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

procedure TForm1.Button1Click(Sender: TObject);
begin
ShowMessage("Нажата " +(Sender as TButton).Name)
end ;

Как именно это работает и что делает оператор as сейчас объяснить довольно сложно. Всему своё время. Пример создан с целью показать, что дублирования всегда можно каким-то способом избежать.

Параметры, передаваемые обработчикам

У событий есть параметры. У многих событий параметр один, но у некоторых их бывает и больше - зависит от типа события. Из переданных параметров можно узнать дополнительные данные и условия, при которых произошло событие. Например, если мы знаем, что пользователь передвинул курсор мыши, это ещё ни о чём не говорит и полезной информации практически не несёт. А вот если мы знаем, что курсор был передвинут в точку экрана с некоторыми известными координатами (x;y) - это уже что-то.
Параметр, который встречается практически во всех событиях - Sender . Он указывает на объект, который данное событие инициировал. Использовать его удобно в случае с несколькими объектами, у которых установлен один обработчик одного и того же события (как в нашем примере №1).

Вот теперь, когда мы достаточно много знаем о событиях, можно наконец рассмотреть стандартные и наиболее часто используемые из них.

OnClick - щелчок мышью по объекту. У кнопки событие также срабатывает, если нажать Enter или Пробел в тот момент, когда фокус ввода находится на кнопке.

OnDblClick - двойной щелчок мышью.

OnContextPopup - при вызове контекстного меню объекта, т.е. при щелчке правой кнопкой мыши. Среди параметров есть MousePos - координаты курсора в момент щелчка. Координата по X доступна как MousePos.X , а по Y - как MousePos.Y .

OnEnter - момент, когда объект получает фокус ввода (фокус ввода обычно изображается пунктирной рамкой на объекте; в поле ввода - это курсор; фокус один на всё приложение, т.е. работать одновременно можно лишь с одним объектом).

OnExit - момент, когда объект теряет фокус ввода.

OnMouseDown - при нажатии кнопки мыши (не обязательно левой). Параметр Button - нажатая кнопка (mbLeft - левая кнопка, mbRight - правая, mbMiddle - средняя). Shift - множество, указывающее, какие функциональные клавиши были зажаты при щелчке. Таким образом можно отслеживать, например, нажатия при зажатых Ctrl +Alt и т.п. X , Y - координаты курсора во время нажатия (относительно левого верхнего угла самого компонента, а не формы!).

OnMouseUp - событие, аналогичное OnMouseDown . Происходит при отпускании кнопки мыши. Пример комбинации этих двух событий - графический редактор: когда кнопка мыши нажата, происходит рисование, а когда отпущена - не происходит.

OnMouseMove - ещё одно событие мыши, происходящее при перемещении курсора над объектом. X , Y - координаты нового положения, Shift - множество нажатых функциональных клавиш.

OnKeyDown - при нажатии какой-либо клавиши клавиатуры в тот момент, когда фокус ввода находится на объекте. Key - код нажатой клавиши, Shift - всё то же множество функциональных клавиш (этот параметр встречается во многих событиях).

OnKeyUp - при отпускании клавиши (антипод OnKeyDown ).

OnKeyPress - при нажатии клавиши, которая печатает какой-либо символ (буква, цифра, знак). Key - уже не код клавиши, а сам символ (тип данных: Char - один символ).

OnResize - при изменении размеров объекта.

OnPaint - при отрисовке объекта на экране (например, формы).

У самой формы также есть множество событий. Отметим некоторые из них:

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

OnClose - при закрытии формы.

OnCloseQuery - при попытке закрыть форму. Это событие можно использовать, если нужно заблокировать закрытие формы. В обработчике присутствует параметр CanClose (англ. - "можно закрыть "): логическое значение, тип данных - Boolean . Если после выполнения обработчика значение переменной CanClose окажется False , то закрытие формы произведено не будет. Значение этой переменной устанавливается программно. Пример использования этого события - текстовый редактор. Если пользователь ввёл текст, но не сохранил его в файл, при выходе из программы нужно спросить, следует ли сохранить изменения.

Пример обработки событий №2

Цель: запретить "прямое" закрытие формы, а спрашивать, действительно ли пользователь хочет закрыть программу. Используем событие формы OnCloseQuery . В обработчике выведем диалоговое окно и, если пользователь ответит "Нет ", заблокируем закрывание. Для вывода окна диалога воспользуемся функцией MessageDlg . Подробнее о ней и о её параметрах Вы узнаете в одном из следующих уроков. Наш обработчик:

procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
if MessageDlg("Вы действительно хотите выйти?" ,mtWarning,,0) = mrNo then
CanClose:= False
end ;

Проверяемое условие - нажата ли кнопка "Нет ". Если она нажата, блокирум закрытие присвоением переменной CanClose значения False . В результате при закрытии увидим следующее:


События (продолжение)

OnDestroy - при уничтожении формы (после закрытия форма уничтожается и освобождается занятая ей память).

OnShow - при показе формы на экран.

OnHide - при скрытии формы с экрана.

OnActivate - момент, когда форма становится активной, т.е. когда ей передаётся фокус ввода (например, когда пользователь щёлкнул по форме).

OnDeactivate - соответственно, когда форма теряет фокус.

OnChange - при изменении чего-либо (например, у поля ввода TEdit это событие срабатывает, когда изменяется текст в этом поле).

OnDragDrop , OnDragOver , OnEndDock , OnEndDrag , OnStartDock , OnStartDrag - все эти события связаны с технологией Drag&Drop (т.е. когда объект "захватывается" мышью и переносится в другое место), а также с Dock -технологией. Суть этой (Dock) технологии во встраивании одних объектов в другие. Например, если взять Object Inspector за заголовок и переместить на нижнюю часть Object TreeView , а затем отпустить, то окна объединятся, а если переместить его в центр Object TreeView , то закладок станет 3 (Object TreeView станет третьей закладкой).

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

Заключение

Мы рассмотрели работу с событиями - одни из принципов объектно-ориентированного программирования. Таким образом, разработка визуальной программы для Windows сводится к написанию обработчиков на события, в которых описывается взаимодействие объектов друг с другом.

Обзор

События в Delphi

Понимание событий

Обработка сообщений Windows в Delphi

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

    Программирование, ориентированное на события - неотъемлемая черта Windows. Некоторые программные среды для быстрой разработки приложений (RAD) пытаются скрыть от пользователя эту черту совсем, как будто она настолько сложна, что большинство не могут ее понять. Истина заключается в том, что событийное программирование само по себе не так уж сложно. Однако, есть некоторые особенности воплощения данной концепции в Windows, которые в некоторых ситуациях могут вызвать затруднения.

    Delphi предоставляет полный доступ к подструктуре событий, предоставляемой Windows. С другой стороны, Delphi упрощает программирование обработчиков таких событий.

    В данном уроке приводится несколько примеров того, как обрабатывать события в Delphi, дается более детальное объяснение работы системы, ориентированной на события.

  3. События в Delphi
  4. Объекты из библиотеки визуальных компонент (VCL) Delphi, равно как и объекты реального мира, имеют свой набор свойств и свое поведение - набор откликов на события, происходящие с ними. Список событий для данного объекта, на которые он реагирует, можно посмотреть, например, в Инспекторе Объектов на странице событий. (На самом деле, на этой странице представлен список свойств, которые имеют тип вроде TMouseMoveEvent и представляют из себя процедуры-обработчики событий. Существует соглашение по названиям данных свойств. Например, OnDblClick соответствует двойному щелчку мыши, а OnKeyUp - событию, когда нажатая клавиша была отпущена.) Среди набора событий для различных объектов из VCL есть как события, портируемые из Windows (MouseMove, KeyDown), так и события, порождаемые непосредственно в программе (DataChange для TDataSource).

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

    Простейшие события, на которые иногда нужно реагировать - это, например, события, связанные с мышкой (они есть практически у всех видимых объектов) или событие Click для кнопки TButton. Предположим, что вы хотите перехватить щелчок левой кнопки мыши на форме. Чтобы сделать это - создайте новый проект, в Инспекторе Объектов выберите страницу событий и сделайте двойной щелчок на правой части для свойства OnClick. Вы получите заготовку для обработчика данного события:

    begin

    end;

    Напишите здесь следующее:

    procedure TForm1.FormClick(Sender: TObject);

    begin

    MessageDlg("Hello", mtInformation, , 0);

    end;

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

    procedure TForm1.FormKeyDown(Sender: TObject;

    var Key: Word;

    Shift: TShiftState);

    begin

    MessageDlg(Chr(Key), mtInformation, , 0);

    end;

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

  5. Понимание событий

Событийное программирование есть не только в 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, но и внутренними процессами.
      1. Обработка сообщений 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 нужно написать обработчик:

begin

  • , 0);
  • end;

    В данном случае при нажатии правой кнопки мыши будет появляться диалог.

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

    procedure TForm1.WMRButtonDown(var Msg: TWMMouse);

    begin

    MessageDlg("Right mouse button click.", mtInformation,

  • , 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, , 0);

    Handled:=True;

    end;

    end;

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

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

    Событие (Event ) - это то, что происходит во время работы программы. В Delphi каждому событию присвоено имя. Например, щелчок кнопкой мыши - это событие OnClick , двойной щелчок мышью событие OnDblClick .

    В табл. В9 приведены некоторые события Windows.

    Таблица В9 . События.

    Событие Происходит
    OnClick При щелчке кнопкой мыши
    OnDblClick При двойном щелчке кнопкой мыши
    OnMouseDown При нажатии кнопки мыши
    OnMouseUp При отпускании кнопки мыши
    OnMouseMove При перемещении мыши
    OnKeyPress При нажатии клавиши клавиатуры
    OnKeyDown При нажатии клавиши клавиатуры. События OnKeyDown и OnKeyPress - это чередующиеся, повторяющиеся события, которые происходят до тех пор, пока не будет отпущена удерживаемая клавиша (в этот момент происходит событие OnKeyUp )
    OnKeyUp При отпускании нажатой клавиши клавиатуры
    OnCreate При создании объекта (формы, элемента управления). Процедура обработки этого события обычно используется для инициализации переменных, выполнения подготовительных действий
    OnPaint При появлении окна на экране в начале работы программы, после появления части окна, которая, например, была закрыта другим окном, и в других случаях
    OnEnter При получении элементом управления фокуса
    OnExit При потере элементом управления фокуса

    Реакцией на событие должно быть какое-либо действие. В Delphi реакция на событие реализуется как процедура обработки события . Таким образом, для того чтобы программа выполняла некоторую работу в ответ на действия пользователя, программист должен написать процедуру обработки соответствующего события. Следует обратить внимание на то, что значительную часть обработки событий берет на себя компонент. Поэтому программист должен разрабатывать процедуру обработки события только в том случае, если реакция на событие отличается от стандартной или не определена. Например, если по условию задачи ограничений на символы, вводимые в поле Edit , нет, то процедуру обработки события OnKeyPress писать не надо, т. к. во время работы программы будет использована стандартная (скрытая от программиста) процедура обработки этого события.

    Методику создания процедур обработки событий рассмотрим на примере процедуры обработки события OnClick для командной кнопки Вычислить .

    Чтобы приступить к созданию процедуры обработки события, надо сначала в окне Object Inspector выбрать компонент, для которого создается процедура обработки события. Затем в этом же окне нужно выбрать вкладку Events (События).

    Что такое событие на языке Delphi и что из себя представляют процедуры обработки событий на языке Delphi.

    В качестве примера описания события на языке Delphi возмём пример, описанный в предыдущих статьях. Вид созданной формы подсказывает, как работает приложение. Очевидно, что пользователь должен ввести в поля редактирования исходные данные и щелкнуть мышью на кнопке Вычислить . Щелчок на изображении командной кнопки - это пример того, что в Windows называется событием.

    Событие (Event ) на Delphi - это то, что происходит во время работы программы. В Delphi каждому событию присвоено имя. Например, щелчок кнопкой мыши - это событие OnClick , двойной щелчок мышью - событие onDblClick .

    В таблице 9 приведены некоторые события Windows.

    Таблица 9. Некоторые события Windows.

    Событие Происходит
    OnСlick При щелчке кнопкой мыши
    OnDblClick При двойном щелчке кнопкой мыши
    OnMouseDown При нажатии кнопки мыши
    OnMouseUp При отпускании кнопки мыши
    OnMouseMove При перемещении мыши
    OnKeyPress При нажатии клавиши клавиатуры
    OnKeyDown При нажатии клавиши клавиатуры. События OnKeyDown и OnKeyPress - это чередующиеся, повторяющиеся события, которые происходят до тех пор, пока не будет отпущена удерживаемая клавиша (в этот момент происходит событие OnKeyUp)
    OnKeyUp При отпускании нажатой клавиши клавиатуры
    OnCreate При создании объекта (формы, элемента управления). Процедура обработки этого события обычно используется для инициализации переменных, выполнения подготовительных действий
    OnPaint При появлении окна на экране в начале работы программы, после появления части окна, которая, например, была закрыта другим окном и в других случаях
    OnEnter При получении элементом управления фокуса
    OnExit При потере элементом управления фокуса

    Реакцией на событие в Delphi должно быть какое-либо действие. В Delphi реакция на событие реализуется как процедура обработки события на языке программирования Delphi . Таким образом, для того, чтобы программа выполняла некоторую работу в ответ на действия пользователя, программист должен написать процедуру обработки соответствующего события. Следует обратить внимание, на то, что значительную часть обработки событий на Delphi берет на себя компонент. Поэтому программист должен разрабатывать процедуру обработки события на языке Delphi только в том случае, если реакция на событие отличается от стандартной или не определена. Например, если по условию задачи ограничений на символы, вводимые в поле Edit нет, то процедуру обработки события onKeyPress писать не надо, т. к. во время работы программы будет использована стандартная (скрытая от программиста) процедура обработки этого события.

    Методику создания процедур обработки событий на Delphi рассмотрим на примере процедуры обработки события onclick для командной кнопки Вычислить .

    Чтобы приступить к созданию процедуры обработки события, надо сначала в окне Object Inspector выбрать компонент, для которого создается процедура обработки события. Затем, в этом же окне нужно выбрать вкладку Events (События).

    Рисунок 21. На вкладке Event (Delphi) перечислены события, которые может воспринимать компонент на Delphi

    В левой колонке вкладки Events (рисунок 21) перечислены имена событий на языке программирования Delphi, которые может воспринимать выбранный компонент (объект). Если для события определена (написана) процедура обработки события, то в правой колонке, рядом с именем события, выводится имя этой процедуры.

    Для того чтобы создать функцию обработки события на языке программирования Delphi, нужно сделать двойной щелчок мышью в поле имени процедуры обработки соответствующего события. В результате этого откроется окно редактора кода, в которое будет добавлен шаблон функции обработки события, а в окне Object Inspector , рядом с именем события, появится имя функции его обработки (рисунок 22).

    Delphi присваивает функции обработки события имя, которое состоит из двух частей. Первая часть имени идентифицирует форму, содержащую объект (компонент), для которого создана процедура обработки события. Вторая часть имени идентифицирует сам объект и событие. В нашем примере имя формы - Form1 , имя командной кнопки - Button1 , а имя события - Click .

    Рисунок 22. Примеры процедуры обработки событий сгенерированной Delphi

    В окне редактора кода между словами begin и end можно печатать инструкции, реализующие функцию обработки события на языке программирования Delphi.

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

    Функция Button1Click выполняет расчет скорости и выводит результат расчета в поле Label4. Исходные данные вводятся из полей редактирования Edit1 и Edit2 путем обращения к свойству Text . Свойство Text содержит строку символов, которую во время работы программы введет пользователь. Для правильной работы программы строка должна содержать только цифры. Для преобразования строки в числа в программе используются функции StrToInt и StrToFloat . Функция StrToInt проверяет символы строки, переданной ей в качестве параметра (Edit1.Text - это содержимое поля Edit1 ), на допустимость, и, если все символы верные, возвращает соответствующее число. Это число записывается в переменную dist . Аналогичным образом работает функция StrToFloat , которая возвращает дробное число, соответствующее содержимому поля Edit2 . Это число записывается в переменную t .

    После того как исходные данные будут помещены в переменные dist и t , выполняются подготовительные действия и расчет. Первоначально с использованием функции Trunc , которая "отбрасывает" дробную часть числа, выделяется целая часть переменной t - это количество минут. Значением выражения Trunc(t*l00) mod 100 является количество секунд. Вычисляется это выражение так. Сначала число t умножается на 100 . Полученное значение передается функции Trunc , которая возвращает целую часть результата умножения t на 100 . Полученное таким образом число делится по модулю на 100 . Результат деления по модулю - это остаток от деления.

    После того как все данные готовы, выполняется расчет. Так как скорость должна быть выражена в км/час, то значения дистанции и времени, выраженные в метрах и минутах, преобразуются в километры и часы.

    Вычисленное значение скорости выводится в поле Label4 путем присваивания значения свойству Caption . Для преобразования чисел в строки используются функции IntToStr и FloatToStr .

    В результате нажатия кнопки Завершить программа должна завершить работу. Чтобы это произошло, надо закрыть, убрать с экрана, главное окно программы. Делается это при помощи метода Сlose . Процедура обработки события OnClick для кнопки Завершить приведена в листинге 2.



    
    Top