Click here to Skip to main content
Click here to Skip to main content

Cool Owner Drawn Menus with Bitmaps - Version 3.03

By , 28 Apr 2002
 

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:

UINT BCMenu::original_drawmode=BCMENU_DRAWMODE_ORIGINAL;

to

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:

  1. Create your MDI application using the App Wizard.
  2. Insert the BCMenu.cpp and BCMenu.h files into your Workspace.
  3. Add the following public member functions to your CMainFrame class in the MainFrm.h header file:
    HMENU NewMenu();
    HMENU NewDefaultMenu();
  4. Add the following public member variables to your CMainFrame class in the MainFrm.h header file:
    BCMenu m_menu,m_default;
  5. Add the line:
    #include "BCMenu.h"

    to the top of the MainFrm.h header file.

  6. 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.
    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());
    }
    
  7. Edit the InitInstance member function of your CWinApp derived class and add following highlighted code in the position noted:
    // 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);
    
  8. 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 selecting Add Windows Message Handler. Choose the Window option from the Filter for messages available to class combo box. Select the message and add the handler. Then edit the handler and add the below 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);
      }
    }
    
  9. 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)

  1. Create your SDI application using the App Wizard.
  2. Insert the BCMenu.cpp and BCMenu.h files into your Workspace.
  3. Add the following public member function to your CMainFrame class in the MainFrm.h header file:
    HMENU NewMenu();
    </tt />
  4. Add the following public member variables to your CMainFrame class in the MainFrm.h header file:
    BCMenu m_menu;
    </tt />
  5. Add the line:
    #include "BCMenu.h"</tt />

    to the top of the MainFrm.h header file.

  6. 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.
    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());
    }
    
  7. Edit the InitInstance member function of your CWinApp derived class and add following highlighted code in the position noted:
      // 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 />
  8. 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 selecting Add Windows Message Handler. Choose the Window option from the Filter for messages available to class combo 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.
  9. 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

  1. 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.

  2. Improved the disabled embossed look for XP menu's.
  3. Fixed up the large toolbar size bitmaps. Must use SetIconSize before doing anything with the bitmaps larger than 16X15.
  4. 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.
  5. Checkmarks look more like Office XP.
  6. Separators don't take up as much space in XP look.
  7. 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

  1. 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.
  2. 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.
  3. Fixed problems with the fonts and default menu options.
  4. 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.
  5. 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.
  6. 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

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

Version 2.63

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

Version 2.62

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

Version 2.61

  1. The ImageListDuplicate function was always being called with an offset of 0 instead of the user defined offset.
  2. The ImageListDuplicate function was improved. It now handles 16X16 bitmaps and can handle more color depth.
  3. User information can now be tagged on to a menu item through the pContext BCMenuData member variable.
  4. A FindMenuItem function is added to allow you to retrieve menu data using a command id.
  5. A 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

  1. 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

  1. 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.
  2. 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);
  3. Can now define the bitmap using a Cbitmap object or a CImageList object
  4. 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.
  5. A few resource leaks were fixed.

Version 2.4

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

Version 2.3 (courtesy of Stefan Kuhr)

  1. 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.
  2. 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.
  3. Corrected a few situations in BCMenu code where portable constant strings (_T()) should be used.
  4. 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.
  5. The function 'IsNewShell' is now replaced by 'IsShellType'. BCMenu has a new static member function BOOL IsNewShell(void).
  6. Added Unicode project settings to the sample.
  7. Changed strings in the sample to portable versions by use of the _T() macro.
  8. 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

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

Version 2.1

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

Version 2.0

  1. Version 1.9 had some problems with the new disabled look under different desktop color schemes. This has been fixed.
  2. 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

  1. The disabled look of the menu icons now match the disabled look of the toolbar buttons in Joerg Koenig's flat toolbar class available from http://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

  1. Made a modification to support OCX controls.

Version 1.7

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

Version 1.6

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

Version 1.5

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

Version 1.4

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

Version 1.3

  1. Keyboard shortcuts did not work correctly with the active view menu options in the MDI Window popup.
  2. 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.
  3. 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

  1. 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.
  2. 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

  1. When the user typed a keyboard shortcut that didn't exist the program crashed.
  2. The first item, if it contained a bitmap, was not drawn correctly when keyboard shortcuts were used.
  3. Large fonts caused some alignment problems, this has been fixed.
  4. The AppendODMenu and ModifODMenu member functions were prototyped incorrectly.
  5. The LoadMenu(LPCTSTR lpszResourceName) overloaded member function didn't work.
  6. 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.
  7. 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)

About the Author

Brent Corkum
Web Developer
Canada Canada
Member
I'm the senior software development manager for Rocscience Inc., a company specializing in geomechanics software. I have a PH.D in Civil Engineering, and have been programming since 1978. I've used more computers and languages then 2000 characters lets me list. Besides programming, I enjoy golfing, watching my 3 kids play sports, spending time with my wife, and watching plenty of hockey and football.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMeasureItem and Pop-up ItemsmemberRakka Rage14 May '02 - 11:06 
When I use your code... my Pop-up menu items do not go into MeasureItem... any item an ">" arrow does not seem to get measured and ends up 16 pixels high and the rest become 20... any ideas why? it does seem to work correctly in the sample... just wondering why?
 
because i have no icons?
GeneralRe: MeasureItem and Pop-up ItemsmemberBrent Corkum14 May '02 - 16:26 
This problem is usually caused by not adding the handlers seen in step 8 of the MDI installation instructions. Make sure thes handlers are added to all classes that use a BCMenu object. For example, CView derived classes that use context menu's.
 
Brent
GeneralRe: MeasureItem and Pop-up ItemsmemberRakka Rage15 May '02 - 3:45 
thanks
GeneralRe: MeasureItem and Pop-up ItemsmemberDieter Hammer16 May '02 - 4:26 
It seems handling MeasureItem is only needed with NT. At 2000 or XP everything looks great without any handlers, in NT the pop-ups need it.
GeneralRe: MeasureItem and Pop-up ItemsmemberBrent Corkum16 May '02 - 5:20 
Thanks Dieter for pointing this out. I've often wondered why sometimes it's needed and sometimes it's not. Many people have commented that they didn't need to add the handlers. I also think if you use TrackPopupMenu with a pointer to the MainFrame then it will work as well. For example:
 
psub->TrackPopupMenu(TPM_LEFTALIGN|TPM_RIGHTBUTTON,point.x,point.y,AfxGetApp()->m_pMainWnd);
 
instead of the usual
 
psub->TrackPopupMenu(TPM_LEFTALIGN|TPM_RIGHTBUTTON,point.x,point.y,this);
 
This will relay all the messages through the MainFrame where you already have the handlers.

 
Brent
GeneralSupport for DefaultItemmemberRakka Rage14 May '02 - 10:17 
has anyone written code which would make this work with CMenu::SetDefaultItem? thanks.
GeneralRe: Support for DefaultItemmemberBrent Corkum14 May '02 - 10:19 
I believe it works already. Look in the example for instances of SetMenuDefaultItem and GetMenuDefaultItem.
 
Brent
GeneralRe: Support for DefaultItemmemberRakka Rage14 May '02 - 10:31 
I did look.. and i tried it too. There are no refs to either of those in the BCMenu.* code.
 
You know what i mean? It just makes the menu item draw in a bold style indicating it is the default.
 
Thanks.
GeneralRe: Support for DefaultItemmemberRakka Rage15 May '02 - 4:27 
i figgured it out if anyone cares...
 
void BCMenu::DrawItem_Win9xNT2000 (LPDRAWITEMSTRUCT lpDIS)
void BCMenu::DrawItem_WinXP (LPDRAWITEMSTRUCT lpDIS)
 
right before the FIRST CreateFontIndirect in each of those functions insert the following three lines...
 
UINT id = this->GetDefaultItem(0);
if (id == lpDIS->itemID)
m_lf.lfWeight = FW_BOLD;

GeneralRe: Support for DefaultItemmemberBrent Corkum15 May '02 - 16:21 
You sure you have the latest version of the class. I believe this was a problem that was fixed in version 3.01 (see #3 in the update section for version 3.01). In the current version there isn't a call to CreateFonIndirect in the DrawItem_Win9xNT2000 function. And in the DrawItem_WinXP it's called only for menu options that are disabled and can not be selected.
 
Anyways, I'm not sure your code would work. I think the what you should be using is:
 
UINT id = GetDefaultItem(GMDI_USEDISABLED);
if (id == lpDIS->itemID)m_lf.lfWeight = FW_BOLD;
 
I tried this for the DrawItem_WinXP function and I could make disabled items default (bold) although I don't think you want this.
 
Brent
GeneralCButtonST supports BCMenumemberDavide Calabro3 May '02 - 5:41 
Starting from version 3.5, CButtonST supports BCMenu class into its built-in menu support.
 
CButtonST can be found here http://www.softechsoftware.it/cbuttonst.html
or here http://www.codeproject.com/buttonctrl/cbuttonst.asp
 
Cheers,
 
SoftechSoftware
Davide Calabro'
davide_calabro@yahoo.com
http://www.softechsoftware.it
QuestionHow do I load an Icon with menu item?memberJoeSox3 May '02 - 3:32 
Lets just say I have a menu item with resource ID (ID_EDIT_FIND) What BCMenu function should I use to load a bitmap or icon?Confused | :confused:
AnswerRe: How do I load an Icon with menu item?memberBrent Corkum3 May '02 - 4:45 
Look in the example in the CMainFrame::NewMenu member function. There are lot's of examples on how to use bitmaps, icons, and toolbars for the menu bitmaps.
 
Brent
GeneralRe: How do I load an Icon with menu item?memberJoeSox3 May '02 - 6:39 
Thanks I got itCool | :cool:
GeneralThis is the ugliest code Ive ever seenmemberAnonymous1 May '02 - 17:10 
and I've seen some ugly code. doesn't it bother you that braces don't line up? or comments are sparse and useless? or that big blobs of code just bang together like teens in a moshpit?
 
whitespace is not a sin.
GeneralRe: This is the ugliest code Ive ever seenmemberBrent Corkum3 May '02 - 4:41 
Yes it is ugly, very ugly, but this is due to the number of people that have worked on it over the last 5 years, the support for all windows operating systems, the support for unicode etc. etc. Also, it works and the price is right. So if you don't want to use it because the code ain't pretty enough for you then so be it. You get what you pay for.
 
Just remember, there's lots of pretty code out there that doesn't work worth a crap. Maybe you'd prefer something along these lines!
 
On a lighter note, if people start complaining about the way the code looks instead of how it works, as in bugs, I'll know I've achieved what I want. Good solid software.
 
Whitespace is not a sin, but buggy code is.

 
Brent
GeneralNo it's not.memberLogan5 May '02 - 14:40 
Actually, compared to most other code I've seen, I thought this was nice to work with. It even follows hungarian notation.
 
Don't listen to the anonymous pest. In general, this is a fantastic project, Brent, and at a wonderful price. Obviously many people are very happy with what you've put together. Keep up the great work..
 
Logan

GeneralRe: This is the ugliest code Ive ever seenmembermchan7 May '02 - 15:10 
Learn how to use indent instead of attacking someone's else style.
 
http://www.gnu.org/software/indent/indent.html
GeneralRe: This is the ugliest code Ive ever seenmemberDaniel Desormeaux9 May '02 - 2:58 
It works well enough that I've never even *had* to look at the source code. So why would you care? Maybe you'd be happier with a compiled DLL?

QuestionRemoveMenu() problem?memberLogan30 Apr '02 - 20:55 
The RemoveMenu() function is not supposed to destroy sub menus. DeleteMenu() is used for this.
 
Therefore, people wanting to use RemoveMenu() so they can reuse their submenus should take out the last paragraph of the function that deletes the submenu and its items.
 
Logan
GeneralDisabled checkmarksmemberLevente Farkas30 Apr '02 - 0:36 
Hello,
 
Yet another option I would love to see (implementation below) are correctly drawn disabled checkmark menu items. You can see a checked and disabled one, XP-style here.
 
For this, the method DrawXPCheckmark becomes (warning: this is based on version 3.01):
 
BOOL BCMenu::DrawXPCheckmark(CDC *dc, const CRect& rc, BOOL bSelected, BOOL bDisabled, HBITMAP hbmCheck, COLORREF selcolor) const
{
if(bDisabled)
{
COLORREF clr =GetSysColor(COLOR_3DSHADOW);
dc->Draw3dRect(rc,clr,clr);
}
else
{
COLORREF clrCheckBack =bSelected ? LightenColor(selcolor,0.6) : LightenColor(GetSysColor(COLOR_BTNFACE),0.25);
CBrush brushin;
brushin.CreateSolidBrush(clrCheckBack);
dc->FillRect(rc,&brushin);
brushin.DeleteObject();
dc->Draw3dRect(rc,selcolor,selcolor);
}
 
int x = rc.left + (rc.Width() - 8)/2;
int y = rc.top + (rc.Height() - 7)/2 + 1;
COLORREF clrCheckMark =GetSysColor(bDisabled ? COLOR_3DSHADOW : COLOR_MENUTEXT);
 
if(!hbmCheck)
{
if(bDisabled)
DrawCheckMark(dc,x+1,y+1,GetSysColor(COLOR_3DHILIGHT));
DrawCheckMark(dc,x,y,clrCheckMark);
}
else
{
if(bDisabled)
DrawRadioDot(dc,x+1,y+1,GetSysColor(COLOR_3DHILIGHT));
DrawRadioDot(dc,x,y,clrCheckMark);
}
return TRUE;
}
 

 

GeneralRe: Disabled checkmarksmemberBrent Corkum30 Apr '02 - 4:02 
Levente,
 
Thanks for the suggestion, I'll add it to the next release.
 

 
Brent
GeneralOther interesting stuffmemberLevente Farkas30 Apr '02 - 14:23 
Brent,
 
I am throwing together a menu bar (that is, a toolbar that has text buttons which run a popupmenu underneath when pressed), based on a '99 DiLascia article in MSJ. I intend to use a slightly modified version of your menu for the submenus. Are you interested in this stuff? I could surely use some help/suggestions when drawing the buttons in XP style. Maybe I will also post an article (not sure about this, since I'm running out of time with a couple of other projects).
 
Levente

QuestionIs it possible to user BCMenu on dialog?membersoichi29 Apr '02 - 7:35 
Before I tried to use BCMenu on dialog, and I simply couldn't get it to work. Has anybody tried and succeeded?
AnswerRe: Is it possible to user BCMenu on dialog?memberBrent Corkum29 Apr '02 - 10:26 
Download the Dialog based example at the top of the article. Also refer to item number 6 in the 3.01 update notes.
 
Brent
GeneralLeft alignment of acceleratorsmemberAnonymous29 Apr '02 - 4:45 
I really wish that BCMenu can have accelarators that supports left justification in future versions, cos' the Windows OS itself uses left justifications for accelarators text, and right justifications in accelarator text in menus makes the thing go ugly.
 
Hope that Brent Corkum can do something about this! Thanks Brent!!!!!!!
GeneralRe: Left alignment of acceleratorsmemberBrent Corkum29 Apr '02 - 5:45 
BCMenu has always right justified the accelarator text as does MS Office XP and MS Visual Studio 6. Now some applications left justify the accelerators such as Wordpad, Notepad, and IE. The default CMenu also left justifies accelarators. So there's no standard way of doing this. As far as being ugly, well to each their own. So in the next release I'll look at adding an option to change the way the accelarators are justified. However the default will be right justified because this is the way I like it.
 
Thanks for you comment.
 
Brent
QuestionFocused items, anyone?memberLevente Farkas29 Apr '02 - 3:53 
Hello,
 
There is yet another (minor) thing missing from this superb OD menu class. Namely, when you change the selected menu item using the keyboard and the menu includes some disabled items, Microsoft's menu will draw a "focus" rectangle around disabled items also, to let you see where your current pseudo-selection is.
 
This makes sense and BCMenu is a little bit handicapped without it. Can we please have this feature also? Thanks :->
 
Best regards,
Levente
AnswerRe: Focused items, anyone?memberBrent Corkum29 Apr '02 - 4:03 
You learn something new everyday. I had never noticed this since I never use the keyboard with a menu. I'll have to think about how to do this???
 
Brent
Generalother transparent colormemberDieter Hammer29 Apr '02 - 1:56 
first: this class is really wonderful, the only little problem until now was its hunger for resources. But I have a little prolem. I have bitmaps with another transparent color then RGB(192,192,192). In the previous version of bc-menu I changed the statement
m_bitmapBackground=RGB(192,192,192);
to my color. But in the newest version that doesnt' work. There is also a line in
void BCMenu::GetTransparentBitmap(CBitmap &bmp)
..
.
col=..
but these 2 changings don't seem to be enough.
Thank You
Dieter
GeneralRe: other transparent colormemberBrent Corkum29 Apr '02 - 6:08 
Dieter,
 
I just gave it a try and it seems to work. Did you forget to change the
 
m_bitmapBackgroundFlag
 
member variable to TRUE as well? I personally use the
 
void SetBitmapBackground(COLORREF color);
void UnSetBitmapBackground(void);
 
member functions to define a different background color for a bitmap. Somthing like:
 
m_menu.SetBitmapBackground(RGB(255,0,255));
m_menu.ModifyODMenu(NULL,ID_WINDOW_CASCADE,IDB_BITMAP9);
m_menu.UnSetBitmapBackground();
 
If the IDB_BITMAP9 bitmap had a magenta background instead of the standard RGB(192,192,192) background.
 


 
Brent
Generalthank You very muchmemberDieter Hammer29 Apr '02 - 20:59 
oh sorry, I stupidly really forgot to do this. Th new look in XP style is super.
I couldn't use the version with shadow until now, because resource. But now all is perfect!
CU
Dieter
GeneralSmall problemsmemberLevente Farkas27 Apr '02 - 18:23 
Hello,
 
First, this is excellent code. But I have some problems with it.
 
1) The menus are always (both with and without accelerators) too wide. Almost twice as wide as they should, and the right side is left blank. If an item has a "tab+accelerator_text" at the end, that is properly aligned to the right, but the wasted space in the middle is too much.
2) The space (height) taken up by the separators is too much.
3) Checkmarks are not properly aligned and are somewhat too small.
4) Height of submenu items is too small. I read your reply that there should be handlers for the menu-related messages in the class that has an OD menu, but I put those in the frame class (SDI), why isn't that enough?
 
I am running on Windows XP with the classic Windows theme. My menu font is Verdana 9pt.
 
Best regards,
Levente
GeneralCorrected somememberLevente Farkas28 Apr '02 - 13:49 
Hello again,
 
I found the cause for issue 1: in method MeasureItem there is code like this:
 
if(Win32s != GetShellType())
VERIFYX(::GetTextExtentPoint32W(...));
#ifndef UNICODE
else // Can't be UNICODE for Win32s
{
...
}
#endif
 
Well, it turns our that because I use some special VERIFY/ASSERT macros that expand to more than one line, the code above was alwas going through the else written for Win32s(ux). The solution is to simply wrap the VERIFY into {}.
 
I also solved the height of the separator, it now looks more like the MS menu. Again in method MeasureItem, at the beginning where the size of a separator is calculated, I canged it to this:
 
// Compute size of separators
lpMIS->itemWidth = 0;
if(IsLunaMenuStyle())
{
lpMIS->itemHeight = GetSystemMetrics(SM_CYBORDER) + 2;
}
else
{
int temp = GetSystemMetrics(SM_CYMENU) >> 1;
lpMIS->itemHeight = (temp > (m_iconY+CM_PAD)/2) ? temp : (m_iconY+CM_PAD)/2;
}
 
Best regards,
Levente

 


GeneralRe: Small problemsmemberBrent Corkum28 Apr '02 - 16:31 
The latest 3.03 release dels with some of your issues. The checkmarks and the separator height have been adjusted to look more MS like. There's also a setting that will narrow the menu's (see the update information). I also read you comments on using custom MACRO's which clears up my confusion since no one else has complained about the width.
 
With regards to item#4, if a class defines a BCMenu object and uses it, it should also define the message handlers.
 
Brent
GeneralA "Flat" Menu CreationmemberMike Jackson15 Apr '02 - 23:50 
Hi,
 
I have figured out how to give the BCMenu a flat look just like in the Office XP product.
 
I will distribute the source code as it is wanted, as it is fairly complex.
 
Mike Jackson
GeneralRe: A "Flat" Menu CreationmemberAlexMarbus22 Apr '02 - 4:02 
Why don't you just create a new article and upload it? Let's not keep us curious here? Smile | :)
 
--
Alex Marbus
www.marbus.net
But then again, I could be wrong.
GeneralRe: A "Flat" Menu CreationmemberMike Jackson22 Apr '02 - 15:53 
For everyones information, ive emailed my code to Brent for consideration in future releases of this class.
 
Im somewhat reluctant to distribute a modified version of his code, which is why I havent posted the code in the group.
 
Using a hook is the right idea, however i did have to modify Brent's code to get the alignment & various other things right.
GeneralRe: A "Flat" Menu CreationmemberRamon Casellas22 Apr '02 - 4:22 
Hi,
 

See the source in WTL distribution or in Bjarne Viksoe WTL XP Menu class
I don't recall the details, but more or less is as follows : (The main idea is to install a windows CBT hook to monitor creation of windows and obtain a handle to the menu window)
 

Steps:
 
Create a CMenuWindow : public CWindowImpl<CMenuWindow>
Implement OnNcPaint
 
Before Creation of the "menu"
Call SetHookEx CBT hook, monitor creation / destruction of windows. There exists a
"magic number" #XXXXX for the menu window class.
If code = Creation
            Subclass the window handle obtained
            (In WTL CMenuWindow* pwnd = new CMenuWindow ;
               pwnd->SubclassWindow (hwnd);
               ....)
            -- the object deletes itself in OnFinalMessage)
If code = destruction
               Clenup
Show the menu (track)
 
Remove windows hook.
 
It works fine. I've been hacking a little to make it work with WTL 7 and MDI
 

There exists an old PSDK sample "FakeMenu" that uses another approach: it emulates completely a menu by capturing the mouse, filtering messages and implementing a MessageLoop.
 

Regards,
R.

GeneralExellent Job!memberStatic7 Apr '02 - 17:19 
Great Job Man!
 
This is one of the most awsome tutorials iv seen..
 
Its Very cool. Has alota features. and is so dam simple.
 
Thx alot.
 
Keep up the good work

 
www.Static-Code.com
GeneralNeed help with function to import HMENUmemberKim Kemp29 Mar '02 - 3:20 
I have some menus that have been loaded from resource that then go through a routine to merge menu items from default menu. Since I have no resource with all the items, I'd like to take that HMENU and import it into a BCMenu. Does this seem doable? I took a stab at it but I'm still having problems getting it to work. Here's what I have.
BOOL BCMenu::ImportMenu(HMENU hMenu, BOOL bTopLevel)
{
    if (!Attach(hMenu))
        return false;
    int count = GetMenuItemCount();
    for(int i=0; i < count; ++i) {
        CString str;
        CMenu *pSub = GetSubMenu(i);
        UINT nFlags = GetMenuState( i, MF_BYPOSITION );
        int nID = GetMenuItemID(i);
        GetMenuString(i,str,MF_BYPOSITION);
        
        // Add the MF_OWNERDRAW flag if not specified:
        nFlags &= ~MF_STRING;
        if (bTopLevel)
            nFlags |= MF_POPUP;
        else
            nFlags |= MF_OWNERDRAW;
        
        if(nFlags & MF_POPUP){
            if (!pSub) {
                pSub = new CMenu;
            }
            nID = (int)((HMENU)pSub->m_hMenu);
            m_AllSubMenus.Add((HMENU)pSub->m_hMenu);
            m_SubMenus.Add((HMENU)pSub->m_hMenu);
        }
        BCMenuData *mdata = new BCMenuData;
        m_MenuList.Add(mdata);
        mdata->SetString(str);
        mdata->menuIconNormal = -1;
        mdata->xoffset=-1;
        mdata->nFlags = nFlags;
        mdata->nID = nID;
        if (bTopLevel) {
            CMenu::ModifyMenu(i,nFlags | MF_BYPOSITION, nID, str);
        } else {
            CMenu::ModifyMenu(i,nFlags | MF_BYPOSITION, nID, (LPCTSTR)mdata);
        }
        if (pSub) {
            if (pSub->m_hMenu) {
                BCMenu *pMenuNew = new BCMenu();
                pMenuNew->ImportMenu((HMENU)pSub->m_hMenu, FALSE);
            }
        }       
    }
    Detach();
	return(TRUE);
}

 
Thanks for your help!
Kim
GeneralRe: Need help with function to import HMENUmemberBrent Corkum29 Mar '02 - 7:09 
The problem with this is that the HMENU is most likely associated with a CMenu object, not a BCMenu object. So the only real way to do this, is have your function create a BCMenu object, return its handle, then use this new handle by substituting it for the old handle. This is the only way you can get it to call BCMenu's DrawItem and MeasureItem functions when you make it ownerdrawn. It is possible to call BCMenu's functions for CMenu objects but this is more much more difficult and error prone. You basically got to intercept the DrawItem and MeasureItem messages and process them yourself.
Yuk.
 

 
Brent
GeneralCheckMarkmemberAnonymous17 Mar '02 - 23:49 
I can't add bitmaped checkmark in popup menu. Ideas?
Also when use somthing like this:
 
BCMenu popup;
popup.LoadMenu(IDR_POPMENU);
BCMenu *pSub = ( BCMenu * ) popup.GetSubMenu(0);
CPoint pos;
GetCursorPos(&pos);
popup.CheckMenuItem( ID_P1, MF_BYCOMMAND | MF_CHECKED );
popup.ModifyODMenuW( NULL, ID_P1, &bmp ); //loaded in init();
pSub->TrackPopupMenu(0, pos.x, pos.y, this, NULL);
popup.DestroyMenu();
 
popup menu display a dark rect before ID_P1 menu item.
GeneralProblem when using the menu in dialogmemberPeder Alm12 Mar '02 - 21:43 
I'm using your menu in a dialog. When I use "Styles->Border->Thin" and "Extended Styles->Static edge" in the dialog properties and using a menuitem as a non-popup item (like a button in the menu), the icon is to big and is drawed into the dialog frame. How to fix this?
 
Confused | :confused:
GeneralSystem MenumemberSUPER_ZORRO8 Mar '02 - 1:06 
Thank youn for menu -- it is real cool.
I added to my app -- works perfect, but i have the same items in System menu and i whant them to look like you new style menus.
//........
CMenu* pSysMenu = GetSystemMenu(FALSE);
 
if (pSysMenu != NULL) {
CString strAboutMenu;
CBitmap bmp;
strAboutMenu.LoadString(IDS_ABOUTBOX);
if (!strAboutMenu.IsEmpty()){
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX_ALW, "&Always on Top\tCtrl+T");
}
//...........................................
Smile | :) Smile | :) Smile | :) Wink | ;) Smile | :)
GeneralRe: System MenumemberAndy Metcalfe18 Mar '02 - 0:04 
I've tried to get this to work too, and although I haven't managed it yet, I did unravel part of the puzzle, so it's possible that my code will help you on your way.
 
Take a look at
http://www.codeproject.com/menu/bcmenu.asp?forumid=150&select=98200&tid=95701#xx98200xx.
 
Good luck. If you get it working, please let me know. Smile | :)
 
Andy Metcalfe - Sonardyne International Ltd

Trouble with resource IDs? Try the Resource ID Organiser Add-In for Visual C++

"I would be careful in separating your wierdness, a good quirky weirdness, from the disturbed wierdness of people who take pleasure from PVC sheep with fruit repositories."
- Paul Watson
GeneralA bug in appending popup menu dynamicallymemberLeona5 Mar '02 - 17:05 
I want to add a popup menu
so as follow
BCMenu Menu,*pSub, *pSelect;
Menu.LoadMenu(IDR_TRAY_MENU);
pSub = (BCMenu *)Menu.GetSubMenu(0);
pSelect = (BCMenu *)pSub->GetSubMenu(6);
pSelect->AppendMenu(MF_POPUP, i, (LPCTSTR)Temp.strAccountName);
 
but debugging I find the address of Variant pSelect is NULL
 
Why? Can you help me?

GeneralRe: A bug in appending popup menu dynamicallymemberBrent Corkum6 Mar '02 - 4:14 
Duplicate the problem in the example application and send it to me.
 
Brent

GeneralRe: A bug in appending popup menu dynamicallymemberBrent Corkum6 Mar '02 - 16:52 
Leona,
 
Here's an example similar to what you want to do. Just replace the code in the below function with the code in member function by the same name in the example that comes with the class.
 

void CMymenuView::OnRButtonDown(UINT /*nFlags*/, CPoint point)
{
popmenu.LoadMenu(IDR_RIGHT_CLICK);
popmenu.LoadToolbar(IDR_TOOLBAR);
popmenu.LoadToolbar(IDR_MAINFRAME);
ClientToScreen(&point);
BCMenu *psub = (BCMenu *)popmenu.GetSubMenu(0);
BCMenu *pSelect = (BCMenu *)psub->GetSubMenu(1);
if(pSelect){
pSelect->AppendMenu(MF_STRING,ID_WINDOW_TILE_HORZ,_T("&Tile"));
pSelect->ModifyODMenu(NULL,ID_WINDOW_TILE_HORZ,IDB_WINDOW_TILE);
}
psub->TrackPopupMenu(TPM_LEFTALIGN|TPM_RIGHTBUTTON,point.x,point.y,AfxGetMainWnd());
popmenu.DestroyMenu();
}

GeneralSideBarmemberMichael Mac5 Mar '02 - 11:02 
I will be nice if you add a sidebar support.
Smile | :)

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130516.1 | Last Updated 28 Apr 2002
Article Copyright 1999 by Brent Corkum
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid