自绘菜单的实现
2008-01-19 20:24:00 来源:WEB开发网核心提示:在VCKBASE上读到《一种漂亮的自绘菜单》 [作者:郑恒 (lbird)],应用到我的工程里后发现:文章中提到的效果能很好的实现,自绘菜单的实现,但是有一点不方便:需要映射 WM_DRAWITEM 和 WM_MEASUREITEM 消息才能实现自画功能,这对于一个基于对话框的工程或者仅仅需要弹出式菜单的工程来说很不方
在VCKBASE上读到《一种漂亮的自绘菜单》 [作者:郑恒 (lbird)]。应用到我的工程里后发现:文章中提到的效果能很好的实现。但是有一点不方便:需要映射 WM_DRAWITEM 和 WM_MEASUREITEM 消息才能实现自画功能。这对于一个基于对话框的工程或者仅仅需要弹出式菜单的工程来说很不方便。网上有一种很有名的自绘菜单 :BCMenu (http://www.rocscience.com/~corkum/BCMenu.html) (在附带工程中也有 BCMenu),在使用它的时候并不需要映射上述的两个消息就能实现自绘效果。这个问题让我觉得很困惑,MSDN也说明:MeasureItem() 和 DrawItem() 两个虚函数是由框架调用的 。并不用手工映射。可是若不映射上述的两个消息则显示不正常。(我查看了好多资料,直到现在还是不明白原因。呵呵:))既然 BCMenu 可以不用映射 WM_DRAWITEM 和 WM_MEASUREITEM 就能实现自画功能,那么它肯定经过了特殊处理。果然,BCMenu::LoadMenu()对整个菜单作了处理 。我注意到,如果菜单是弹出式的,那么不需要映射 WM_DRAWITEM 和 WM_MEASUREITEM 就能实现自画功能。于是我在CMenuEx::LoadMenu()中重新构建了整个菜单, 把所有的子菜单创建为弹出式的菜单使用API函数::CreatePopupMenu(),代码如下:
BOOL CMenuEx::LoadMenu(UINT uMenu)
{
//重新读入菜单,创建为popup菜单,才能自画(由框架调用MesureItem() 和 DrawItem()
HMENU hMenu = ::CreateMenu();
this->Attach(hMenu);
//临时菜单(使用CMenu的LoadMenu()函数读入菜单,并以之为蓝本构建新的菜单)
CMenu Menu;
UINT uID;
Menu.LoadMenu(uMenu);
for(int i = 0; i < (int)Menu.GetMenuItemCount(); i++)
{
uID = Menu.GetMenuItemID(i);
if(uID == 0) //分隔符
{
::AppendMenu(hMenu,MF_SEPARATOR,0,NULL);
}
else if((int)uID == -1) //弹出菜单(即子菜单)
{
CMenu *pSubMenu = Menu.GetSubMenu(i);
//创建子菜单
HMENU hSubMenu = ::CreatePopupMenu();
CString strPopup;
Menu.GetMenuString(i,strPopup,MF_BYPOSITION);
::InsertMenu(hMenu,
i,
MF_BYPOSITION | MF_POPUP | MF_STRING,
(UINT)hSubMenu,
strPopup);
//对子菜单递归调用ChangeMenuStyle(),把子菜单改为MF_OWNERDRAW风格
ChangeMenuStyle(pSubMenu,hSubMenu);
}
else //正常的菜单项
{
CString strText;
Menu.GetMenuString(uID,strText,MF_BYCOMMAND);
AppendMenu(MF_STRING,uID,strText);
}
}
Menu.DestroyMenu(); //销毁临时菜单
return TRUE;
}
void CMenuEx::ChangeMenuStyle(CMenu *pMenu,HMENU hNewMenu)
{
//关联为CMenuEx(关联为CMenuEx后才能自动重画
//原因不明(CMenu封装的结果?)
CMenuEx *pNewMenu;
pNewMenu = new CMenuEx;
pNewMenu->Attach(hNewMenu);
m_SubMenuArr.Add(pNewMenu);
UINT uID;
int nItemCount = pMenu->GetMenuItemCount();
for(int i = 0; i < nItemCount; i++)
{
uID = pMenu->GetMenuItemID(i);
if(uID == 0) //分隔符
{
::AppendMenu(hNewMenu,MF_SEPARATOR,0,NULL);
//pNewMenu->AppendMenu(MF_SEPARATOR,0,NULL);
CString strText;
MENUITEM *pMenuItem = new MENUITEM;
pMenuItem->uID = 0;
pMenuItem->uIndex = -1;
pMenuItem->uPositionImageLeft = -1;
pMenuItem->pImageList = &m_ImageList;
m_MenuItemArr.Add(pMenuItem);
::ModifyMenu(hNewMenu,
i,
MF_BYPOSITION | MF_OWNERDRAW,
-1,
(LPCTSTR)pMenuItem);
}
else if(uID == -1) //弹出菜单(即子菜单)
{
CMenu *pSubMenu = pMenu->GetSubMenu(i);
HMENU hPopMenu = ::CreatePopupMenu();
CString strPopup;
pMenu->GetMenuString(i,strPopup,MF_BYPOSITION);
::InsertMenu(hNewMenu,
i,
MF_BYPOSITION | MF_POPUP,
(UINT)hPopMenu,
strPopup);
MENUITEM *pMenuItem = new MENUITEM;
pMenuItem->uID = -1;
pMenuItem->strText = strPopup;
pMenuItem->uIndex = -1;
pMenuItem->uPositionImageLeft = -1;
pMenuItem->pImageList = &m_ImageList;
m_MenuItemArr.Add(pMenuItem);
::ModifyMenu(hNewMenu,
i,
MF_BYPOSITION | MF_OWNERDRAW,
-1,
(LPCTSTR)pMenuItem);
ChangeMenuStyle(pSubMenu,hPopMenu);
}
else //正常的菜单项
{
CString strText;
pMenu->GetMenuString(uID,strText,MF_BYCOMMAND);
MENUITEM *pMenuItem = new MENUITEM;
pMenuItem->uID = pMenu->GetMenuItemID(i);
pMenu->GetMenuString(pMenuItem->uID,
pMenuItem->strText,
MF_BYCOMMAND);
pMenuItem->uIndex = -1;
pMenuItem->uPositionImageLeft = -1;
pMenuItem->pImageList = &m_ImageList;
m_MenuItemArr.Add(pMenuItem);
UINT uState = pMenu->GetMenuState(i,MF_BYPOSITION);
::AppendMenu(hNewMenu,
MF_OWNERDRAW | MF_BYCOMMAND | uState,
uID,
(LPCTSTR)pMenuItem);
}
}
}
这样,利用标注的CMenu::LoadMenu()函数读入菜单,并根据这个菜单重新构建一个新的菜单,在新菜单中把所有的子菜单创建为弹出式菜单并关联一个CMenuEx类。根据需要,我提供了一个
更多精彩
赞助商链接