您的位置:首页 > 其它

Cool Owner Drawn Menus with Bitmaps - Version 3.03

2013-02-06 10:07 435 查看


Cool Owner Drawn Menus with Bitmaps - Version 3.03

By Brent
Corkum, 28 Apr 2002




   4.92 (178 votes)
 
Download MFC MDI Example - 113 KB

Download MFC Dialog Example - 68 KB


What's new in Version 3.01

As you can see I've added the new Office XP drawing style for the menu's. I just got a machine with Windows XP on it and I noticed that the menu's in all our applications looked terrible. So I decided to do something about it and after 2 years of not looking
at the class, added the new menu drawing style, and added lot's of fixes and user requests. Now the new drawing style isn't exactly like Microsoft's, but I got it so it looks good enough to me. For people that use the old class it's a simple matter of taking
exchanging the old 
BCMenu
 .cpp and .h files with the new ones.

The class currently uses the new style on XP and the old style on Win9x/NT and 2000. However, if you like the new style and you want to use it on all Windows platforms, just change the following line at the top of the BCMenu.cpp file from:


 Collapse | Copy
Code
UINT BCMenu::original_drawmode=BCMENU_DRAWMODE_ORIGINAL;

to


 Collapse | Copy
Code
UINT BCMenu::original_drawmode=BCMENU_DRAWMODE_XP;

Likewise, if you think I did a terrible job you can change the drawing style to the original one on all the platforms.

Other additions include support for images with greater than 16 colors. The example contains images with both 256 and 16 million colors. There is also an option for how to draw disabled options. In XP mode they are not selected but this can be changed. I
also fixed the problem with multiple menu items with the same command ID not getting images (only the first one did!). See the bottom of this article for more information on updates.

Introduction

This class, BCMenu, implements owner drawn menus derived from the 
CMenu
 class.
The purpose of which is to mimic the menu style used in Visual C++ 5.0 and MS Word. I can't take credit for all the code, some portions of it were taken from code supplied by Ben Ashley and Girish Bharadwaj. The difference between their codes and this one
is quite simple, this one makes it very easy to build those cool menus with bitmaps into your application. I've removed the Icon loading stuff and replaced it with Bitmaps. The bitmaps allow you to use the 16X15 toolbar bitmaps directly from your toolbars
in the resource editor. As well, there is no scaling of the bitmaps so they always look good. You can also load Bitmap resources and define bitmaps for your check marks. I've also added the default checkmark drawing stuff, separators, proper alignment of keyboard
accelerator text, keyboard shortcuts, proper alignment of popup menu items, proper system color changes when the Display Appearance changes, plus bug fixes to the Ben Ashley's 
LoadMenu
 function
for complex submenu systems. I made quite a few other modifications as well, too many to list or remember. I also use the disabled bitmap dithering function of Jean-Edouard Lachand-Robert to create the disabled state bitmaps. I must admit, it does a much better
job then the
DrawState
 function. If you find any bugs, memory leaks, or just better ways of doing things, please let
me know. I used Visual C++ 5.0 and I have not tested compatibility with earlier VC versions. I've tested it on Win 95/NT at various resolutions and color palette sizes.

Installation (MDI Application)

Well, enough of the boring stuff, lets talk about implementation. To make it easy I'm first going to list step by step the method for implementing the menus into a MDI application:

Create your MDI application using the App Wizard.
Insert the BCMenu.cpp and BCMenu.h files into your Workspace.
Add the following public member functions to your 
CMainFrame
 class
in the MainFrm.h header file:


 Collapse | Copy
Code
HMENU NewMenu();
HMENU NewDefaultMenu();


Add the following public member variables to your 
CMainFrame
 class
in the MainFrm.h header file:


 Collapse | Copy
Code
BCMenu m_menu,m_default;


Add the line:


 Collapse | Copy
Code
#include "BCMenu.h"

to the top of the MainFrm.h header file.

Open the Mainfrm.cpp implementation file and add the 
NewMenu
 and 
NewDefaultMenu
 member
functions as listed below. IMPORTANT: Make sure you replace the 
IDR_MYMENUTYPE
 menu
id in the below 
LoadMenu
 call to the menu id associated with the menu's in your application. Look in the menus folder
of the Resources view.


 Collapse | Copy
Code
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());
}


Edit the 
InitInstance
 member function of your 
CWinApp
 derived
class and add following highlighted code in the position noted:


 Collapse | Copy
Code
// 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);


Add the message handlers for the 
WM_MEASUREITEM
WM_MENUCHAR
,
and 
WM_INITMENUPOPUP
 messages to your 
CMainFrame
 class.
Do this by right clicking on the 
CMainFrame
 class in the ClassView and selectingAdd
Windows Message Handler. Choose the Window option from the Filter for messages available to classcombo box. Select the message and add
the handler. Then edit the handler and add the below code.


 Collapse | Copy
Code
//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);
}
}


If you are debugging or you are mixing standard menus with the BCMenu's (maybe you have different Document templates using the standard menu's) then you should turn on RTTI in the project settings.
Well, that's it. Compile the program and look in the File menu. You should see the bitmaps. I've tried the menus with context menus and they seem to work fine.

Installation (SDI Application)

Create your SDI application using the App Wizard.
Insert the BCMenu.cpp and BCMenu.h files into your Workspace.
Add the following public member function to your 
CMainFrame
 class in
the MainFrm.h header file:


 Collapse | Copy
Code
HMENU NewMenu();
</tt />


Add the following public member variables to your 
CMainFrame
 class
in the MainFrm.h header file:


 Collapse | Copy
Code
BCMenu m_menu;
</tt />


Add the line:


 Collapse | Copy
Code
#include "BCMenu.h"</tt />

to the top of the MainFrm.h header file.

Open the Mainfrm.cpp implementation file and add the 
NewMenu
 and 
NewDefaultMenu
 member
functions as listed below. IMPORTANT: Make sure you replace the 
IDR_MAINFRAME
 menu
id in the below 
LoadMenu
 call to the menu id associated with the menu's in your application. Look in the menus folder
of the Resources view.


 Collapse | Copy
Code
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());
}


Edit the 
InitInstance
 member function of your 
CWinApp
 derived
class and add following highlighted code in the position noted:


 Collapse | Copy
Code
// 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 />


Add the message handlers for the 
WM_MEASUREITEM
WM_MENUCHAR
,
and 
WM_INITMENUPOPUP
 messages to your 
CMainFrame
 class.
Do this by right clicking on the 
CMainFrame
 class in the ClassView and selectingAdd
Windows Message Handler. Choose the Window option from the Filter for messages available to classcombo box. Select the message and add
the handler. Then edit the handler and add the MDI code from above. Replace the references to 
CMDIFrameWnd
 to 
CFrameWnd
.
If you are debugging or you are mixing standard menus with the 
BCMenu's
 (maybe
you have different Document templates using the standard menu's) then you should turn on RTTI in the project settings.

Improvements and Bug Fixes

Version 3.03

Completely changed the way the bitmaps are stored. To improve resource memory management on Win9x platforms I moved to a single image list for all bitmaps. No longer does every menu option store a CImageList object for the bitmap.
People will notice a drop in GDI object utilization under all platforms.

Improved the disabled embossed look for XP menu's.
Fixed up the large toolbar size bitmaps. Must use SetIconSize before doing anything with the bitmaps larger than 16X15.
Fixed up the reading of hi-color bitmaps. Now properly disables and displays them as long as you use the standard RGB(192,192,192) as the background color.
Checkmarks look more like Office XP.
Separators don't take up as much space in XP look.
Can now turn change the way the right justification of the accelerators works. You can get the more condensed look (yuk!) that MS uses by simply changing the xp_space_acclerators and original_space_accelerators
values from TRUE to FALSE in the BCMenu.cpp file.

Version 3.01

The Office XP menus can be displayed with a 3D pop-out look. Basically the icons have a faded look until the menu option is selected. When selected they are drawn with a 3D shadow in their full glorious color.
If you don't like this look (some people I know do and some don't) then you can easily turn it off by setting the BOOL BCMenu::xp_draw_3D_bitmaps variable to FALSE in the BCMenu.cpp file.
Added DECLARE_DYNAMIC/IMPLEMENT_DYNAMIC to the class so that I could use IsKindOf. This solves some problems of the class inadvertently trying to delete CMenu objects. It also makes it easier to add menu options
to the top level menubar. See the MDI example for how to do this.
Fixed problems with the fonts and default menu options.
Improved how the class tests for running under XP Luna. This fixes some problems that the 3.0 class had when people used funky desktop color schemes.
Changed how the menu responds to a change in the system menu color. The original menu's get colored with the menu color while the XP menu's get colored with the window color. This is how MS does it. The only exception
is the original look on WinXP Luna. Since I hate white menu's I use the 3D color instead. This seems to be what Developer Studio does. Basically MS has no policy on menu color on WinXP Luna so I'm making it up as I go.
Added a MFC Dialog based example. I got tired of people asking how to do it. It's actually quite easy. You basically do all the same stuff as you do in an MDI app. You define an instance of BCMenu in your CDialog
derived class and override the menu in the OnInitDialog using SetMenu. Then handle the windows messages OnMenuChar,OnInitMenuPopup,and OnMeasureItem as you do in an MDI app. Make sure you DestroyMenu the class in an OnClose handler. The only issue I found,
that's unrelated to the class, is the behavior of the OnUpdate CCmdUI stuff. It's not done the same way as in an MDI app. The OnUpdates are not called when a OnInitMenuPopup message is handled. So I made it behave the same way by grabbing a chunk from CFrameWnd::OnInitMenuPopup
and putting it in my Dialog's OnInitMenuPopup handler. If anyone knows a better way, please let me know.

Version 3.0

Added Office XP drawing style.
Added support for menu images with greater than 16 colors.
Can set whether disabled menu options can be selected.
Improved the memory storage of the images. The entire toolbar image is no longer stored for a menu option.
Added a 
SetMenuText
 method.
Added a 
RemoveMenu
 function that takes the popup menu title instead
of it's location.
Added a 
GetSubMenu
 function that returns a pointer to the menu.
Fixed the problem with only the first of multiple menu options with the same command id getting an image.
InsertMenu
 now takes -1 for the position and properly appends the option
to the end of the menu.
Fixed the problem with RichEditViews
If the menu option was given a checked state in the resource editor it wasn't getting applied. Fixed.
Reformatted and improved the class layout.

Version 2.63

RemoveMenu
/
DeleteMenu
 with
popups asserts, this has been fixed.
Updated the example.

Version 2.62

There were some problems with dynamically adding and removing separators.
InsertMenu
 using the 
MF_BYCOMMAND
 flag
didn't work correctly.

Version 2.61

The 
ImageListDuplicate
 function was always being called with an offset
of 0 instead of the user defined offset.
The 
ImageListDuplicate
 function was improved. It now handles 16X16
bitmaps and can handle more color depth.
User information can now be tagged on to a menu item through the 
pContext
 
BCMenuData
 member
variable.
FindMenuItem
 function is added to allow you to retrieve menu data
using a command id.
m_bDynIcons
 member variable has been added. If it is set to TRUE
then resource id's are loaded as Icons instead of bitmaps.

Version 2.6

Version 2.5 was not VC++ 5.0 compatible. I had used a 
CImageList::Create(
CImageList* pImageList );
 overload that isn't available in 5.0. The above overload also calls a SDK function that is not supported in Windows 95 without IE 4.0. As a result, the menu class would crash if you use any of the menu functions that add a
bitmap through a 
CBitmap
 or 
CImageList
 object.
All is now fixed!

Version 2.5

Added support for dynamically created menu's. The new functions include:
BOOL RemoveMenu(UINT uiId,UINT nFlags);

BOOL DeleteMenu(UINT uiId,UINT nFlags);

BOOL AppendMenu(UINT nFlags,UINT nIDNewItem,const char *lpszNewItem,int nIconNormal=-1);

BOOL AppendMenu(UINT nFlags,UINT nIDNewItem,const char *lpszNewItem,CImageList
*il,int xoffset);

BOOL AppendMenu(UINT nFlags,UINT nIDNewItem,const char *lpszNewItem,CBitmap
*bmp);

BOOL InsertMenu(UINT nPosition,UINT nFlags,UINT nIDNewItem,const char *lpszNewItem,int
nIconNormal=-1);

BOOL InsertMenu(UINT nPosition,UINT nFlags,UINT nIDNewItem,const char *lpszNewItem,CImageList
*il,int xoffset);

BOOL InsertMenu(UINT nPosition,UINT nFlags,UINT nIDNewItem,const char *lpszNewItem,CBitmap
*bmp);


See the example for details on implementation. You can define the bitmap for the appended or inserted menu item using a resource id, 
CBitmap
 object
or a 
CImageList
 object.
Added new functions:
BCMenu* AppendODPopupMenuA(LPCSTR lpstrText);
 - A simple method for
dynamically adding a popup menu.
BOOL ModifyODMenuA(const char *lpstrText,UINT nID,CImageList *il,int xoffset);

BOOL ModifyODMenuA(const char *lpstrText,UINT nID,CBitmap *bmp)
;

Can now define the bitmap using a 
Cbitmap
 object or a 
CImageList
 object
BCMenu
 now maintains a list of every 
BCMenu
 created.
Using the now static member function
BCMenu::Ismenu
 you can easily determine if a menu is a 
BCMenu
.
A few resource leaks were fixed.

Version 2.4

Updated the code to work with VC++ 6.0. Fixed the 
GetMenuItemInfo
 problem.
If a tab character existed in every menu option there was a problem with the 
InsertSpaces
 function.
The 
FindMenuOption
 would not find a popup menu option if it was in
a popup itself.
Initialization was added for 
pFont
 and 
nID
.

Version 2.3 (courtesy of Stefan Kuhr)

Conversions from Unicode to ANSI and vice versa are now being made through the helper macros described in MFC Tech Note 059. Memory allocations for the conversions are now being made primarily on the stack, which
is much safer and faster on Win32 than on the heap. And the code looks much better and shorter.
The Unicode String in the 
BCMenuData
 is now to be allocated on the
heap thus having only the memory footprint it needs to have. 
BCMenuData
 has new member functions for accessing the
now private String variable 
wchar_t *m_szMenuText
.
Corrected a few situations in BCMenu code where portable constant strings (_T()) should be used.
Improved look on the old shell. It really looks nice now on WinNT 3.51 and Win32s. You can now run the sample on NT3.51.
The function '
IsNewShell
' is now replaced by '
IsShellType
'.
BCMenu has a new static member function
BOOL IsNewShell(void)
.
Added Unicode project settings to the sample.
Changed strings in the sample to portable versions by use of the _T() macro.
Set warning level of all project settings in the sample to the highest level (W4) and corrected everything to compile cleanly without any warning being thrown by VC 5.

Version 2.2

Fixed a memory leak in 
AddBitmapToImageList
. This only affected floating
popup menu's (right mouse button).

Version 2.1

Was not deleting the disabled bitmap in 
DrawItem
 causing a resource
leak. This has been fixed.

Version 2.0

Version 1.9 had some problems with the new disabled look under different desktop color schemes. This has been fixed.
There was a bug in the image list stuff. For non-default desktop color schemes, the 3D colors were not being done correctly to match the toolbar.

Version 1.9

The disabled look of the menu icons now match the disabled look of the toolbar buttons in Joerg Koenig's flat toolbar class available fromhttp://www.codeproject.com/menu/<font%20color=#008000>//www.codeguru.com/.
If you prefer the old look you can get it back through a call to the 
SetDisableOldStyle
 member function.
Version 1.8

Made a modification to support OCX controls.

Version 1.7

Added UNICODE and Win32s old shell support (WIN 3.1 and WINNT 3.5).
Added integrated radio and checked buttons.
Now using 
AfxLoadSysColorBitmap
 to load the bitmaps.
Now using the 
WM_INITMENUPOPUP
 message handler to update the menus
instead of the
OnUpdateWindowNew
.

Version 1.6

Multiple 
LoadMenu
/
DestroyMenu
 combinations
crashed. This has been fixed.

Version 1.5

Can now easily add bitmaps to popup options. See the 
CMainFrame::Newmenu
 member
function in the sample application for implementation instructions.
The 
UpdateMenu
 member function will now work to update menu's after
a dynamic change in the menu's options text.

Version 1.4

More work on the sizing and position of Accelerator text on menus.

Version 1.3

Keyboard shortcuts did not work correctly with the active view menu options in the MDI Window popup.
Keyboard shortcuts did not work correctly with the most-recently-used (MRU) file list. Menu options after this list could not be selected with keyboard shortcuts.
For some reason MFC treats 
MeasureItem
 results differently for first
level popups. The expanded tabs size calculation (
GetTabbedTextExtent
) works well for submenus but MFC seems to pad
too much space onto the first level popups. I tried to be smart and reduce the size manually, but this messed up submenus. I've gone back to the original tabbed size calculation in 
MeasureItem
.

Version 1.2

The memory used to store the checkmark bitmaps was not being released when the menu was destroyed. This resulted in a memory leak. This has been fixed.
Added the 
AddFromToolBar
 member function for loading all the bitmaps
from a toolbar into menu options (if they exist). If you want ALL the bitmaps from a toolbar to be mapped to menu options, use this function. This replaces the many ModifyODMenu function calls that you would have to make. If you only want a few of the toolbar
options mapped to menu options then you have to use the 
ModifyODMenu
 method.

Version 1.1

When the user typed a keyboard shortcut that didn't exist the program crashed.
The first item, if it contained a bitmap, was not drawn correctly when keyboard shortcuts were used.
Large fonts caused some alignment problems, this has been fixed.
The 
AppendODMenu
 and 
ModifODMenu
 member
functions were prototyped incorrectly.
The 
LoadMenu(LPCTSTR lpszResourceName)
 overloaded member function didn't
work.
Added RTTI checking in the code if it's defined in the project Settings. This is both helpful for debugging and is required if you mix standard menus with BCMenu's.
Added Word 97 radio button style for checked menu options with bitmaps.

License

This article, along with any associated source code and files, is licensed under The Code Project Open
License (CPOL)

转自:http://www.codeproject.com/Articles/22/Cool-Owner-Drawn-Menus-with-Bitmaps-Version-3-03
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: