Сообщений 3 Оценка 13 Оценить |
Прежде чем отображать контекстное меню, его необходимо загрузить из ресурсов приложения или создать его прямо на лету. Рассмотрим оба способа.
Меню создаётся с помощью функции CreatePopupMenu. Это очень простая функция, которая не принимает параметров и возвращает хэндл созданного меню. В этом меню изначально нет ни одного пункта. Добавить пункты можно, используя функции AppendMenu и InsertMenuItem. Например:
HMENU hPopupMenu = CreatePopupMenu(); AppendMenu(hPopupMenu, MF_STRING, ID_EDIT_UNDO, "&Undo\tCtrl+Z"); // Undo Ctrl+Z AppendMenu(hPopupMenu, MF_SEPARATOR, 0, 0); // -------------- AppendMenu(hPopupMenu, MF_STRING, ID_EDIT_CUT, "Cu&t\tCtrl+X"); // Cut Ctrl+X AppendMenu(hPopupMenu, MF_STRING, ID_EDIT_COPY, "&Copy\tCtrl+C"); // Copy Ctrl+C AppendMenu(hPopupMenu, MF_STRING, ID_EDIT_PASTE, "&Paste\tCtrl+V"); // Paste Ctrl+V |
Этот код создаёт стандартное меню, содержащее пункты Undo/Cut/Copy/Paste.
Загрузить меню из ресурсов приложения можно, используя функцию LoadMenu. Передавайте ей дескриптор модуля и идентификатор ресурса, содержащего нужное меню. Теоретически, можно использовать также функцию LoadMenuIndirect, но на практике контекстное меню редко отображают столь окольными путями.
Меню, которое размещается в ресурсах приложения, не является всплывающим, и его невозможно отобразить как контекстное. Поэтому вы должны поучить одно из его подменю с помощью функции GetSubMenu и отобразить его. Учитывая эту особенность, программисты часто создают в редакторе ресурсов фиктивное меню с одним единственным пунктом, с которым и связывается нужное всплывающее меню (рисунок 1).
Рисунок 1
ПРИМЕЧАНИЕ В некоторых случаях можно даже не создавать отдельного меню, а "позаимствовать" одно из подменю главного. Например, при щелчке правой кнопкой на панели инструментов или строке состояния уместно отобразить подменю View из главного меню приложения. |
Вот фрагмент кода, который загружает меню из ресурсов, а затем извлекает самое первое подменю.
HMENU hMenu, hSubMenu;
hMenu = LoadMenu(hInstance, MAKEINTRESOURCE(IDR_MENU1));
hSubMenu = GetSubMenu(hMenu, 0);
|
Для отображения контекстного меню в Win32 API предусмотрена функция TrackPopupMenu(Ex). В MFC ей соответствует функция CMenu::TrackPopupMenu. Все эти функции выполняют следующие действия: отображают контекстное меню в заданной точке экрана, запускают собственный цикл сообщений, в котором отслеживают выбор пользователя, а в конце, когда пользователь выбрал какой-то пункт или закрыл меню, уведомляют программу о его выборе.
Рассмотрим прототип функции TrackPopupMenu.
BOOL TrackPopupMenu( HMENU hMenu, // хэндл всплывающего меню UINT uFlags, // опции int x, // горизонтальная позиция int y, // вертикальная позиция int nReserved, // зарезервированный параметр, д. б. 0 HWND hWnd, // хэндл окна-владельца меню CONST RECT *prcRect // параметр игнорируется ); |
Параметр hMenu определяет всплывающее меню, которое следует отобразить. Параметры x и y задают положение меню на экране, а hWnd - окно, которое будет получать все сообщения от меню (этот параметр не может быть равен NULL). Параметры nReserved и prcRect не используются. Что касается опций, их полный список можно найти в документации. Я хочу обратить ваше внимание только на флаг TPM_RETURNCMD. Если он не задан, программа получит уведомление о выборе пользователя в виде сообщения WM_COMMAND. Если же его задать, функция TrackPopupMenu просто вернёт в программу идентификатор выбранного пользователем пункта меню, не отправляя никаких сообщений.
По окончании работы с меню его следует уничтожить вызовом DestroyMenu. Эта функция получает один параметр - хэндл меню, которое подлежит уничтожению. Обратите внимание, что меню, полученное с помощью GetSubMenu, уничтожать не надо. Оно будет удалено в процессе уничтожения родительского меню, которое вы загрузили с помощью LoadMenu.
Итак, мы научились отображать контекстное меню. Осталось ответить на последний вопрос: в каком месте программы вызывать рассмотренные выше функции. Первое, что приходит в голову - отобразить меню в ответ на сообщение WM_RBUTTONUP. Но предпочтительнее делать это в обработчике другого сообщения - WM_CONTEXTMENU. У этого сообщения есть целый ряд преимуществ перед WM_RBUTTONUP.
Вот полный пример обработчика WM_CONTEXTMENU.
case WM_CONTEXTMENU: { // Извлекаем координаты курсора мыши из lParam POINT pt; pt.x = GET_X_LPARAM(lParam); pt.y = GET_Y_LPARAM(lParam); if(pt.x == -1 && pt.y == -1) { // Вызов с клавиатуры RECT rect; GetWindowRect(hWnd, &rect); // Выводим меню радом с левым верхним углом окна pt.x = rect.left + 5; pt.y = rect.top + 5; } // Загружаем меню из ресурсов HMENU hMenu, hPopupMenu; hMenu = LoadMenu(hInstance, MAKEINTRESOURCE(IDR_MENU1)); hPopupMenu = GetSubMenu(hMenu, 0); // Отображаем меню TrackPopupMenu(hPopupMenu, TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, 0, hWnd, NULL); // Уничтожаем меню DestroyMenu(hMenu); } |
В этом примере для извлечения координат курсора мыши используются макросы GET_X_LPARAM и GET_Y_LPARAM. Эти и многие другие полезные макросы описаны в заголовочном файле windowsx.h. |
В MFC можно использовать этот код без изменений или переписать его через класс CMenu.
Если вы программируете с использованием MFC, у вас есть счастливая возможность переложить работу по созданию контекстного меню на среду Visual C++. Для этого откройте окно Project->Add To Project->Components and Controls, а затем выберите компонент Pop-up Menu из папки Visual C++ Components. Появится ещё одно окно, в котором вам предложат выбрать, к какому окну вы хотите добавить контекстное меню, а также идентификатор ресурса меню. Вводите нужные параметры, жмите ОК, и в выбранный класс добавится готовый обработчик OnContextMenu. Кроме этого, в ресурсы вашего приложения добавится новое меню, и вам останется только подредактировать его.
Сообщений 3 Оценка 13 Оценить |