В этой статье я вас ознакомлю с классом CMenu.
С помощью этого класса можно создавать меню в MFC приложениях. Так же будет
рассмотрен вопрос об использовании акселераторов. Итак, начнем с описания класса
CMenu.
Класс CMenu
Рассмотрим некоторые наиболее важные функции и
переменные этого класса.
° m_hMenu ? дескриптор меню, связанный с
объектом класса CMenu.
° BOOL CreateMenu() ? создает пустое меню и связывает
его с объектом класса CMenu.
° BOOL CreatePopupMenu() ? создает пустое
выпадающее меню и соединяет с объектом класса CMenu.
° BOOL LoadMenu(UINT
nIDResource) ? загружает меню из ресурсов и соединяет его с объектом класса
CMenu. Меню может быть создано программным путем метода заполнения структур
MENUITEMTEMPLATEHEADER и MENUITEMTEMPLATE и посредством вызова метода
LoadMenuIndirect(const MUNUTEMPLATE *lpMenuTemplate).
° BOOL Attach(HMENU
hMenu) ? пристыковка существующего меню к объекту класса CMenu. Если известен
дескриптор меню, то можно определить указатель на соответствующий ему объект
CMenu с помощью следующего метода static CMenu* PASCAL FromHandle(HMENU hMenu).
° BOOL DestroyMenu() ? разрушает объект CMenu и освобождает все ресурсы.
Этот метод обычно не вызывается явно, так как он автоматически вызывается из
деструктора.
° BOOL DeleteMenu(UINT uPosition, UINT uFlags) ? метод удаляет
указанный элемент меню. uPosition ? определяет удаляемый элемент меню. uFlags ?
определяет, как интерпретируется первый параметр. Если флаг равен MF_BYCOMMAND,
то первый параметр интерпретируется как идентификатор пункта меню. Если параметр
равен MF_BYPOSITION, то первый параметр определяет позицию элемента меню. Первый
элемент имеет позицию ноль.
° BOOL TrackPopupMenu ( UINT nFlags, int x, int
y, CWnd* pWnd, LPCRECT lpRact = 0 ) ? метод осуществляет отображение
всплывающего (контекстного) меню. Всплывающее меню может быть отображено в любом
месте экрана. Метод автоматически отслеживает выбор пункта меню. nFlags ? может
быть задан следующими константами: TPM_CENTERALIGN, TMP_LEFTALIGN,
TMP_RIGHTALIGN ? для указания позиции выравнивания по горизонтали относительно
координаты x, TMP_LEFTBUTTON, TMP_RIGHTBUTTON ? для определения кнопки мыши,
выполняющей выбор пункта меню. Координаты x, y ? позиция меню. pWnd ? указатель
на окно, в котором находится это меню. Параметр lpRect ? определяет
прямоугольную область, в которой щелчок мыши не вызывает исчезновение меню. Если
значение параметра NULL, то область ограничена окном меню.
° BOOL
AppenedMenu(UINT nFlags, UINT_PTR nIDNewItem = 0, LPCTSTR lpszNewItem = NULL) и
BOOL AppenedMenu(UINT nFlags, UINT_PTR nIDNewItem, const CBitmap* pBmp)
Используются для добавления элемента в конец меню. Параметр nFlags может быть
комбинацией следующих констант: MF_CHECKED ? переключаемый элемент, состояние
?включен? (отмечается галочкой). MF_UNCHECKED ? переключаемый элемент, состояние
?выключен? (галочки нет). MF_DISABLED ? элемент не лоступен. MF_ENABLED ?
элемент доступен. MF_GRAYED ? 2серый? элемент (блокирован). MF_MENUBARBREAK ?
элемент линейки меню. MF_OWNERDRAW ? самоотображаемый элемент., меня само
отвечает за отображение элемента. MF_POPUP ? элемент меню имеет подменю.
MF_SEPARATOR ? отображение горизонтальной разделительной линии. MF_STRING ?
элемент является текстовой строкой. Параметр nIDNewItem ? в зависимости от
первого параметра отпеделяет либо идентификаторор нового элемента, либо
указатель (HMENU) подменю; игнорируется для значения MF_SEPARATOR. Параметр
lpszNewItem определяет содержимое нового элемента. Параметр pBmp ? определяет
указатель на объект класса CBitmap, который может использоваться в качестве
содержимого строки меню. При этом первый параметр не может принимать значения
MF_OWNERDRAW и MF_STRING.
° BOOL InsertMenu(UINT nPosition, UINT nFlags,
UINT_PTR nIDNewItem = 0, LPCTSTR lpszNewItem = NULL) и BOOL InsertMenu(UINT
nPosition, UINT nFlags, UINT_PTR nIDNewItem = 0, const CBitmap* pBmp) ?
поставляют новый элемент в позицию, определяемцю параметром nPosition.
°
UINT CheckMenuItem(UINT nIDCheckItem, UINT nCheck) ? устанавливает или
сбрасывает галочку у элемента меню. Второй параметр может принимать два
значения: MF_CHECKED и MF_UNCHECKED в комбинации с параметрами MF_BYCOMMAND и
MF_BYPOSITION.
° UINT EnableMenuItem(UINT nIDEnableItem, UINT nEnable) ?
метод устанавливает для указанного элемента одно из следующих значений:
MF_DISABLES и MF_ENABLED, MF_GRAYED.
° UINT GetMenuItemCount() ? возвращает
количество элементов меню.
° UINT GetMenuItemID(int nPos) ? метод возвращает
идентификатор указанного элемента меню.
° Int GetMenuString(UINT nIDItem,
lptstr lpString, int nMaxCount, UINT nFlags) и int GetMenuString(UINT nIDItem,
CString& rString, UINT nFlags) ? определяют надпись на указанном элементе
меню.
° BOOL RemoveMenu(UINT nPosition, UINT nFlags) ? метод удаляет
указанный элемент меню.
° UINT GetMenustate(UINT nID, UINT nFlags) ?
определяет состояние элемента или число элементов в раскрывающемся меню. Второй
параметр определяет то, как интерпретировать первый элемент ? идентификатор или
позиция элемента меню. Для раскрывающегося меню старший байт возвращаемого
значения содержит число элементов в нем, младший байт ? набор флагов. Для меню
верхнего уровня все возвращаемые значения являются набором флагов.
° Хотя
сам редактор меню, предлагаемый Visual Studio.NET, весьма удобен, приведу
структуру меня, то есть то, как оно хранится в текстовом файле ресурсов.
IDR_MENU1 MENU BEGIN POPUP "File" BEGIN MENUITEM "Open",
ID_FILE_OPEN131 MENUITEM "Exit", ID_FILE_EXIT END POPUP "File2"
BEGIN MENUITEM "Open2", ID_FILE2_OPEN2 MENUITEM "Exit2",
ID_FILE2_EXIT2 END POPUP "Help" BEGIN MENUITEM "About",
ID_HELP_ABOUT END END
|
Как видите,
меня имеет идентификатор IDR_MENU1, который, разумеется, имеет числовое
выражение. Если мы посмотрим таблицу Resource Symbols, то увидим, что числовое
выражение этого идентификатора 129 (у автора). Как вы наверно уже знаете,
числовые значения идентификаторов хранятся в файле resource.h. Такие же
идентификаторы имеет каждый элемент меню. Это очень важно, так как в программе
мы фактически имеем дело не с меню, а с его элементами.
Теперь разберем
алгоритм установки меню в диалоговое окно. Для этой цели естественно нужно
выбрать функция OnInitDialog. Определим в начале файла указатель cm: CMenu *
cm;. Следующий шаг ? это создание объекта класса CMenu: cm = new CMenu;. Теперь
загружаем шаблон меню из файла ресурсов: cm->LoadMenu(IDR_MENU!). таким
образом, наш объект наполнился реальным содержанием. Наконец последнее, мы
должны пристыковать получившийся объект к диалоговому окну. Это производится
следующим действием
This->SetMenu(cm);
?This?, как вы
понимаете, указывает на объект ? диалоговое окно, SetMenu ? метод,
осуществляющий желанную нами пристыковку. И все, этого уже достаточно, чтобы в
созданном диалоговом окне появилось и меню. Да, не забудьте, закрывая окно,
удалить меню из памяти, дабы не оставлять за собой мусор. Делается это очень
просто: delete cm.
Конечно, этого еще не достаточно для полноценного
функционирования приложения, так как пункты меню должны работать, то есть при
выборе пункта должна выполняться какая-то процедура. Функцию-обработчик можно
сделать для каждого пункта меню. Делается это точно тек же, как для любых других
элементов диалогового окна. Для этого, находясь в редакторе меню, нужно щелкнуть
правой кнопкой мыши на нужном элементе меню и выбрать Add Event Handler. Далее,
в появившемся окне выбирается имя функции и команда, которую вы хотите
обрабатывать. Разумеется, этой командой будет COMMAND. Кроме того, в окне
следует выбрать класс, где будет производиться обработка. Следует выбрать класс,
объектом которого является наше окно. Далее, оказавшись в нужном файле и в
нужной функции, можете вводить нужный нам фрагмент кода, который будет
выполняться, когда будет выбран соответствующий пунк меню. В принципе этого уже
достаточно, чтобы писать приложения с меню.
Теперь обратимся к отдельному,
но тесно связанному с меню вопросу ? клавишам акселераторам. Дело в том что этот
ресурс используется в основном для быстрого доступа к элементам меню. Для того
чтобы клавиша-акселератор была связана с соответствующим пунктом меню, нужно
чтобы идентификаторы пункта меню и акселератора совпадали. Тогда обработчик
пункта меню и клавиши будет одним и тем же. Но для работы акселератора нужно
сделать еще две вещи.
Во-первых, следует загрузить таблицу акселераторов в
память. Сделать это можно там же, где вы загружаете меню. Но вот для диалогового
окна (точнее в классе CDialog) подходящего метода нет. Придется воспользоваться
функцие API: LoadAccelerators. Загружая таблицу, эта функция возвращает
дискриптор загруженного ресурса. Следует запомнить этот дискриптор в
какой-нибудь глобальной переменной. Наприметр, можно ввести дополнительное
свойство в объект theApp. Первым аргументом функции является дескриптор
приложения, который может быть с помощью глобальной функции
AfxgetInstanceHandle(). Вторым аргументом является идентификатор таблици
акселераторв, преобразованный с помощью макроса MAKEINTRESOURCE.
Итак,
таблица акселераторов загружена, что дальше? Дальше, и это последнее, следует
подвергнуть дополнительной обработке сообщение, приходящее на окно. Для этого
достаточно переписать метод PreTransleteMessage.
BOOL CDemoMenuDlg::PreTranslateMessage(MSG*pMsg) {
::TranslateAccelerator(this->m_hWnd, theApp.h,pMsg); return
CDialog::PreTranslateMessage(pMsg); } Как вы уже понимаете, CDemoMenuDlg
? это объект, соответствующий нашему диалоговому окну. Вот теперь все, наша
таблица акселераторов будет успешно работать в паре с меню. Теория будет не
полной, если я не приведу пример. // DemoMenuDlg.cpp : implementation file
//
#include "stdafx.h" #include "DemoMenu.h" #include
"DemoMenuDlg.h"
#ifdef _DEBUG #define new DEBUG_NEW #endif
// CDemoMenuDlg dialog CMenu* cm;
CDemoMenuDlg::CDemoMenuDlg(CWnd* pParent /*=NULL*/) :
CDialog(CDemoMenuDlg::IDD, pParent) { m_hIcon =
AfxGetApp()->LoadIcon(IDR_MAINFRAME); }
void
CDemoMenuDlg::DoDataExchange(CDataExchange* pDX) {
CDialog::DoDataExchange(pDX); DDX_Control(pDX, IDC_CHECK1, m_check1);
}
BEGIN_MESSAGE_MAP(CDemoMenuDlg, CDialog) ON_WM_PAINT()
ON_WM_RBUTTONUP() ON_WM_QUERYDRAGICON() //}}AFX_MSG_MAP
ON_COMMAND(ID_FILE_OPEN130, OnFileOpen130) ON_BN_CLICKED(IDC_CHECK1,
OnBnClickedCheck1) END_MESSAGE_MAP()
// CDemoMenuDlg message
handlers
BOOL CDemoMenuDlg::OnInitDialog() {
CDialog::OnInitDialog();
// Set the icon for this dialog. The
framework does this automatically // when the application′s main window is
not a dialog SetIcon(m_hIcon, TRUE); // Set big icon SetIcon(m_hIcon,
FALSE); // Set small icon
// TODO: Add extra initialization here
theApp.h=::LoadAccelerators(AfxGetInstanceHandle(),
MAKEINTRESOURCE(IDR_ACCEL));
cm = new CMenu;
cm->LoadMenu(IDR_MENU1); this->SetMenu(cm);
return TRUE;
// return TRUE unless you set the focus to a control }
// If you add
a minimize button to your dialog, you will need the code below // to draw
the icon. For MFC applications using the document/view model, // this is
automatically done for you by the framework.
void
CDemoMenuDlg::OnPaint() { if (IsIconic()) { CPaintDC dc(this);
// device context for painting
SendMessage(WM_ICONERASEBKGND,
reinterpret_cast(dc.GetSafeHdc()), 0);
// Center icon in client
rectangle int cxIcon = GetSystemMetrics(SM_CXICON); int cyIcon =
GetSystemMetrics(SM_CYICON); CRect rect; GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2; int y = (rect.Height() - cyIcon
+ 1) / 2;
// Draw the icon dc.DrawIcon(x, y, m_hIcon); }
else { CDialog::OnPaint(); } }
// The system calls
this function to obtain the cursor to display while the user drags // the
minimized window. HCURSOR CDemoMenuDlg::OnQueryDragIcon() { return
static_cast(m_hIcon); }
BOOL
CDemoMenuDlg::PreTranslateMessage(MSG*pMsg) {
::TranslateAccelerator(this->m_hWnd, theApp.h,pMsg); return
CDialog::PreTranslateMessage(pMsg); }
void
CDemoMenuDlg::OnFileOpen130() { // TODO: Add your command handler code
here MessageBox("Open"); }
void CDemoMenuDlg::OnRButtonUp(UINT
nFlags, CPoint point) { // TODO: Add your command handler code here
CMenu menu;
POINT pt; GetCursorPos(&pt);
menu.LoadMenu(MAKEINTRESOURCE(IDR_MENU2));
menu.GetSubMenu(0)->TrackPopupMenu(0, pt.x, pt.y, this, NULL); }
void CDemoMenuDlg::OnBnClickedCheck1() {
if(!m_check1.GetCheck()) { cm->EnableMenuItem(ID_FILE2_OPEN2,
MF_ENABLED); } else { cm->EnableMenuItem(ID_FILE2_OPEN2,
MF_GRAYED); } }
|