Peter Taran дата публикации 20-03-2001 00:00 Меню на основе панели инструментов
Наверное многие видели меню, которое используется в MS Office или в
самой
среде Delphi: главные пункты выглядят как flat-кнопки -- плоские, но
при
перемещении над ними мыши как бы вспухающие. Кроме того, меню
оформлено как
панель инструментов и может пристыковываться к окну в любом месте.
Я предлагаю вариант реализации такого меню стандартными средствами
VCL.
Сразу замечу, что чтобы двигать меню произвольным образом нужно
возиться
дополнительно TToolBar такой возможности не предоставляет.
Само меню оформляется как объект класса TToolBar, главные пункты меню
стандартные для панели инструментов кнопки TToolButton, выпадающие
подменю объекты TPopupMenu.
Итак, по пунктам.
- 1. Создаётся обычная панель инструментов MenuBar: TToolBar
-
У неё устанавливаются следующие свойства:
Color = clMenu //цвет как у меню
AutoSize = TRUE //авторазмер
Flat = TRUE //плоские кнопки, иначе на меню непохоже
List = TRUE //рекомендую, иначе картинки сверху будут, а не слева
ShowCaptions = TRUE //рисовать и надписи, не только картинки
BorderWidth = 0..2 //необязательно, ширина отступа
EdgeBorders = [ebBottom] //тоже по вкусу
Свойствами Align, Left, Top, Width, Height а так же Anchors
выберите
способ привязки и положение меню (панели инструментов) на форме.
Размер кнопок не задаётся и менять его нельзя.
- 2. Создаются кнопки-главные элементы меню, класс TToolButton.
-
У каждой кнопки устанавливаются свойства:
AllowAllUp = TRUE
AutoSize = TRUE
Grouped = TRUE
Caption = "Название пункта меню"
- 3. Для каждой кнопки создаётся своё подменю объект класса
TPopupMenu
-
В каждом из подменю задаётся соответствующий список пунктов. У
кнопок на
панели MenuBar свойство DropdownMenu заполняется ссылкой на
соответствующий
объект TPopupMenu.
Всё? Нет. К сожалению установленный в системе шрифт нельзя задать в
design-режиме, а потому:
-
4. Когда-нибудь, до использования меню (например в обработчике
события формы
OnCreate) должен исполниться следующий код:
-
var
nc: TNonClientMetrics;
s: TFontStyles;
. . . .
begin
. . . .
//читаем системные настройки в структуру nc
nc.cbSize := sizeof(nc);
SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(nc), @nc, 0);
with MenuBar.Font do begin
Charset := nc.lfMenuFont.lfCharSet; //устанавливаем charset
Height := nc.lfMenuFont.lfHeight; //высоту шрифта
Name := nc.lfMenuFont.lfFaceName; //гарнитуру шрифта
//далее определяем набор стилей
s := [];
if nc.lfMenuFont.lfWeight >= FW_BOLD then
s := s + [fsBold];
if nc.lfMenuFont.lfItalic <> 0 then
s := s + [fsItalic];
if nc.lfMenuFont.lfUnderline <> 0 then
s := s + [fsUnderline];
if nc.lfMenuFont.lfStrikeOut <> 0 then
s := s + [fsStrikeOut];
Style := s;
end;
- 5. Есть ещё один существенный недостаток.
- Созданное описанным образом
меню
не считается таковым с точки зрения Windows. Самое явное
последствие
оно не подсвечивается по нажатию клавши Alt. Исправить данный
недостаток
удалось написанием обработчика сообщения WM_SYSCOMMAND, которое
вызывается,
в частности, для выделения пункта меню по горячей клавише.
Заведите новый метод в private-секции формы с меню (имя метода и
параметра
роли не играет):
procedure SysCommand(var M: TWMSysCommand); message WM_SYSCOMMAND;
Реализация метода такова (код основан на исходниках TToolBar):
procedure TMain.SysCommand(var M: TWMSysCommand);
begin
//проверяется, что это команда - menu accelerator key, что дополнительной
//кнопки не нажато (только Alt), что никто не захватил (capture) мышь
if (M.CmdType and $FFF0 = SC_KEYMENU) and (M.Key = 0) and
(GetCapture() = 0) then begin
MenuBar.TrackMenu(nil);
//аргумент это кнопка, подменю которой вывалится; nil-никакой, такова
//стандартная реакция; если хотите, чтобы подменю первой кнопки
//сразу развернулось напишите
MenuBar.TrackMenu(MenuBar.Buttons[0]);
//можно и просто указать компонент-кнопку:
MenuBar.TrackMenu(mb_Options);
end else
inherited;
//условие не выполнили - обрабатываем по умолчанию
end;
Здесь правда возникает другая проблема -- становится недоступным
через
клавиатуру системное меню (которое с Переместить/Закрыть и пр.), но
это
уже не самое страшное.
Вот теперь всё! Можете запускать программу и смотреть что получилось.
Несколько замечаний.
- Описанное меню, по сравнению со стандартным имеет и дополнительные
преимущества. Ведь созданные popup-менюшки можно использовать и
отдельно,
именно в качестве popup. Есть у Вас к примеру popup с операциями
над
каким-то объектом. Вы настраиваете этот объект так, чтобы по
правому клику
выскакивало меню и записываете соответствующий элемент в главное
меню.
Когда объект выделяется, кнопке в главном меню ставится Visible :=
TRUE,
когда теряет фокус: Visible := FALSE. Вот и программа сразу
солидней стала :)
Можно вместо Visible использовать Enabled. Не забывайте и про
событие OnPopup
у класса TPopupMenu - это хорошее место для динамического скрытия
или
запрещения отдельных пунктов, в зависимости от состояния программы.
- Можно ещё создать обычное TMainMenu (но не ссылаться на него в
свойстве
Menu у главной формы) и кнопкам в панели задавать не DropdownMenu,
MenuItem и ссылаться не на отдельные popup-меню, а на пункты
главного
меню. Единственное преимущество такого способа это то, что меню
создаётся
реальное, системное. Пятый пункт, с обработкой WM_SYSCOMMAND в
данном
случае становится бессмысленным. Тем не менее этот вариант мне
понравился
меньше, поскольку первый более гибок. Смешать оба варианта не
удалось --
с выпадением подменю проблемы начались. Так что рекомендую
использовать
TPopupMenu, как описано.
Примерчик, иллюстрирующий всё здесь написанное, а так же содержащий
копию текста, который Вы сейчас читаете, содержится
в архиве MSMenuExmpl.zip (10 K)
Таран Петр
P.S. Уже после публикации статьи ко мне поступила просьба
поместить ссылку на ресурс, который содержит интересную
реализацию toolbar-меню:
http://www.akzhan.midi.ru/devcorner/samples/menubar/menubar.html
По сути, это pas-файл, описывающий компонент - наследник
TToolBar, в который добавлены дополнительные возможности. А
именно: (а) необходимые свойства TToolBar (см. выше)
устанавливаются сразу в нужные значения; (б) добавлено свойство
Menu: TMainMenu, позволяющее автоматически создать
toolbar-меню, на основе существующего главного меню.
Основным недостатком можно считать отсутствие синхронизации
между обычным главным меню и создаваемым toolbar-меню. Плюс,
учтите всё сказанное во втором замечании.
Достоинство в том, что это законченный компонент, которому
достаточно задать свойство Menu и не мучаться больше -- всё
остальное оформление он выполнит сам.
[TFont] [TMainMenu] [TPopupMenu] [TToolBar] [TToolButton] [Работа с меню] [Меню (WinAPI)]
Обсуждение материала нет сообщений |