翻译来源:https://www.codeproject.com/Articles/22/Cool-Owner-Drawn-Menus-with-Bitmaps-Version
这个类很常见,在很多源码中都有见到。
包含BCMenu.cpp和BCMenu.h
![]() | ![]() |
3.01版的新功能
您可以看到我已经添加了新的Office XP绘图样式的菜单。我刚刚收到一台带有Windows XP的机器,我注意到我们所有应用程序中的菜单看起来都很糟糕。所以我决定做一些事情,经过2年的不看课程,添加了新的菜单绘图风格,并添加了很多修复和用户请求。现在新的绘画风格不像微软那样,但是我觉得这样做对我来说看起来不错。对于使用旧类的人来说,使用BCMenu
新的.cpp和.h文件是很简单的事情。
该课程目前使用XP上的新样式和Win9x / NT和2000上的旧样式。但是,如果您喜欢新的样式,并且希望在所有Windows平台上使用它,只需更改BCMenu顶部的以下行 .cpp文件来自:
UINT BCMenu::original_drawmode=BCMENU_DRAWMODE_ORIGINAL;
至
UINT BCMenu::original_drawmode=BCMENU_DRAWMODE_XP;
同样,如果你认为我做了一个糟糕的工作,你可以在所有平台上将绘图风格改成原来的样式。
其他添加包括支持大于16种颜色的图像。该示例包含256和1600万色的图像。还有一个如何绘制禁用选项的选项。在XP模式下,它们未被选中,但可以更改。我也修复了多个菜单项的问题与相同的命令ID没有获得图像(只有第一个!)。有关更新的更多信息,请参阅本文的底部。
介绍
该类BCMenu实现从CMenu
类派生的所有者绘制的菜单。其目的是模仿Visual C ++ 5.0和MS Word中使用的菜单样式。我无法承担所有的代码,其中一部分取自本·阿什利和Girish Bharadwaj提供的代码。他们的代码和这个代码之间的区别是非常简单的,这使得使用位图在应用程序中构建这些酷菜单变得非常简单。我已经删除了图标加载的东西,并用位图替换它。位图允许您使用资源编辑器中的工具栏直接使用16X15工具条位图。同样,没有缩放位图,所以它们总是看起来不错。您还可以加载位图资源并为您的复选标记定义位图。我还添加了默认的勾号图纸,LoadMenu
分隔符,键盘加速器文本的正确对齐,键盘快捷键,弹出菜单项的正确对齐,显示外观更改时适当的系统颜色更改,以及Ben Ashley 对复杂子菜单系统的功能的错误修复。我做了很多其他修改,太多列表或记住了。我还使用Jean-Edouard Lachand-Robert的禁用位图抖动功能创建禁用的状态位图。我必须承认,这样做的DrawState
功能要好得多。如果您发现任何错误,内存泄漏或更好的做事方式,请让我知道。我使用Visual C ++ 5.0,我没有测试与早期VC版本的兼容性。我已经在Win 95 / NT上以各种分辨率和调色板大小进行了测试。键盘快捷键,弹出菜单项的正确对齐,显示外观更改时适当的系统颜色更改,以及Ben Ashley 对复杂子菜单系统的功能的错误修复。我做了很多其他修改,太多列表或记住了。我还使用Jean-Edouard Lachand-Robert的禁用位图抖动功能创建禁用的状态位图。我必须承认,这样做的功能要好多了。如果您发现任何错误,内存泄漏或更好的做事方式,请让我知道。我使用Visual C ++ 5.0,我没有测试与早期VC版本的兼容性。我已经在Win 95 / NT上以各种分辨率和调色板大小进行了测试。键盘快捷键,弹出菜单项的正确对齐,显示外观更改时适当的系统颜色更改,以及Ben Ashley 对复杂子菜单系统的功能的错误修复。我做了很多其他修改,太多列表或记住了。我还使用Jean-Edouard Lachand-Robert的禁用位图抖动功能创建禁用的状态位图。我必须承认,这样做的功能要好得多。如果您发现任何错误,内存泄漏或更好的做事方式,请让我知道。我使用Visual C ++ 5.0,我没有测试与早期VC版本的兼容性。我已经在Win 95 / NT上以各种分辨率和调色板大小进行了测试。显示外观发生变化时,正确的系统颜色变化,以及Ben Ashley 对复杂子菜单系统的功能的修复。我做了很多其他修改,太多列表或记住了。我还使用Jean-Edouard Lachand-Robert的禁用位图抖动功能创建禁用的状态位图。我必须承认,这样做的功能要好得多。如果您发现任何错误,内存泄漏或更好的做事方式,请让我知道。我使用Visual C ++ 5.0,我没有测试与早期VC版本的兼容性。我已经在Win 95 / NT上以各种分辨率和调色板大小进行了测试。显示外观发生变化时,正确的系统颜色变化,以及Ben Ashley 对复杂子菜单系统的功能的修复。我做了很多其他修改,太多列表或记住了。我还使用Jean-Edouard Lachand-Robert的禁用位图抖动功能创建禁用的状态位图。我必须承认,这样做的功能要好得多。如果您发现任何错误,内存泄漏或更好的做事方式,请让我知道。我使用Visual C ++ 5.0,我没有测试与早期VC版本的兼容性。我已经在Win 95 / NT上以各种分辨率和调色板大小进行了测试。我还使用Jean-Edouard Lachand-Robert的禁用位图抖动功能创建禁用的状态位图。我必须承认,这样做的功能要好得多。如果您发现任何错误,内存泄漏或更好的做事方式,请让我知道。我使用Visual C ++ 5.0,我没有测试与早期VC版本的兼容性。我已经在Win 95 / NT上以各种分辨率和调色板大小进行了测试。我还使用Jean-Edouard Lachand-Robert的禁用位图抖动功能创建禁用的状态位图。我必须承认,这样做的功能要好得多。如果您发现任何错误,内存泄漏或更好的做事方式,请让我知道。我使用Visual C ++ 5.0,我没有测试与早期VC版本的兼容性。我已经在Win 95 / NT上以各种分辨率和调色板大小进行了测试。
安装(MDI应用程序)
那么,无聊的东西,让我们谈谈实现。为了方便起见,我将首先列出将MDI菜单实现到MDI应用程序中的方法:
- 使用应用向导创建您的MDI应用程序。
- 将BCMenu .cpp和BCMenu .h文件插入到您的工作区。
CMainFrame
在MainFrm.h头文件中将以下公共成员函数添加到您的类中:隐藏 复制代码HMENU NewMenu(); HMENU NewDefaultMenu();
CMainFrame
在MainFrm.h头文件中将以下公共成员变量添加到您的类中:隐藏 复制代码BCMenu m_menu,m_default;
- 添加行:
隐藏 复制代码
#include <span class="code-string">"BCMenu.h"</span>
到MainFrm.h头文件的顶部。
- 打开Mainfrm.cpp实现文件,并添加
NewMenu
和NewDefaultMenu
成员函数如下所列。重要信息:确保IDR_MYMENUTYPE
将以下LoadMenu
调用中的菜单ID替换为与应用程序中的菜单相关联的菜单ID。查看资源视图的菜单文件夹。隐藏 复制代码HMENU CMainFrame::NewMenu() { static UINT toolbars[]={ IDR_MAINFRAME }; // Load the menu from the resources // ****replace IDR_MENUTYPE with your menu ID**** m_menu.LoadMenu(IDR_MYMENUTYPE); // One method for adding bitmaps to menu options is // through the LoadToolbars member function.This method // allows you to add all the bitmaps in a toolbar object // to menu options (if they exist). The first function // parameter is an array of toolbar id's. The second is // the number of toolbar id's. There is also a function // called LoadToolbar that just takes an id. m_menu.LoadToolbars(toolbars,1); return(m_menu.Detach()); } HMENU CMainFrame::NewDefaultMenu() { m_default.LoadMenu(IDR_MAINFRAME); m_default.LoadToolbar(IDR_MAINFRAME); return(m_default.Detach()); }
- 编辑
InitInstance
您的CWinApp
派生类的成员函数,并在以下位置添加以下突出显示的代码:隐藏 复制代码// create main MDI Frame window CMainFrame* pMainFrame = new CMainFrame; if (!pMainFrame->LoadFrame(IDR_MAINFRAME)) return FALSE; m_pMainWnd = pMainFrame; // This code replaces the MFC created menus with // the Ownerdrawn versions pDocTemplate->m_hMenuShared=pMainFrame->NewMenu(); pMainFrame->m_hMenuDefault=pMainFrame->NewDefaultMenu(); // This simulates a window being opened if you don't have // a default window displayed at startup pMainFrame->OnUpdateFrameMenu(pMainFrame->m_hMenuDefault); // Parse command line for standard shell commands, // DDE, file open CCommandLineInfo cmdInfo; ParseCommandLine(cmdInfo);
- 添加消息的处理程序
WM_MEASUREITEM
,WM_MENUCHAR
以及WM_INITMENUPOPUP
邮件到您的CMainFrame
类。通过右键单击CMainFrame
ClassView中的类并选择添加Windows消息处理程序来执行此操作。从过滤器中选择可用于类组合框的消息的窗口选项。选择消息并添加处理程序。然后编辑处理程序并添加以下代码。隐藏 收缩复制代码
//This handler ensure that the popup menu items are // drawn correctly void CMainFrame::OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct) { BOOL setflag=FALSE; if(lpMeasureItemStruct->CtlType==ODT_MENU){ if(IsMenu((HMENU)lpMeasureItemStruct->itemID)){ CMenu* cmenu = CMenu::FromHandle((HMENU)lpMeasureItemStruct->itemID); if(m_menu.IsMenu(cmenu)||m_default.IsMenu(cmenu)){ m_menu.MeasureItem(lpMeasureItemStruct); setflag=TRUE; } } } if(!setflag)CMDIFrameWnd::OnMeasureItem(nIDCtl, lpMeasureItemStruct); } //This handler ensures that keyboard shortcuts work LRESULT CMainFrame::OnMenuChar(UINT nChar, UINT nFlags, CMenu* pMenu) { LRESULT lresult; if(m_menu.IsMenu(pMenu)||m_default.IsMenu(pMenu)) lresult=BCMenu::FindKeyboardShortcut(nChar, nFlags, pMenu); else lresult=CMDIFrameWnd::OnMenuChar(nChar, nFlags, pMenu); return(lresult); } //This handler updates the menus from time to time void CMainFrame::OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu) { CMDIFrameWnd::OnInitMenuPopup(pPopupMenu, nIndex, bSysMenu); if(!bSysMenu){ if(m_menu.IsMenu(pPopupMenu)||m_default.IsMenu(pPopupMenu)) BCMenu::UpdateMenu(pPopupMenu); } }
- 如果您正在调试或正在将标准菜单与BCMenu混合(也许您使用标准菜单使用不同的文档模板),则应在项目设置中打开RTTI。
那就是这样。编译程序并查看文件菜单。你应该看到位图。我已经尝试了菜单与上下文菜单,他们似乎工作正常。
安装(SDI应用)
- 使用应用向导创建您的SDI应用程序。
- 将BCMenu .cpp和BCMenu .h文件插入到您的工作区。
CMainFrame
在MainFrm.h头文件中将以下public成员函数添加到您的类中:隐藏 复制代码HMENU NewMenu(); </tt />
</ tt />CMainFrame
在MainFrm.h头文件中将以下公共成员变量添加到您的类中:隐藏 复制代码BCMenu m_menu; </tt />
</ tt />- 添加行:
隐藏 复制代码
#include <span class="code-string">"BCMenu.h"</tt /></span>
</ tt />到MainFrm.h头文件的顶部。
- 打开Mainfrm.cpp实现文件,并添加
NewMenu
和NewDefaultMenu
成员函数如下所列。重要信息:确保IDR_MAINFRAME
将以下LoadMenu
调用中的菜单ID替换为与应用程序中的菜单相关联的菜单ID。查看资源视图的菜单文件夹。隐藏 复制代码HMENU CMainFrame::NewMenu() { // Load the menu from the resources // ****replace IDR_MAINFRAME with your menu ID**** m_menu.LoadMenu(IDR_MAINFRAME); // One method for adding bitmaps to menu options is // through the LoadToolbar member function.This method // allows you to add all the bitmaps in a toolbar object // to menu options (if they exist). The function parameter // is an the toolbar id. There is also a function called // LoadToolbars that takes an array of id's. m_menu.LoadToolbar(IDR_MAINFRAME); return(m_menu.Detach()); }
- 编辑
InitInstance
您的CWinApp
派生类的成员函数,并在以下位置添加以下突出显示的代码:隐藏 复制代码// Dispatch commands specified on the command line if (!ProcessShellCommand(cmdInfo)) return FALSE; CMenu* pMenu = m_pMainWnd->GetMenu(); if (pMenu)pMenu->DestroyMenu(); HMENU hMenu = ((CMainFrame*) m_pMainWnd)->NewMenu(); pMenu = CMenu::FromHandle( hMenu ); m_pMainWnd->SetMenu(pMenu); ((CMainFrame*)m_pMainWnd)->m_hMenuDefault = hMenu; </tt />
</ tt /> - 添加消息的处理程序
WM_MEASUREITEM
,WM_MENUCHAR
以及WM_INITMENUPOPUP
邮件到您的CMainFrame
类。通过右键单击CMainFrame
ClassView中的类并选择添加Windows消息处理程序来执行此操作。从过滤器中选择可用于类组合框的消息的窗口选项。选择消息并添加处理程序。然后编辑处理程序并从上面添加MDI代码。将引用替换CMDIFrameWnd
为CFrameWnd
。 - 如果您正在调试或正在混合标准菜单
BCMenu's
(也许您使用标准菜单有不同的文档模板),那么您应该在项目设置中打开RTTI。