Синхронизация MainMenu и ToolBar через TActionManager и TApplicationEvents в Delphi и С++ Builder

2010-07-05 papirosnik Delphi

Какое страшное получилось название у темы! На деле всё намного проще. Хочу рассказать, как сделать главное меню в своей программе настраиваемым (т.е некоторые редко использыемые пункты можно скрывать или добавлять контекстно-зависимые пункты меню). Также в этой статье коснёмся вопроса, как синхронизировать ToolBar  с меню, сделать, чтобы он тоже менял свой вид в зависимости от контекста программы. И наконец, пройдёмся по тем компонентам, которые призваны облегчить нам этот процесс, а не набивать всё вручную. И конечно же, делаем всё это на примере.

Итак, кликаем в Delphi или Билдере File->New->VCL Form Application. Так как среда программирования Delphi как-то более распространена в русскоязычной части света (а также в Латиноамериканской:) ), нежели С++Builder , то и пример буду сопровождать в ней. Для Билдера различия будут несущественными и выражаться только в синтаксисе языка. Поэтому адаптировать пример под Builder любому мало-мальски начинающему программисту труда не составит.

Сразу же переименовываем нашу форму в инспекторе объектов в frmMain (Свойсто Name). В свойстве Caption (заголовок формы) пишем "TACtionManager test application" или что-то в этом духе. Я так рекомендую делать, чтобы потом удобнее было различать проекты, если вдруг спустя какое-то время Вы к ним вернётесь за примером. Жмём сохранить проект как (Save Project As…), даём ему имя, напрмер:  ActionManTest, а файл главной формы сохраняем под незатейливым именем Main

Далее кладём на форму TActionManager и называем его amMain. TToolBar теперь размещать на форме не надо. TActionManager берёт всю работу на себя. В редакторе этого компонента есть закладка Toolbars, в котором можно создавать какое-угодно количество тулбаров. А вот главное меню всё-таки надо создать. Но теперь это не компонент TMainMenu, а TActionMainMenuBar (вкладка Additional — Дополнительные). Затем дважды щёлкаем по значку нашего компонента и в открывшемся редакторе переходим на вкладку Toolbars. Здесь нажимаем экранную кнопку New и видим, что в списке добавился ActionToolBar1. Кроме того, замечаем, что он и физически добавился на форму. Теперь у нас вверху формы две полосы — самая верхняя для главного меню, а ниже под ней — полоса тулбара.

Примечательным свойство TActionManager'а (а также его предка — TActionList) является то, что он позволяет инкапсулировать в себе все действия программы, сконцентрировав их в одном месте, возможно разбив по категориям. Если нам потребуется доступ к какому-либо действию из главного меню, из тулбара, возможно по клику на какой-то экранной кнопке — нам теперь не надо писать обработчики для всех этих событий у каждого элемента. Достаточно заиметь свой один TAction, который будем присваивать в поле Action каждого упомянутого элемента. И они будут вести себя как один (независимо от того, пункт это меню, экранная кнопка или пиктограмма тулбара). Более того, они даже выглядеть будут в некотором роде одинаково, так как свой Загоолвок, Hint, ImageIndex они будут наследовать от TAction.

От слов у делу. Переходим в редакторе на закладку Actions. И в одноимённом поле (Actions) кликаем правой кнопкой и выбираем New Standard Action. Раскрывшийся список стандартных действий пролистываем немного вниз, находим ветку File и выбираем в ней первый элемент TFileOpen. Ok — вот и всё — у нас появилось стандартное действие для открытия файла.

Добавим ещё один экшн — выход из программы. Снова правый клик мышкой — пункт выпадающего меню Добавить стандартный экшн — и снова в ветке файл выбираем TFileExit.

Пока всё проосто (а дальше ещё проще). Осталось опубликовать наши экшены в главном меню и тулбаре. В главном меню мы хотим, чтобы появился целый пункт верхнего уровня: Файл. Для этого мышкой из редактора TActionManager перетаскиваем не отдельный экшн — а цедую категорию (обратие внимание, появилась у Вас слева в окне редактора). Итак, зацепляем слово File и тянем на главную форму — в полосу верхнего меню и там бросаем.

Появился пункт меню File. Но мы, рускоговорящие, хотим, чтобы он назывался Файл. Благодаря тому, что теперь он доступен в инспекторе объекто, мы просто выделив его мышкой там и переименуем.

Теперь надо эти же экшены опубликовать и в тулбаре. Тоже перетаскиваем мышкой на полосу тулбара, но теперь не целую категорию, а по отдельности каждый экшен. Всё бы ничего, да они отображаются как-то не так — название, как в пунктах меню, а общепринято, чтобы там была иконка. Ах да — иконки. Срочно добавляем на форму TImageList со вкладки Win32. Дважды щёлкнув по нему в открывшемся редакторе жмём кнопку Add и опять же дважды добавляем какое-нибуь подходящее изображение: 1-е для открытия файла, 2-е — для выхода из программы.

Теперь вернёмся к компоненту TActionManager. В его свойствах Images выберем единственный TImageList1. Открываем редактор экшенов (двойной клик по компоненте amMain) в свойствах экшенов пропишем индекс картинки: для первого ImaheIndex = 0, для второго = 1; Опа — чудесным образом в тулбаре рядом с заголовками экшенов появились и пиктограммы. Но в главном меню не так. Надо просто удалить пункт Файл из главного меню (утащив его на пустое место формы и заново притянув из редактора amMain).  Осталось убрать названия экшенов в тулбаре. Просто выделяем каждый элементтулбара (не экшн в мэнеджере) по отдельности и в его свойствах убираем галочку напротив ShowCaption (или выбираем false из выпадающего списка).

После всех манипуляций должно получится окно главной формы и редактора TActionManager наподобие этого:

Скрин для примера ActionManager

Запсукаем наши труды и получаем полностью работоспособное приложение, умеющие открывать файл (теоретически — по крайней мере, показывать диалог открытия файла…физически никакого файла мы пока не открываем) и выходить из программы по кликам в главном меню или тулбаре. И это без единой строчки кода!

Но в мудёрном заглавии статьи я ещё приплёл TApplicationEvents. Что это за зверь и для чего он здесь: это компонента, отвечающий за обработку системных событий приложения, а именно: минимизирование окна, обработчик холостого хода, обработчик восстановления и т.п. Конкретно в нашем случае он будет полезен для той самой синхронизации, о которой я упомянул в заголовке статьи.

Предположим, мы не хотим, чтобы пользователь мог открывать файлы в какой-то период времени. Для этого нам надо сделать недоступными (Disabled, а точнее Enabled := false) пункты меню и кнопки тулбара, отвечающие за открытие файла. Пример конечно притянутый за уши. На практике чаще наоборот, запрещают кнопки сохранения (если файл не открыт — нечего сохранять) или доступа к чему-либо (если версия триальная) и т.п. Ну да ладно, для понимания принципов механизма это не важно. Важно то, что мы не будем всё это делать вручную, а поручим обработчику OnIdle компонеты TApplicationEvents. Итак, открыв его свойства и перейдя на закладку Events, создадим обработчик OnIdle (для этого дважды щёлкнем в соответсвующей строке напротив нужного нам свойства). Теперь в демонстрационных целях я хочу, чтобы файл можно было открывать только по нечётным минутам текущего времени, т.е в 1-ю, 3-ю, 5-ю и т.п., а по чётным пользователь был лишён такой радости.

Пишем такой код обработика: 

procedure TfrmMain.ApplicationEvents1Idle(Sender: TObject; var Done: Boolean);
begin
    FileOpen1.Enabled := Odd(MinuteOf(Now));
end;

Функция MinuteOf возвращает номер минуты на какой-то момент, в нашем случае Now (теперь). Эта функция объявлена в модуле DateUtils, поэтому его надо подключить (написав uses DateUtils; сразу же после ключевого слова implementation в коде программы) Функция Odd возвращает true, если её аргумент нечётный. Таким образом, наши пункты меню и иконки тулбара будут разрешены или запрещены в зависимости от того, чётная ли или нет сейчас идёт минута  системного времени. Заметьте, что мы не обращаемся к свойству Enabled пункта меню или пиктограммы тулбара, а делаем это в единственном месте — в свойстве Enabled экшена FileOpen1. Это работает, потому что наш пункт меню и элемент тулбара имеют один общий Action = FileOpen1.

Запускаем наше тестовое приложение и в течении нескольких минут смотрим то на него (а именно на пункт меню Open и тулбар), то на системные часы (по умолчанию в правом нижнем углу экрана). И видим, что как только наступает чётная минута, эти элемента становятся запрещёнными и наоборот. Это возможно благодаря обработчику OnIdle компоненты TApplicationEvents.

Конечно же, на практике в этом обработчике пишутся более реальные вещи. Напрмер, если файл открыт и изменён, то становятся доступными пункты меню Save и Save As… и т.п.

Не шуточный проект с нечётными минутами, а реально действующий, в котором реализована эта концепция, можно увидеть здесь: http://papirosnik.info/programmy/

Delphi, TActionmanager, TApplicationsEvent, TMainMenu, TToolBar,


Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Powered by WordPress. Designed by elogi.