目录
在Win32编程中,使用菜单句柄 HMENU 来标识菜单,在MFC中使用CMenu类对象表示菜单。封装了关于菜单的各种操作成员函数,另外还封装了一个非常重要的成员变量m_hMenu(菜单句柄)
(1)添加菜单资源
(2)将菜单设置到窗口
添加一个菜单
修改 新建 ID 为 ID_NEW
方式一:利用pFrame调用Create函数时,传参。
pFrame->Create(NULL,"MFCCreate",WS_OVERLAPPEDWINDOW,CFrameWnd::rectDefault,NULL,(char*)IDR_MENU1);
根据提供的参数,Create()
函数的参数含义如下:
NULL
?表示窗口的父窗口为默认值,即没有父窗口。"MFCCreate"
?是窗口的标题,将显示在窗口的标题栏上。WS_OVERLAPPEDWINDOW
?是窗口的样式标志,表示创建一个具有标题栏、边框和控制菜单的重叠窗口。CFrameWnd::rectDefault
?是窗口的初始位置和大小。CFrameWnd::rectDefault
?是一个常量,表示使用默认的大小和位置。NULL
?表示没有指定父窗口,因为这是一个顶级窗口。(char*)IDR_MENU1
?是窗口的菜单资源标识符,用于关联窗口的菜单。此时新建这个下拉菜单是灰色的,无法点击。这是因为店家这个菜单的消息没被处理
方式二;在处理框架窗口的WM_CREATE消息时
int CMyFrameWnd::OnCreate(LPCREATESTRUCT pcs) {
menu.LoadMenu(IDR_MENU1);
this->SetMenu( &menu );// 等价与 ::SetMenu(this->m_hWnd, menu.m_hMenu);
return CFrameWnd::OnCreate(pcs);
}
调用类的成员函数本质还是调用Win32 API?
代码说明:可以实现将指定的菜单资源加载到窗口,并将其设置为窗口的菜单,以便在窗口中显示和处理菜单项的相关操作。?
LoadMenu(IDR_MENU1)
函数,将菜单资源 IDR_MENU1
加载到 menu
对象中。本质还是调用Win32 API::SetMenu(this->m_hWnd, menu.m_hMenu)
函数,将 menu
对象的菜单句柄 m_hMenu
设置为当前窗口的菜单。值得注意的是,菜单对象应该作为框架窗口对象成员变量
LoadMenu内部代码,通过菜单ID拿到菜单句柄,把句柄和菜单对象绑定
_AFXWIN_INLINE BOOL CMenu::LoadMenu(UINT nIDResource)
{ return Attach(::LoadMenuW(AfxFindResourceHandle(
MAKEINTRESOURCE(nIDResource), RT_MENU), MAKEINTRESOURCEW(nIDResource))); }
attach函数:
把菜单句柄赋值给对象成员变量
调用SetPermanent函数把句柄绑定到菜单对象
BOOL CMenu::Attach(HMENU hMenu)
{
ASSERT(m_hMenu == NULL); // only attach once, detach on destroy
if (hMenu == NULL)
{
return FALSE;
}
// Capture menu in object first to ensure it does not leak if the map cannot be allocated/expanded
m_hMenu=hMenu;
CHandleMap* pMap = afxMapHMENU(TRUE); // create map if not exist
ASSERT(pMap != NULL);
pMap->SetPermanent(m_hMenu, this);
return TRUE;
}
通过菜单句柄就能拿到菜单对象?
inline void CHandleMap::SetPermanent(HANDLE h, CObject* permOb)
{ m_permanentMap[(LPVOID)h] = permOb; }
可以在MSDN中搜索 ON_COMMAND 就可以找到 WM_COMMAND 消息的处理
在框架窗口的消息映射中处理,ON_COMMAND(菜单项ID,处理消息的函数名)
void CMyFrameWnd::OnNew() {
AfxMessageBox("框架类处理了新建菜单项被点击");
}
框架类比应用程序类先处理 WM_COMMAND 消息
在应用程序类中也处理 WM_COMMAND 消息,框架类处理完后,其他的就不处理了,只处理一次
对比系统消息,如WM_CREATE只在框架窗口类处理。只有WM_COMMAND会在多个类的中处理
需要处理消息 WM_INITMENUPOPUP 菜单激活,即将显示还没显示,可以调用两个API:CheckMenuItem 、 EnableMenuItem 处理。在MFC中也是要处理这个消息?
void CMyFrameWnd::OnInitMenuPopup(CMenu* pPopup, UINT nPos, BOOL i) {
// pPopup->CheckMenuItem( ID_NEW, MF_CHECKED );
::CheckMenuItem(pPopup->m_hMenu, ID_NEW, MF_CHECKED);
}
右键菜单即是上下文菜单,右击鼠标可以显示一个类似Win右击鼠标可以显示刷新一样
WM_CONTEXTMENU? ? API:::TrackPopupMenu
ON_WM_CONTEXTMENU? 成员函数:CMenu::TrackPopupMenu
菜单是包含两个部分,也就是说CMenu对象是整个菜单
void CMyFrameWnd::OnContextMenu(CWnd* pWnd, CPoint pt) {
// HMENU hPopup = ::GetSubMenu(menu.m_hMenu,0);
// ::TrackPopupMenu( hPopup, TPM_LEFTALIGN|TPM_TOPALIGN, pt.x, pt.y,
// 0, this->m_hWnd, NULL );
CMenu* pPopup = menu.GetSubMenu(0);
pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_TOPALIGN, pt.x, pt.y, this);
}
TrackPopupMenu函数只能显示弹出式菜单,需要先拿到对应的句柄,调用GetSubMenu函数,参数0表示”文件“,可以拿到文件菜单的下拉菜单
TPM_LEFTALIGN
?将菜单左对齐,即菜单的左边缘与触发菜单的点对齐。TPM_TOPALIGN
?将菜单上对齐,即菜单的上边缘与触发菜单的点对齐。