Глава 5

Меню

Меню, как элемент интерфейса, присутствовали уже в самых первых программах, работавших еще в среде DOS. За прошедшие годы роль меню не уменьшилась — любое современное приложение имеет меню. Причем все это время меню совершенствовалось. Такая популярность этого элемента интерфейса приложений объясняется просто — меню обеспечивает стандартный и удобный доступ к функциям приложения и прекрасно структурирует их в однородные группы.

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

Компоненты меню унаследованы от классов тмепи и TComponent. Поэтому, строго говоря, меню не являются элементами управления (Controls) и становятся визуальными только при выполнении приложения. Во время разработки меню конструируются при помощи специального Редактора меню.

В этой главе рассматриваются следующие вопросы.

Как работает меню

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

Основные возможности, обеспечивающие функционирование меню, реализованы в базовом классе меню тмепи.

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

property Items: TMenuItem; default;

К отдельному элементу меню можно обратиться следующим образом:

SomeMenu.Items[1].Caption := 'Edit';

Или

SomeMenu[1].Caption := 'Edit';

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

property Images: TImageList;

Картинки должны содержаться в компоненте TImageList. У каждого элемента меню задается свойство image index, соответствующее номеру картинки в списке.

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

Таблица 5.1. Основные свойства и методы класса TMenuItem

Объявление

Описание

Property Action:

TBasicAction;

Определяет действие, связанное с элементом меню

Property Bitmap: TBitmap;

Содержит изображение, отображающееся вместе с элементом меню (если не задано свойство Imagelndex)

Type TMenuBreak = (mbNone, mbBreak, mbBarBreak) ;

Управляет представлением элементов в панели меню.

Property Break: TMenuBreak;

MbNone — элементы следуют друг за другом

MbBreak — начиная с этого элемента, команды располагаются в соседнем столбце

MbBarBreak — начиная с этого элемента, команды располагаются в соседнем столбце.

Между столбцами расположен разделитель

Property Caption: strings;

Текст команды меню

Property Checked: Boolean;

Управляет установкой флажка перед элементом меню

Property Default: Boolean;

Показывает, является ли элемент выбираемым по умолчанию в дочернем меню

Property Enabled: Boolean-

Управляет доступностью элемента меню

Property Grouplndex: Byte;

Используется при слиянии двух меню (см.ниже)

\ property Items[Index:

Integer]: TMenuItem; default;

Список элементов дочернего меню (если имеется)

property Menulndex: Integer;

Указывает на индекс элемента в родительском меню

property Radioltem: Boolean;

Значение True позволяет превращать элемент в переключатель: зависимыми становятся все элементы с одинаковым значением свойства Grouplndex

property Shortcut: TShortCut;

Содержит код горячей клавиши элемента меню

property Visible: Boolean;

Управляет видимостью элемента меню

procedure Add(Item: TMenuItem) ;

Добавляет в конец меню новый элемент

procedure Click; virtual;

Генерирует выбор элемента меню

procedure Delete(Index: Integer) ;

Удаляет элемент меню

function HasParent: Boolean;

Определяет наличие родительского меню

function IndexOf(Item: TMenuItem): Integers-

Возвращает индекс элемента меню

procedure Insert(Index: Integer; Item: TMenuItem) ;

Вставляет новый элемент меню перед текущим

procedure Remove(Item: TMenuItem) ;

Удаляет элемент меню

property OnClick: TNotifyEvent;

Вызывается при щелчке на элементе

property OnDrawItem: TMenuDrawItemEvent ;

type TMenuDrawItemEvent = procedure (Sender: TObject; ACanvas: TCanvas; ARect: TRect; Selected: Boolean) of object;

Вызывается при необходимости перерисовки элемента

 

 

 property OnMeasureItem:

TMenuMeasureItemEvent;

Вызывается для определения размеров элемента при его перерисовке

type TMenuMeasureItemEvent

= procedure (Sender:

TObject; ACanvas: TCanvas;

var Width, Height: Integer) of objects;

 

 

 

 

 

Property

OnAdvancedDrawItem:

TAdvancedMenuDrawItemEvent;

Отличается от OnDrawltem передачей расширенного кода состояния элемента TOwnerDrawState

Type

TAdvancedMenuDrawItemEvent

= procedure (Sender:

TObject; ACanvas: TCanvas;

ARect: TRect; State:

TOwnerDrawState) of object;

 

 

 

 

 

 

 

Рис. 5.1. Использование свойства Break команд меню

Свойство Break позволяет создать новую колонку в панели меню, содержащей данный элемент. На рисунке 5.1 показан результат использования cвойства Break (значения mbBreak И mbBarBreak Присвоены свойству Break элемента Экран).

Набор методов обеспечивает управление элементами меню программными средствами (вместо Редактора меню), в том числе их создание и удаление.

У самого компонента тмепи есть только событие onchange, возникающее при изменении структуры меню. Основная функциональность должна быть заложена в методы-обработчики события onclick , которое вызывается при выборе команды меню в приложении.

Для того чтобы связать меню с конкретным элементом управления, необходимо использовать свойства этого элемента управления. Свойство Menu в классе формы TForm связывает с формой компонент главного меню. Свойство PopupMenu в других визуальных компонентах обеспечивает использование компонента всплывающего меню.

При желании можно и меню, и их элементы создавать и видоизменять во время выполнения. Для этого есть большой набор функций — обратитесь к модулю Menus.

 Редактор меню

Для работы с компонентами меню во время разработки используется специализированный Редактор меню (рис. 5.2). Он открывается при щелчке на кнопке свойства items компонента меню в Инспекторе объектов или при двойном щелчке на компоненте. Он обеспечивает отображение всех элементов меню, представленных в свойстве items.

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

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

Для вставки и удаления элементов используются команды всплывающего меню Редактора или соответствующие клавиши.

Для выбранного элемента меню в Инспекторе объектов активизируется свойство caption, что позволяет быстро вводить текст команды меню и переходить к следующему элементу. Если в тексте команды встречается символ амперсанта (&), то следующий за ним символ автоматически становится горячей клавишей и подчеркивается. Например, свойство

Caption := '&Close'

в меню будет представлено как команда Close. Внутри меню она может быть вызвана комбинацией клавиш <Alt>+<0.

Рис. 5.2. Редактор меню

Если в качестве текста элемента меню введен символ "-", то этот элемент меню отображается как разделитель, элемент.

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

Рис. 5.3. Диалог выбора шаблона меню

Многие приложения имеют стандартные меню, использующиеся для работы с файлами, папкой обмена и т. д. Наиболее часто используемые наборы команд можно сохранять для дальнейшей работы в виде шаблона. Для этого применяются команды Save As Template и Insert From Template из всплывающего меню Редактора меню. При этом в Редакторе меню имеется несколько шаблонов для стандартных меню Windows (рис. 5.3). Также можно использовать меню из файлов ресурсов (*.mnu и *.гс), для этого необходимо выбрать команду Insert From Resource из всплывающего меню.

Главное меню приложения

Компонент TMainMenu инкапсулирует главное меню. Он происходит от класса тмепи, а для создания элементов меню использует Редактор меню.

Для того чтобы связать форму и главное меню, используется свойство Menu класса TForm. В нем из списка доступных в форме компонентов TMainMenu выбирается нужный компонент.

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

Для автоматического слияния двух меню используется свойство

property AutoMerge: Boolean;

Меню дочерней формы должно включаться в состав меню главной формы. Для этого свойство AutoMerge дочерней формы должно иметь значение True. Это же свойство для меню главной формы должно иметь значение False.

Слияние меню можно осуществить явно, используя метод

procedure Merge(Menu: TMainMenu);

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

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

Непосредственно на результат слияния влияет свойство

property Grouplndex: Byte;

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

По умолчанию все элементы меню имеют одно значение свойства Groupindex. Обычно это 0. Значение свойства элемента меню дочернего окна сравнивается со значениями свойства элементов меню главного окна. На этой основе осуществляется формирование объединенного меню. Его элементы ранжируются в соответствии с возрастанием значений свойства Groupindex.

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

Если значения свойства Groupindex элементов дочернего меню лежат между значениями этого свойства элементов главного меню, то данные элементы дочернего меню вставляются между элементами главного меню.

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

Если значение свойства элемента дочернего меню меньше минимального значения свойства элементов главного меню, то элементы дочернего меню добавляются в начало главного.

Рис. 5.4. Главная форма проекта DemoMerge

В качестве небольшого примера рассмотрим приложение DemoMerge (рис. 5.4.).

При щелчке на кнопке Show Form2 появляется дочерняя форма Form2. Ее меню объединяется с меню главной формы за счет использования свойства

AutoMerge, при этом аналогичное свойство главной формы имеет значение

-alse. При щелчке на кнопке Show Form3 появляется форма Form3. Для слияния меню в данном случае используется метод Merge.

 

Листинг 5.1 Секция implementation модуля главной формы проекта DemoMerge

implementation uses Unit2, Unit3;

($R *.DFM)

procedure TMainForni.ShowBtn2Click( Sender: TObject);

begin

Form2.Show;

3howBtn3.Enabled := False;

end;

procedure TMainForm.ShowBtn3Click(Sender: TObject);

begin

Form3.Show;

MainMenu.Merge(Form3.ChildMenu) ;

ShowBtn2.Enabled := False;

end;

end.

Значения свойства Groupindex для главного и дочерних окон можно просмотреть при помощи Редактора меню.

Благодаря механизму слияния меню, в меню главного окна внедряются команды, обеспечивающие сворачивание соответствующих дочерних меню.

Команды меню главного окна не имеют связанных методов-обработчиков и предназначены для иллюстрации работы свойства Groupindex.

Обратите внимание (листинг 5.1), что в главной форме предусмотрена блокировка включения второго дочернего окна. Дело в том, что при добавлении в главное меню элементов меню еще одного дочернего окна механизм слияния не учитывает наличие в главном меню уже добавленных ранее элементов другого дочернего окна.

 

Листинг 5.2 Секции implementation модуля формы Form3 проекта DemoMerge

implementation uses Uniti;

{$R *.DFM}

procedure TForm3-N3Click(Sender: TObject);

begin

Close;

MainForm.ShowBtn2.Enabled := True ;

end;

procedure TForm3.FormClose(Sender: TObject; var Action: TCloseAction);

begin MainForm.MainMenu.UnMerge(ChildMenu);

end;

end.

При использовании для слияния меню метода Merge необходимо предусмотреть восстановление исходного состояния главного меню при закрытии дочерней формы (листинг 5.2) при помощи метода unMerqe:

procedure Unmerge(Menu: TMainMenu);

Всплывающее меню

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

Свойство

property AutoPopup: Boolean;

отвечает за разворачивание меню при щелчке правой кнопкой мыши на элементе управления; это происходит, если установлено значение True.

Свойство

property PopupComponent: TComponent;

содержит указатель на компонент, использующий данное всплывающее меню:

Метод

procedure Popup(X, Y: Integer);

разворачивает всплывающее меню в точке экрана с координатами х и y. В Windows 98 и 2000 при этом предусмотрены различные визуальные эффекты: меню плавно разворачивается сверху, снизу и т. п. Если вы неравнодушны к красивым безделушкам, задайте значение свойства:

property MenuAnimation: TMenuAnimation;

При этом можно определить положение меню относительно точки щелчка (слева, справа, посредине):

property Alignment: TPopupAlignment;

При открытии всплывающего меню вызывается метод-обработчик

property OnPopup: TNotifyEvent;

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

Действия. Компонент TActionList

Пользовательский интерфейс современных приложений весьма многообразен, и зачастую один и тот же результат можно получить разными способами — нажатием кнопки на панели инструментов, выбором пункта меню и т. п. Можно решить проблему "в лоб" и повторить один и тот же код два, три раза и так далее. Недостатки такого подхода не обязательно комментировать. Можно воспользоваться Инспектором объектов и назначить пункту меню тот же обработчик события, что и кнопке, благо событие onciick везде одинаковое. Этот способ неплох, но при большом количестве взаимных ссылок легко запутаться.

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

Для начала работы следует поместить в форму компонент TActionList. Он находится в Палитре компонентов на первой странице Standard (вот видите, какое ему уделяется внимание!). После этого следует запустить редактор списка действий двойным щелчком мышью на компоненте или с помощью контекстного меню (рис. 5.5).

Теперь можно начинать добавление действий. Для программиста предусмотрен набор типовых, наиболее часто встречающихся действий (рис. 5.6). К ним относятся операции для работы с Буфером обмена (Copy/Cut/Paste), упорядочения окон (Tile/Cascade и т. п.), а также типовые операции по работе с наборами данных (перемещение, вставка, замена и прочие).

Рис. 5.5 Внешний вмд редактора действий компонента TactionList

Рис. 5.6. Окно выбора cтандартных действий компонента TactionList

Рис. 5.7. Свойства компонента TAction

Внешне эти свойства напоминают свойства компонента TMenuitem (пункт меню). Такие из них, как Caption, Checked, Enabled, Visible, Hint, He нуждаются в комментариях. Через свойство shortcut действие связывается с определенной комбинацией клавиш. Новинкой является то, что действие можно снабдить поясняющей картинкой. Компонент TActionList связывается со списком картинок TimageList, а действие TAction — с конкретной картинкой через свойство imageindex. Таким образом, все элементы управления, связанные с действием — кнопки и пункты меню — будут иметь одну и ту же картинку, одну надпись и одну подсказку, как показано на рис. 5.8.

Рис. 5.8. Меню и панель инструментов используют один список действий

Компонент TAction реагирует на три события: OnExecute, OnUpdate И onHint. Первое — и самое главное — должно быть как раз реакцией на данное действие. Это событие возникает в момент нажатия кнопки и пункта меню.

Введение второго события (OnUpdate) является очень хорошей находкой, о нем напишем подробнее. И автор этих строк, и, возможно, вы, потратили немало времени, чтобы в разрабатываемых программах элементы управления находились в актуальном состоянии. Если, скажем, вашей программой открыт первый файл, то нужно включить ряд кнопок и пунктов меню (Save, Save as, Print и т. д.); как только закрыт последний — отключить их. Если в буфере обмена есть что-то подходящее, нужно включить меню и кнопку Paste, если нет — отключить. В результате код, отслеживающий это, размазывается по всему приложению. А ведь можно поступить проще. Событие TAction.onUpdate возникает в моменты простоя приложения, то есть тогда, когда оно не занято обработкой сообщений. Это гарантирует, что оно возникнет до того, как пользователь щелкнет мышью и увидит всплывающие пункты меню; поэтому можно успеть обновить их состояние.

Третье событие имеет такой тип:

THintEvent = procedure (var HintStr: string; var CanShow: Boolean) of object;

Оно вызывается тогда, когда от элемента управления требуется показать подсказку, связанную с данным действием. В обработчике события можно указать, будет ли что-нибудь показываться (параметр CanShow) и если да, то что именно (параметр HintStr).

Сам компонент TActionList также имеет свои события. Среди них нужно отметить событие OnExecute:

type TActionEvent = procedure (Action: TBasicAction; var Handled: Boolean) of object;

Обработчик этого события вызывается до того, как получит управление обработчик события OnExecute самого действия Action. И, если вы все сделали в нем, можно установить параметр Handled в True и запретить дальнейший вызов. Обработчика Action. OnExecute;

После того как определены действия и написан код, реагирующий на них, осталось поставить завершающую точку. У большого числа элементов управления (например, всех кнопок и пунктов меню) есть опубликованное свойство Action; если действия к этому моменту определены, то в Панели инструментов из выпадающего списка среди возможных значений этого свойства достаточно выбрать нужный элемент.

Резюме

В этой главе рассматриваются вопросы создания меню для приложений Delphi. Хорошо знакомые всем компоненты TMainMenu (главное меню приложения) и трорирмепи (всплывающее меню) имеют на самом деле массу особенностей, позволяющих использовать их более эффективно. Если вы хотите продублировать меню при помощи кнопок панели инструментов или других элементов управления, используйте компонент TActionList.

Работу с компонентами меню облегчает специализированный Редактор меню. Он наглядно представляет всю иерархию элементов меню.