Начиная с Delphi 4 появился инструментарий, который, не добавляя никаких принципиально новых возможностей, позволяет систематизировать и упорядочить разработку объектно-ориентированных приложений. К тому же грамотное его использование позволяет сэкономить немало времени при проектировании. Для организации диспетчеризации событий введены следующие понятия.
Действие (action) — реализация некоторого поведения, являющегося реакцией на поступок пользователя, такой, как щелчок на кнопке или на разделе меню — инициаторе действия. Разработан класс TAction и ряд его наследников, реализующих многие стандартные действия, характерные для приложений Windows.
Список действий — компонент типа ActionList, содержащий предусмотренные в приложении действия. Это интерфейс разработчика, упорядочивающий его работу с действиями в процессе проектирования.
Редактор связей — объект типа TActionLink, который поддерживает связь между действиями и инициаторами действий. Редактор связей определяет, какое действие должно выполняться для данного инициатора.
Цель действия — объект, в котором отражается результат действия. Это может быть окно отображения или редактирования текста, набор данных и т.п.
В начале проектирования приложения разработчик должен представить себе список тех действий, которые может осуществлять пользователь. Конечно, в процессе проектирования этот список будет пополняться и изменяться, но некоторое начальное приближение очень полезно продумать заранее. Практическая реализация составленного вами списка действий может начинаться с переноса на проектируемую форму невизуального компонента ActionList, расположенного на странице библиотеки Standard
. Сделав на этом компоненте двойной щелчок, вы попадаете в редактор действий (рис. 9.1), позволяющий вводить и упорядочивать действия.
Рис. 9.1 Окно редактора действий |
Щелчок правой кнопкой мыши или щелчок на маленькой кнопочке со стрелкой вниз правее первой быстрой кнопки окна редактирования позволит вам выбрать одну из команд: New Action
(новое действие) или New Standard Action...
(новое стандартное действие). Первая из них относится к вводу нового действия любого типа. По умолчанию эти действия будут получать имена Action1, Action2 и т.д. Вторая команда открывает окно (рис. 9.2), в котором вы можете выбрать необходимое вам стандартное действие (или сразу несколько действий). После этого в правом окне (Actions
) редактора появятся имена выбранных действий, а в левом (Categories
) — категории действий.
Рис. 9.2 Окно выбора стандартных действий |
Каждое действие, которое вы внесли в список — это объект типа TAction для нестандартных действий или других производных типов для стандартных. Выбрав в окнах редактора ту или иную категорию или [AllActions]
(все категории), а в правом — конкретное действие, вы можете увидеть в Инспекторе Объектов его свойства и события. Вы можете установить свойство Name (имя) — оно появится в правом окне редактора свойств и будет в дальнейшем фигурировать в заголовках обработчиков событий. Можете также установить надпись (Caption), которая далее будет появляться в инициаторах действия — кнопках, разделах меню и т.д. Можете задать быстрые клавиши (Shortcut), надписи на ярлычках подсказок и в строке состояний (Hint), идентификатор темы контекстной справки (HelpContext) и другие обычные для многих компонентов свойства.
Можно для каждого или некоторых действий указать свойство ImageIndex, которое является индексом (начиная с 0) изображения, соответствующего данному действию, в отдельном компоненте списка изображений ImageList (см. раздел 9.3). Этот индекс передастся в дальнейшем компонентам, связанным с данным событием — разделам меню, кнопкам. Если в свойстве Images компонента ActionList указать имя списка, размещенного на форме и заполненного изображениями, то эти изображения появятся также в окне редактора действий (рис. 9.1).
Свойство Category (категория) не имеет отношения к выполнению приложения. Задание категории просто позволяет в процессе проектирования сгруппировать действия по их назначению. Вы можете для каждого действия выбрать категорию из выпадающего списка, или написать имя новой категории и отнести к ней какие-то действия. Например, на рис. 9.1 видна введенная пользователем категория Файлы
, к которой отнесены действия, связанные с меню Файл
.
На странице событий Инспектора Объектов для каждого действия определено три события: OnExecute, OnUpdate и OnHint.
Событие OnExecute возникает в момент, когда пользователь инициализировал действие, например, щелкнув на компоненте (разделе меню, кнопке), связанном с данным действием. Обработчик этого события должен содержать процедуру, реализующую данное действие. Например, обработчик события OnExecute действия Exit может в простейшем случае иметь вид
procedure TForm1.ExitExecute(Sender: TObject); begin Close; end;а в более сложных случаях может содержать проверку возможности закрыть приложение, запросы пользователю и т.д. Одним из преимуществ использования действий является то, что заголовки обработчиков приобретают осмысленный характер и код становится более прозрачным. Действительно, гораздо понятнее заголовок ExitExecute, чем, например, Button7Click или N14Click (попробуйте найти в вашем большом приложении, где эта кнопка Button7 или раздел меню N14). В результате вы избавляетесь от необходимости давать осмысленные имена кнопкам и разделам меню, т.е. облегчаете свою работу с компонентами.
Событие OnUpdate периодически возникает в промежутках между действиями. Возникновение этих событий прекращается только во время реализации события или во время, когда пользователь ничего не делает и компьютер находится в состоянии ожидания действий. Обработчик события OnUpdate может содержать какие-то настройки, подготовку ожидаемых дальнейших действий или выполнение каких-то фоновых операций.
Событие OnHint возникает в момент, когда на экране отображается ярлычок подсказки в результате того, что пользователь задержал курсор мыши над компонентом, инициализирующим событие.
Наличие в объекте действия событий OnUpdate и OnHint обогащает ваши возможности по проектированию приложения. Без этого объекта подобные события отсутствуют и их при необходимости приходится моделировать более сложными приемами.
Связь объектов действий с конкретными инициализаторами действий — управляющими элементами типа кнопок, разделов меню и т.д., осуществляется через свойство Action, имеющееся у всех управляющих элементов. Поместите на вашу форму кнопку, и вы увидите в Инспекторе Объектов это свойство. Раскройте его выпадающий список и выберите из него действие, которое вами было предварительно описано. Вы сможете заметить, что после этого в кнопку перенесутся такие свойства объекта действия, как Caption, Hint и др., и что в ее событие OnClick подставится обработчик, предусмотренный вами для данного действия. Если далее вы введете в приложение меню с разделом, соответствующим тому же действию, и укажете в нем то же значение свойства Action, то свойства и обработчики событий объекта действия будут перенесены и на этот раздел меню. Вам не придется повторно задавать значение Hint, Caption и др.
Подобная связь между свойствами объекта действия и свойствами управляющих элементов выполняется классом TActionLink и его наследниками. Передаются в компоненты такие свойства действия, как Caption, Checked, Enabled, HelpContext, Hint, ImageIndex, Shortcut, Visible. Впрочем, в любом компоненте разработчик может изменить переданное в него свойство. Обратной связи TActionLink с компонентами нет, так что эти изменения будут локальными и не отразятся на других компонентах. Если же требуется изменить свойства всех связанных с одним действием компонентов, надо изменять свойство объекта действия. Это облегчает программное управление компонентами, связанными с одним и тем же действием. Например, если в какой-то момент вам надо сделать недоступными (или, наоборот, доступными) два компонента — кнопку Button3 и раздел меню N5, связанные с одним событием (назовем его объект Do), то при отсутствии централизованной диспетчеризации событий через ActionList вам пришлось бы писать два оператора:
Button3.Enabled := false; N5.Enabled := false;а при наличии объекта Do — всего один:
Do.Enabled := false;
Дело не только в экономии кода, но и в его прозрачности, понятности его как для вас самих, так и для тех, кому придется, может быть, в дальнейшем сопровождать ваше приложение.