а также:
POPUP "text" [, признаки]
MENUITEM SEPARATOR рисует во всплывающем меню горизонтальную черту. Эта черта часто используется для разделения групп, связанных по смыслу и назначению опций.
Для пунктов всплывающего меню в строке символов можно использовать символ табуляции \t для разделения текста по столбцам. Текст, следующий за символом \t располагается в новом столбце, с достаточным отступом вправо для размещения в первом столбце всплывающего меню самой длинной строки текста. Символ \а выравнивает следующий за ним текст по правому краю. Признаками в инструкциях MENUITEM, относящихся к всплывающим меню, являются следующие:
• CHECKED — Слева от текста появляется метка ("галочка").
• GRAYED — Данный пункт меню недоступен и не генерирует сообщений WM_COMMAND. Текст изображается недоступным (серым цветом).
• INACTIVE — Данный пункт меню неактивен и не генерирует сообщений WM_COMMAND. Текст изображается обычным образом.
• MENUBREAK — Данный пункт меню, а также все последующие появляются на экране в новом столбце.
•MENUBARBREAK — Данный пункт меню, а также все последующие появляются на экране в новом столбце. Столбцы разделяются между собой вертикальной чертой.
Опции GRAYED и INACTIVE нельзя использовать вместе. Опции MENUBREAK и MENUBARBREAK нельзя использовать вместе. Если число пунктов всплывающего меню больше, чем можно разместить в одном столбце, необходимо использовать либо MENUBREAK, либо MENUBARBREAK.
Значения идентификаторов в инструкциях MENUITEM — это числа, которые Windows посылает оконной процедуре в сообщениях меню. Значение идентификатора должно быть уникальным в рамках меню. Вместо чисел может оказаться удобнее использовать идентификаторы, определенные в заголовочном файле. По договоренности эти идентификаторы начинаются с символов IDM (ID for a menu, идентификатор меню).
Ссылки на меню в вашей программе
Во многих приложениях Windows в описании ресурсов имеется только одно меню. Ссылка в программе на это меню имеет место в определении класса окна:
wndclass.lpszMenuName = "MyMenu" ;
Часто программисты используют строку имени программы в качестве имени меню, имени класса окна, имени значка программы. Однако, вместо имени для меню можно использовать число (или идентификатор). Тогда описание ресурса выглядело бы так:
45 MENU { [определение меню] }
В этом случае, оператор присваивания для поля IpszMenuName структуры класса окна может быть либо такой:
wndclass.lpszMenuName = MAKEINTRESOURCE (45) ;
либо такой:
wndclass.lpszMenuName = "#45" ;
Хотя задание имени меню в классе окна является наиболее обычным способом ссылки на ресурс меню, существуют альтернативные варианты. В приложении для Windows ресурс меню можно загрузить в память с помощью функции LoadMenu, которая аналогична функциям Loadlcon и LoadCursor, описанным в главе 9. Если в описании ресурса меню используете имя, то возвращаемым значением функции LoadMenu является описатель меню:
hMenu = LoadMenu (hinstance, "MyMenu") ;
При использовании числа функция LoadMenu принимает либо такой вид:
hMenu = LoadMenu (hinstance, MAKEINTRESOURCE (45));
либо такой:
hMenu = LoadMenu (hinstance, "#45") ;
Затем этот описатель меню можно указать в качестве девятого параметра функции Create Window.
hwnd = CreateWindow ("MyClass", "Window Caption", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, hMenu, hinstance, NULL) ;
В этом случае меню, указанное при вызове функции CreateWindow, перекрывает любое меню, заданное в классе окна. Меню, задаваемое в классе окна, можно считать заданным по умолчанию меню для окон, созданных на основе данного класса окна, если девятый параметр функции CreateWindow установлен в NULL. Поэтому можно использовать разные меню для различных окон, созданных на основе одного и того же класса окна.
Можно также указать NULL для меню при регистрации класса окна и NULL для меню при вызове функции CreateWindow, а затем присоединить меню к окну следующим образом:
SetMenu (hwnd, hMenu) ;
Любое меню, связанное с окном, удаляется при удалении окна. Любое несвязанное с окном меню перед завершением работы программы должно быть явно удалено при помощи вызова функции DestroyMenu.
Меню и сообщения
Когда пользователь выбирает пункт меню, Windows посылает оконной процедуре несколько различных сообщений. Большинство из этих сообщений могут игнорироваться программой, и просто передаваться DefWindowProc. Одним из таких сообщений является сообщение WM_INITMENU, которое имеет следующие параметры:
wParam – описатель главного меню, lParam – 0.
Значением параметра wParam является описатель главного меню, даже если пользователь выбирает пункт системного меню. В программах для Windows сообщение WM_INITMENU обычно игнорируется. Это сообщение существует для того, чтобы дать вам возможность изменить меню или выполнить другое действие перед тем, как будет выбран пункт меню.
Кроме того, программа получает сообщения WM_MENUSELECT. Если пользователь перемещает курсор мыши по пунктам меню, программа может получить множество сообщений WM_MENUSELECT. Это полезно при использовании строки состояния, содержащей полное описание опции меню. Данное сообщение имеет следующие параметры:
Младшее слово
(LOWORD) wParam
|
| Старшее слово
(HIWORD) wParam
|
| Iparam
|
Выбранный пункт: идентификатор меню или описатель всплывающего меню
|
| Флаги выбора
| | Описатель меню, содержащего выбранный пункт
|
Сообщение WM_MENUSELECT — это сообщение для отслеживания перемещения по меню. Младшее слово параметра wParam говорит о том, какой пункт меню выбран (подсвечен) в данный момент. В старшем слове параметра wParam могут быть комбинации из следующих флагов выбора: MF_G RAYED, MF_DISABLED, MF_CHECKED, MF_BITMAP, MF_POPUP, MF_HELP, MF_SYSMENU и MF_MOUSESELECT. Сообщение WM_MENUSELECT может понадобиться для того, чтобы изменить что-нибудь в рабочей области окна на основе информации о перемещении подсветки по пунктам меню. В большинстве программ это сообщение передается в DefWindowProc.
Когда Windows готова вывести на экран всплывающее меню, она посылает оконной процедуре сообщение WM_INITMENUPOPUP со следующими параметрами:
wParam
|
| Младшее слово
(LOWORD) IParam
| Старшее слово (HIWORD) IParam
|
Описатель всплывающего меню
|
| Индекс всплывающего меню
| Для системного меню 1, в противном случае 0
|
Это сообщение важно, если необходимо разрешать или запрещать пункты меню перед их выводом на экран. Например, предположим, что программа с помощью команды Paste всплывающего меню может копировать текст из буфера обмена. При получении для этого всплывающего меню сообщения WM_INITMENUPOPUP необходимо определить, имеется ли текст в буфере обмена. Если нет, то необходимо сделать этот пункт меню Paste недоступным.
Самым важным сообщением меню является WM_COMMAND. Это сообщение показывает, что пользователь выбрал разрешенный пункт меню окна. Сообщения WM_COMMAND также посылаются дочерними окнами управления. Если оказывается, что для меню и дочерних окон управления используются одни и те же идентификаторы, то различить их можно с помощью значения параметра IParam, который для пункта меню будет равен 0:
| Младшее слово (LOWORD) wParam
| Старшее слово (HIWORD) wParam
| IParam
|
Меню:
Элемент управления:
| Идентификатор меню
Идентификатор элемента управления
|
Код уведомления
|
Описатель дочернего окна
|
Сообщение WM_SYSCOMMAND похоже на сообщение WM_COMMAND за исключением того, что сообщение WM_SYSCOMMAND сигнализирует, что пользователь выбрал разрешенный пункт системного меню:
| Младшее слово (LOWORD) wParam
| Старшее слово (HIWORD) wParam
| IParam
|
Системное меню:
| Идентификатор меню
|
| 0 (Если сообщение WM_SYSCOMMAND является результатом щелчка мыши, тогда старшее и младшее слово IParam являются, соответственно, экранными координатами Х и Y курсора мыши.)
|
Идентификатор меню показывает, какой пункт системного меню выбран. Для предопределенных пунктов системного меню, четыре младших разряда должны быть замаскированы. Результирующая величина будет одной из следующих:
SC_SIZE, SC_MOVE, SC_MINIMIZE, SC_MAXIMIZE, SC_NEXTWINDOW, SCJPREVWINDOW, SC_CLOSE, SC_VSCROLL, SC_HSCROLL, SC_ARRANGE, SC_RESTORE и SC_TASKLIST. Кроме того, младшее слово параметра wParam может быть SC_MOUSEMENU или SC_KEYMENU.
Если вы добавите к системному меню новые пункты, то младшее слово параметра wParam станет идентификатором, который вы определите. Для избежания конфликта с предопределенными идентификаторами меню, используйте значения меньше чем ОхFООО. Важно, чтобы обычные сообщенияWM_SYSCOMMAND передавались в DefWindowProc. Если этого не сделать, то обычные команды системного меню будут отключены.
Последним сообщением, которое мы обсудим, является сообщение WM_MENUCHAR, которое, в действительности, не является собственно сообщением меню. Windows посылает это сообщение оконной процедуре в одном из двух случаев: если пользователь нажимает комбинацию клавиши <Alt> и символьной клавиши, несоответствующей пунктам меню, или при выводе на экран всплывающего меню, если пользователь нажимает символьную клавишу, не соответствующую пункту всплывающего меню. Параметры сообщения WM_MENUCHAR являются следующими:
Младшее слово (LOWORD) wParam
| Старшее слово (HIWORD) wParam
| Iparam
|
Код ASCII
| Код выбора
| Описатель меню
|
Код выбора равен:
• 0 — всплывающее меню не отображается.
• MF_POPUP — отображается всплывающее меню.
• MF_SYSMENU — отображается системное меню.
Как правило, в программах для Windows это сообщение передается DejWindowProc, которая обычно возвращает 0 в Windows, после чего Windows издает звуковой сигнал (гудок).
Этикет при организации меню
Формат всплывающих меню File и Edit в программах для Windows обычно имеет аналогичный вид. Одной из задач Windows является обеспечение пользователя таким интерфейсом, при котором для каждой новой программы не требуется изучение базовых концепций. Этому, несомненно, помогает то, что меню File и Edit выглядят одинаково в любой программе для Windows, а также то, что для выбора используются одни и те же комбинации символьных клавиш и клавиши <Alt>.
За исключением всплывающих меню File и Edit, остальные пункты меню программ для Windows отличаются от программы к программе. При создании меню вам необходимо изучить существующие программы для Windows и стремиться к поддержанию некоторого стандарта. Конечно, если вы считаете эти другие программы неправильными, и знаете лучший путь их создания, никто не посмеет вас остановить. Кроме этого запомните, что для исправления меню обычно требуется исправить только файл описания ресурсов, а не код программы.
Сложный способ определения меню
Определение меню в файле описания ресурсов — это, как правило, простейший способ добавить меню к окну программы, но этот способ — не единственный. Вы можете обойтись без файла описания ресурсов и, с помощью вызовов функций CreateMenu и AppendMenu, создать все меню внутри программы. После завершения определения меню, можно передать описатель меню функции Create Window или использовать функцию SetMenu для установки меню окна.
Теперь о том, как это делается. Возвращаемым значением функции CreateMenu является просто описатель нового меню:
hMenu = CreateMenu () ;
В исходном состоянии меню не содержит ни одного элемента. Элементы в меню вставляются с помощью функции AppendMenu. Вам необходимо получить свой описатель меню для каждого пункта главного меню и для каждого всплывающего меню. Всплывающие меню строятся отдельно, а затем их описатели вставляются в меню верхнего уровня.
hMenu = CreateMenu( ) ;
hMenuPopup = CreateMenu ( ) ;
AppendMenu (hMenuPopup, MF_STRING, IDM_NEH, "&New") ;
AppendMenu (hMenuPopup, MF_STRING, IDM_OPEN, "&Open...");
AppendMenu (hMenuPopup, MF_STRING, IDM SAVE, "&Save");
AppendMenu (hMenuPopup, MF_STRING, IDM_SAVEAS, "Save &As...") ;
AppendMenu (hMenuPopup, MF_SEPARATOR, 0, NULL) ;
AppendMenu (hMenuPopup, MP_STRING, IDM_EXIT, "E&xit") ;
AppendMenu (hMenu, MF_POPUP, (UINT) hMenuPopup, "&File") ;
hMenuPopup = CreateMenu () ;
AppendMenu (hMenuPopup, MF STRING, IDM UNDO, "&Undo") ;
AppendMenu (hMenuPopup, MF_SEPARATOR, 0, NULL) ;
AppendMenu (hMenuPopup, MF_STRING, IDM_CUT, "Cu&t") ;
AppendMenu (hMenuPopup, MF_STRING, IDM_COPY, "&Copy") ;
AppendMenu (hMenuPopup, MP_STRING, IDM_PASTE, "&Paste") ;
AppendMenu (hMenuPopup, MF_STRING, IDMJ3EL, "De&lete") ;
AppendMenu (hMenu, MF_POPUP, (UINT) hMenuPopup, "&Edit") ;
hMenuPopup' = CreateMenu () ;
AppendMenu (hMenuPopup, MF_STRING I MF_CHECKED, IDM_WHITE, "&White") ;
AppendMenu (hMenuPopup, MF_STRING, IDM_LTGRAY, "&Lt Gray") ;
AppendMenu (hMenuPopup, MF_STRING, IDM_GRAY, "&Gray") ;
AppendMenu (hMenuPopup, MF_STRING, IDM_DKGRAY, "&Dk Gray") ;
AppendMenu (hMenuPopup, MF_STRING, IDM_BLACK, "&SBlack") ;
AppendMenu (hMenu, MF_POPUP, (UINT) hMenuPopup, "{&Background") ;
hMenuPopup = CreateMenu () ;
AppendMenu (hMenuPopup, MF STRING, IDM START, "&Start") ;
AppendMenu (hMenuPopup, MF_STRING I MF_GRAYED, IDM_STOP, "S&top") ;
AppendMenu (hMenu, MF_POPUP, (UINT) hMenuPopup, "&Timer") ;
hMenuPopup = CreateMenu () ;
AppendMenu (hMenuPopup, MF_STRING, IDM_HELP, "&Help...") ;
AppendMenu (hMenuPopup, MF_STRING, IDM_ABOUT, "&About MenuDemo...") ;
AppendMenu (hMenu, MF_POPUP, (UINT) hMenuPopup, "&Help") ;
Не рекомендуется определять меню таким образом, а только показано, как это можно сделать. Несомненно, что вы могли бы существенно уменьшить размер кода,используя массивы структур, содержащие строки символов,идентификаторы и флаги всех пунктов меню. Действуя таким образом, вы получите преимущества при использовании третьего способа определения меню.
Независимые всплывающие меню
Вы также можете создать меню без строки главного меню. Вместо нее можно создать всплывающее меню, которое будет появляться в любой части экрана. Один из подходов состоит в том, что всплывающее меню должно появляться при щелчке правой кнопки мыши. Однако, сами пункты меню по-прежнему должны выбираться левой кнопкой мыши.
В файле описания ресурсов меню определяется почти так, как ранее. Отличие состоит только в том, что главное меню содержит только один пункт — всплывающее меню, содержащее опции File, Edit, Background и Help.
hMenu = LoadMenu (hinst, szAppName) ;
hMenu = GetSubMenu (hMenu, 0) ;
При обработке сообщения WM_RBUTTONDOWN, программа получает положение указателя мыши, преобразует это положение в координаты экрана и передает их функции TrackPopupMenu:
point.x = LOWORD (IParam) ;
point.у = HIWORD (IParam) ;
ClientToScreen (hwnd, Spoint) ;
TrackPopupMenu (hMenu, 0, point.x, point.y, 0, hwnd, NULL) ;
Затем Windows выводит на экран всплывающее меню с пунктами File, Edit, Background и Help. Выбор любого из этих пунктов приводит к тому, что вложенное всплывающее окно меню появляется на экране правее выбранной опции. Функции этого меню те же, что и у обычного меню.
Изменение меню
Мы уже видели, как функция AppendMenu может использоваться для определения меню в целом внутри программы и добавления пунктов к системному меню. До появления Windows 3.0 для выполнения этой работы использовалась функция ChangeMenu. Функция ChangeMenu была столь многогранной по своим задачам, что была одной из наиболее сложных функций в Windows. В Windows 95 эта функция по-прежнему имеется, но ее задачи распределены между пятью новыми функциями:
• AppendMenu — добавляет новый элемент в конец меню.
• DeleteMenu — удаляет существующий пункт меню и уничтожает его.
• InsertMenu — вставляет в меню новый пункт.
• ModifyMenu — изменяет существующий пункт меню.
• RemoveMenu — удаляет существующий пункт меню.
Отличие между функциями DeleteMenu и RemoveMenu весьма важно, если указанный пункт меню является всплывающим меню. Функция DeleteMenu уничтожает всплывающее меню, а функция RemoveMenu — нет.
Другие команды меню
Для работы с меню имеется еще несколько полезных функций.
Если изменяется пункт главного меню, изменения не произойдет, пока Windows не перерисует строку меню. Вызвав функцию DrawMenuBar, можно форсировать эту операцию:
DrawMenuBar (hwnd) ;
Описатель всплывающего меню можно получить с помощью функции GetSubMenu:
hMenuPopup = GetSubMenu (hMenu, iPosition) ;
где iPosition — это индекс (отсчитываемый с 0) всплывающего меню внутри главного меню, которое задается параметром hMenu. Затем, полученный описатель всплывающего меню hMenuPopup можно использовать в других функциях, например, AppendMenu.
Текущее число пунктов главного или всплывающего меню можно получить с помощью функции GetMenuItemCount:
iCount = GetMenuItemCount (hMenu) ;
Идентификатор меню для пункта всплывающего меню можно получить следующим образом:
id = GetMenuItemID (hMenuPopup, iPosition) ;
Установить или удалить метку пункта всплывающего меню с помощью функции
CheckMenuItem (hMenu, id, iCheck) ;
hMenu - описатель главного меню, id — идентификатором меню, а значение параметра iCheck равно либо MF_CHECKED, либо MF_UNCHECKED. Если hMenu является описателем всплывающего меню, то параметр id может стать не идентификатором меню, а индексом положения. Если пользоваться этим индексом удобнее, то в третьем параметре указывается флаг MF_BYPOSITION. Например:
CheckMenuItem (hMenu, iPosition, MF_CHECKED I MF_BYPOSITION) ;
Работа функции EnableMenuItem похожа на работу функции CheckMenuItem за исключением того, что третьим параметром может быть MF_JENABLED, MF_DISABLED или MF_GRAYED.
EnableMenuItem ((HMENU) wParam, IDM_PASTE, MF_ENABLED) ;
Если используется функция EnableMenuItem для пункта главного меню, содержащего всплывающее меню, то в качестве третьего параметра следует использовать идентификатор MF_BYPOSITION, поскольку этот пункт меню не имеет идентификатора меню.
Функция HiliteMenuItem напоминает функции CheckMenuItem и EnableMenuItem, но использует идентификаторы MF_HILITE и MF_UNHILITE. Эта функция обеспечивает инверсное изображение, которое Windows использует, когда вы перемещаете указатель от одного из пунктов меню к другому. Обычным приложениям нет необходимости использовать функцию HiliteMenuItem.
Что еще нужно сделать с меню? Если вы забыли, какие символьные строки использовались в вашем меню, то освежить память можно следующим образом:
iByteCount = GetMenuString (hMenu, id, pString, iMaxCount, iFlag) ;
Параметр iFlag равен либо MF_BYCOMMAND (при этом id — это идентификатор меню), либо MF_JBYPOSITION (при этом id — это индекс положения). Функция копирует iMaxCount байтов строки символов в pString и возвращает число скопированных байтов.
Может быть, вы хотите узнать, каковы текущие флаги пункта меню:
iFlags = GetMenuState (hMenu, id, iFlag) ;
И снова, параметр iFlag равен либо MF_BYCOMMAND, либо MF_BYPOSITION. Возвращаемое значение функции iFlags — это комбинация всех текущих флагов. Вы можете определить текущие флаги, проверив iFlags с помощью идентификаторов MF_DISABLED, MF_GRAYED, MF_CHECKED, MF_MENUBREAK, MF_MENUBARBREAK и MF_SEPARATOR.
А может быть, вам уже слегка надоело меню. В таком случае, если меню вам больше не нужно, его можно удалить:
DestroyMenu (hMenu) ;
Эта функция делает недействительным описатель меню.