分类: C/C++
2008-08-01 17:03:19
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类。根据需要,我提供了一个
CMenuEx::LoadToolBar(UINT uToolBar, UINT uFace)
接口,请注意它的两个参数:uToolBar 是工具条的资源,uFace
是一个替代位图的资源ID。因为VC6.0中做一个真彩工具栏并不是一件容易的事,所以我做了一个小动作:用IDE的资源编辑器随便编辑一个工具条,只要ID和菜单ID相对应即可,然后可以用外部编辑器编辑好真正要使用的位图(顺序和工具条资源的顺序一样),并把该位图作为uFace参数传入,菜单就可以有真彩图标了。
CMenuEx还提供了如下三个接口:
BOOL ModifyMenuEx() BOOL AppendMenuEx() BOOL RemoveMenuEx()
功能一目了然,只是增加了对自绘风格的处理,应用的时候只要像调用普通的CMenu::AppendMenu()等函数一样就自动拥有自绘风格了。我写这篇文章的目的在于提出菜单派生类调用 MeasureItem() 和 DrawItem()的问题。至于实现漂亮的菜单界面主要工作当然还是在 DrawItem() 函数中做,有特殊需要的可以自行定义 MENUITEM 结构,重新写 DrawItem() 函数。我没有提供设置菜单附加位图的具体代码,相信这个不是问题。你可以很容易的通过重写 DrawItem()实现。有必要提醒的是:有关一个菜单项的信息最好能完全从一个MENUITEM结构中取得,使
virtual void MeasureItem(LPMEASUREITEMSTRUCT lpMIS); virtual void DrawItem(LPDRAWITEMSTRUCT lpDIS);
两个函数完全不依赖于CMenuEx类的数据成员。
要在工程中使用CMenuEx很简单:
效果如下:
主菜单
弹出式菜单
最后,对《一种漂亮的自绘菜单》的作者郑恒给予我的帮助表示衷心感谢!
下载本文示例代码