Версия для печати
Меню на основе панели инструментов
http://www.delphikingdom.com/asp/viewitem.asp?catalogID=377Peter 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;Здесь правда возникает другая проблема -- становится недоступным через клавиатуру системное меню (которое с Переместить/Закрыть и пр.), но это уже не самое страшное.Примерчик, иллюстрирующий всё здесь написанное, а так же содержащий копию текста, который Вы сейчас читаете, содержится в архиве MSMenuExmpl.zip (10 K) P.S. Уже после публикации статьи ко мне поступила просьба поместить ссылку на ресурс, который содержит интересную реализацию toolbar-меню:
- Описанное меню, по сравнению со стандартным имеет и дополнительные преимущества. Ведь созданные popup-менюшки можно использовать и отдельно, именно в качестве popup. Есть у Вас к примеру popup с операциями над каким-то объектом. Вы настраиваете этот объект так, чтобы по правому клику выскакивало меню и записываете соответствующий элемент в главное меню. Когда объект выделяется, кнопке в главном меню ставится Visible := TRUE, когда теряет фокус: Visible := FALSE. Вот и программа сразу солидней стала :) Можно вместо Visible использовать Enabled. Не забывайте и про событие OnPopup у класса TPopupMenu - это хорошее место для динамического скрытия или запрещения отдельных пунктов, в зависимости от состояния программы.
- Можно ещё создать обычное TMainMenu (но не ссылаться на него в свойстве Menu у главной формы) и кнопкам в панели задавать не DropdownMenu, MenuItem и ссылаться не на отдельные popup-меню, а на пункты главного меню. Единственное преимущество такого способа это то, что меню создаётся реальное, системное. Пятый пункт, с обработкой WM_SYSCOMMAND в данном случае становится бессмысленным. Тем не менее этот вариант мне понравился меньше, поскольку первый более гибок. Смешать оба варианта не удалось -- с выпадением подменю проблемы начались. Так что рекомендую использовать TPopupMenu, как описано.
http://www.akzhan.midi.ru/devcorner/samples/menubar/menubar.html
По сути, это pas-файл, описывающий компонент - наследник TToolBar, в который добавлены дополнительные возможности. А именно: (а) необходимые свойства TToolBar (см. выше) устанавливаются сразу в нужные значения; (б) добавлено свойство Menu: TMainMenu, позволяющее автоматически создать toolbar-меню, на основе существующего главного меню. Основным недостатком можно считать отсутствие синхронизации между обычным главным меню и создаваемым toolbar-меню. Плюс, учтите всё сказанное во втором замечании. Достоинство в том, что это законченный компонент, которому достаточно задать свойство Menu и не мучаться больше -- всё остальное оформление он выполнит сам.