4.2. Создание меню
Алгоритм создания меню в общем случае содержит следующие шаги:
Создание пустого меню hMenu.
Вставка элемента в меню hMenu.
Шаг 2 повторяем столько раз, сколько элементов содержит меню hMenu.
Этот алгоритм позволяет создавать многоуровневое меню. Например, вставляемый на 2-м шаге элемент может указывать на уже созданное временное меню. Причем любой элемент последнего также может указывать на временное меню.
Меню сначала создают только в памяти. Существуют функции, которые операционной системе указывают, какое меню создано, какому окну оно принадлежит и когда его отображать. Рассмотрим эти функции.
Функция CreateMenu создает пустое главное меню окна:
HMENU CreateMenu(VOID);
В случае успешного выполнения функция CreateMenu возвращает дескриптор созданного главного меню, иначе – NULL.
Функция CreatePopupMenu создает пустое временное меню:
HMENU CreatePopupMenu(VOID);
В случае успешного выполнения функция CreatePopupMenu возвращает дескриптор созданного временного меню, иначе – NULL.
Функция SetMenu подключает главное меню hMenu к окну hwnd:
BOOL SetMenu(HWND hwnd, HMENU hMenu );
Она удаляет предыдущее меню, но не разрушает его. Если же hMenu = NULL, то только удаляется главное меню. В случае успешного подключения функция возвращает ненулевое значение.
После изменений в меню, независимо от состояния окна hwnd, для перерисовки полосы меню нужно вызвать функцию DrawMenuBar:
void WINAPI DrawMenuBar(HWND hwnd);
Она перерисовывает полосу меню окна hwnd.
Таким образом, алгоритм подключения главного меню к окну и отображения полосы меню содержит следующие шаги:
Создание главного меню. Этот шаг полностью совпадает с алгоритмом создания меню.
Подключение главного меню к окну.
Перерисовка полосы меню окна.
После этого работу с меню обеспечивает операционная система.
Приложение в созданное меню может вставлять новые элементы или удалять старые, а также может изменять или запрашивать текущее состояние любого элемента.
4.2.1. Вставка элементов в меню
Функция InsertMenuItem вставляет элемент в меню hMenu:
BOOL InsertMenuItem(HMENU hMenu, UINT uItem,
BOOL fByPosition, LPMENUITEMINFO lpmii);
Значение аргумента uItem зависит от значения fByPosition. При fByPosition = FALSE значение uItem равно идентификатору команды вставляемого элемента, иначе – позиции того элемента в меню, перед которым нужно вставить новый элемент. Если меню содержит n элементов и нужно вставить новый элемент последним, то номер позиции приравнивают n (n = 0, 1, 2, ...).
Параметр lpmii указывает на структуру типа MENUITEMINFO, которая уже содержит информацию о вставляемом элементе.
В случае успешной вставки элемента функция возвращает ненулевое значение.
Задача. Описать функцию для вставки элемента в меню.
Нижеследующий фрагмент может служить примером решения данной задачи.
BOOL CreateMenuItem(HMENU hMenu, char *str, UINT uIns, UINT uCom, HMENU hSubMenu, BOOL flag, UINT fType)
{
MENUITEMINFO mii;
mii.cbSize = sizeof(MENUITEMINFO);
mii.fMask = MIIM_STATE | MIIM_TYPE | MIIM_SUBMENU | MIIM_ID;
mii.fType = fType;
mii.fState = MFS_ENABLED;
mii.dwTypeData = str;
mii.cch = sizeof(str);
mii.wID = uCom;
mii.hSubMenu = hSubMenu;
return InsertMenuItem(hMenu, uIns, flag, &mii);
}
Рассмотрим список формальных параметров этой функции.
hMenu – дескриптор меню, в который вставляется элемент.
str указывает на текст отображения строки меню.
uIns равноценен параметру uItem функции InsertMenuItem.
uCom, когда он ненулевой, исполняет роль параметра wID функции InsertMenuItem.
hSubMenu – дескриптор временного меню, которое должно появиться при выборе строки str в меню hMenu.
flag совпадает с параметром fByPosition в InsertMenuItem.
fType принимает те же значения, что и поле fType структуры MENUITEMINFO.
Задача. Главное меню содержит разделы "Файлы" и "Правка". При выборе строки "Файлы" отображается временное меню со строками "Открыть", "Сохранить" и "Выход". При выборе строки "Правка" отображается временное меню со строками "Найти" и "Заменить".
Листинг 4.1. Пример приложения со статическим меню.
#include <windows.h>
#define CM_FILE_OPEN 1001
#define CM_FILE_SAVE 1002
#define CM_FILE_QUIT 1003
#define CM_EDIT_FIND 2001
#define CM_EDIT_REPLC 2002
BOOL RegClass(WNDPROC, LPCTSTR, UINT);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE hInstance;
TCHAR szClass[] = TEXT("MenuWindow");
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpszCmdLine, int nCmdShow)
{
MSG msg;
HWND hwnd;
hInstance = hInst;
if (!RegClass(WndProc, szClass, COLOR_WINDOW)) return FALSE;
hwnd = CreateWindow(szClass, TEXT("Приложение со статическим меню"), WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, hInstance, NULL);
if (!hwnd) return FALSE;
while(GetMessage(&msg, 0, 0, 0))
{ TranslateMessage(&msg); DispatchMessage(&msg); }
return msg.wParam;
}
BOOL RegClass(WNDPROC Proc, LPCTSTR szName, UINT brBackground)
{
WNDCLASS wc;
wc.style = wc.cbClsExtra = wc.cbWndExtra = 0;
wc.lpfnWndProc = Proc;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(brBackground +1);
wc.lpszMenuName = NULL;
wc.lpszClassName = szName;
return (RegisterClass(&wc) != 0);
}
BOOL CreateMenuItem(HMENU hMenu, TCHAR *str, UINT uIns, UINT uCom, HMENU hSubMenu, BOOL flag, UINT fType)
{
MENUITEMINFO mii;
mii.cbSize = sizeof(MENUITEMINFO);
mii.fMask = MIIM_STATE | MIIM_TYPE | MIIM_SUBMENU | MIIM_ID;
mii.fType = fType;
mii.fState = MFS_ENABLED;
mii.dwTypeData = str;
mii.cch = sizeof(str);
mii.wID = uCom;
mii.hSubMenu = hSubMenu;
return InsertMenuItem(hMenu, uIns, flag, &mii);
}
LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static HMENU hMainMenu, hFileMenu, hEditMenu;
switch (msg)
{
case WM_CREATE:
{
hMainMenu = CreateMenu();
//Создаем временное меню для раздела "Файлы"
hFileMenu = CreatePopupMenu();
int i=0; //Инициализация позиции в меню hFileMenu
CreateMenuItem( hFileMenu, TEXT("&Открыть"), i++, CM_FILE_OPEN, NULL, FALSE, MFT_STRING);
CreateMenuItem( hFileMenu, TEXT("&Сохранить"), i++, CM_FILE_SAVE, NULL, FALSE, MFT_STRING);
CreateMenuItem( hFileMenu, NULL, i++, 0, NULL, FALSE, MFT_SEPARATOR);
CreateMenuItem( hFileMenu, TEXT("&Выход"), i++, CM_FILE_QUIT, NULL, FALSE, MFT_STRING);
//Создаем временное меню для раздела "Правка"
hEditMenu = CreatePopupMenu();
i=0; //Инициализация позиции в меню hEditMenu
CreateMenuItem( hEditMenu, TEXT("&Найти"), i++, CM_EDIT_FIND, NULL, FALSE, MFT_STRING);
CreateMenuItem( hEditMenu, TEXT("&3аменить"), i++, CM_EDIT_REPLC, NULL, FALSE, MFT_STRING);
//Подключаем временные меню к главному меню
i=0; //Инициализация позиции в меню hMainMenu
CreateMenuItem( hMainMenu, TEXT("&Файл"), i++, 0, hFileMenu, FALSE, MFT_STRING);
CreateMenuItem( hMainMenu, TEXT("&Правка"), i++, 0, hEditMenu, FALSE, MFT_STRING);
SetMenu( hwnd, hMainMenu);
DrawMenuBar( hwnd);
return 0;
}
case WM_COMMAND:
{
switch (LOWORD(wParam))
{
case CM_FILE_OPEN:
{ MessageBox(hwnd, TEXT("Команда CM_FILE_OPEN"), TEXT("Меню"), MB_OK); return 0; }
case CM_FILE_SAVE:
{ MessageBox(hwnd, TEXT("Команда CM_FILE_SAVE"), TEXT("Меню"), MB_OK); return 0; }
case CM_EDIT_FIND:
{ MessageBox(hwnd, TEXT("Команда CM_EDIT_FIND"), TEXT("Меню"), MB_OK); return 0; }
case CM_EDIT_REPLC:
{ MessageBox(hwnd, TEXT("Команда CM_EDIT_REPLC"), TEXT("Меню"), MB_OK); return 0; }
case CM_FILE_QUIT: { DestroyWindow(hwnd); return 0; }
}
return 0;
}
case WM_DESTROY: { PostQuitMessage(0); return 0; }
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
Это приложение демонстрирует применение рассмотренной выше функции CreateMenuItem для создания статического меню. Меню названо статическим потому, что в процессе работы приложения оно не изменяется. Рассмотрим основные шаги создания меню.
Описывают дескрипторы для создаваемых меню:
static HMENU hMainMenu, hFileMenu, hEditMenu;
При обработке сообщения WM_CREATE создают всю систему меню (хотя не запрещается это делать и в любом другом месте).
Создают пустое главное меню окна:
hMainMenu = CreateMenu();
Создают временное меню для раздела "Файлы".
Создают пустое временное меню:
hFileMenu = CreatePopupMenu();
Вставляют элементы в меню hFileMenu:
CreateMenuItem( hFileMenu, "&Открыть", i++, CM_FILE_OPEN,
NULL, FALSE, MFT_STRING);
CreateMenuItem( hFileMenu,"&Сохранить", i++, CM_FILE_SAVE,
NULL, FALSE, MFT_STRING);
CreateMenuItem( hFileMenu, NULL, i++, 0,
NULL, FALSE, MFT_SEPARATOR);
CreateMenuItem( hFileMenu, "&Выход", i++, CM_FILE_QUIT,
NULL, FALSE, MFT_STRING);
Здесь аргумент "&Открыть" обозначает ту строку, которая будет отображена во временном меню. Ключ & обозначает, что символ "О" можно использовать для быстрого доступа к данной строке меню. Аргумент CM_FILE_OPEN является идентификатором той команды, которую получит функция окна при выборе строки "&Открыть".
Так же вставляют второй и четвертый элементы меню. Третьим элементом является разделительная линия.
Создают временное меню для раздела "Правка".
Создают пустое временное меню:
hEditMenu = CreatePopupMenu();
Вставляют элементы в меню hEditMenu:
CreateMenuItem( hEditMenu, "&Найти", i++, CM_EDIT_FIND,
NULL, FALSE, MFT_STRING);
CreateMenuItem( hEditMenu,"&3aMeHHTb", i++, CM_EDIT_REPLC,
NULL, FALSE, MFT_STRING );
Подключают временные меню hFileMenu и hEditMenu к главному меню hMainMenu:
CreateMenuItem( hMainMenu, "&Файл", i++,
0, hFileMenu, FALSE, MFT_STRING);
CreateMenuItem( hMainMenu, "&Правка", i++,
0, hEditMenu, FALSE, MFT_STRING);
Подключают главное меню к окну и перерисовывают полосу меню:
SetMenu(hwnd, hMainMenu);
DrawMenuBar(hwnd);
Приложение обрабатывает команды меню. Как можно увидеть из листинга, обработка команд от меню ничем не отличается от обработки команд от других органов.
