Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Mobile / WinMobile

CmenuModifier - An Owner Draw Image Menu

3.92/5 (10 votes)
3 Jun 2008CPOL6 min read 1   1.9K  
CmenuModifier - an owner draw image menu

Features

The class, CMenuModifier, is targeted to MFC programs, which has the following features: 

  1. Trusted

    It uses raw owner-draw method to modify existing menu (CMenu), so it is trusted (compared to using another menu to replace existing menu) and uses less memory.

  2. Add images to any level sub-menus

    I used some menu classes downloaded from CodeProject, but those classes can only add images to top level sub-menus. This is why I created this class.

  3. Suitable for all MFC applications such as SDI, MDI, Dialog menu and Track-Popup Menu.

Class File

All code is inside CMenuModifier.h (no CPP file) under "Class" directory.
You only need to include the head file in your program.

Image and Transparent color

To use image, a CToolBar instance must be past to SetOwnerDraw(...) function.
The CToolBar instance can be either global or local member.
If without CToolBar instance, image will not be added to menu.

Important

Color of first pixel (top-left) of first button on CToolBar stands for transparent color.
When images are drawn on menus, the color is transparent to its background (menu vertical bar) - refer to sample 4 below.

Class Instance

Instance of CMenuModifier must be a class member or global, because it is used by two messages:

  1. WM_DRAWITEM and
  2. WM_MEASUREITEM 
  • Static menu Each static menu, such as SDI, MDI and dialog main menu, requires one CMenuModifier instance.
  • Track popup menu All track popup menus in one window require only one CMenuModifier instance.
    You can use one instance for one track popup menu respectively.

Functions and Use

The following three functions must be used: 

  1. C++
    BOOL SetOwnerDraw (CMenu*pMenu,BOOL bMainMenu,CToolBar*pBar=NULL,
    int iImageStyle=CCMenuModifier::FLATE,
    COLORREF*pClrMenuBar=NULL, 
    	COLORREF*pClrVertBar=NULL, COLORREF*pClrSelected=NULL); 

    Call this function when menu is ready.
    Function parameters, all are "in" values.

    CMenu*pMenuMenu to be modified
    BOOL bMainMenuTrue for main menu, false for track popup menu
    CToolBar*pBarImage toolbar, or NULL if not use images
    int iImageStyleEnabled image style, not used if pBar is NULL - refer to sample 3 below.
    COLORREF *pClrMenuBarPointer to color of menu bar, use default color if NULL
    COLORREF *pClrVertBarPointer to color of vertical bar, use default color if NULL
    COLORREF*pClrSelectedPointer to color of selected item, use default color if NULL

  2. C++
    BOOL OnMeasureItem (int nIDCtl, MEASUREITEMSTRUCT*pMIS); 

    Call this function in message handler:

    C++
    OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct)
  3. C++
    BOOL OnDrawItem (int nIDCtl, DRAWITEMSTRUCT*pDIS); 

    Call this function in message handler:

    C++
    OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct)

If multiple menus need to be modified, the best way to call functions 2. and 3. are:
For example, CMenuModifier instances of md_Main and md_Popup are used to modify main menu and track popup menus. The following code will make modification faster:  

C++
BOOL b=md_Main.OnDrawMeasureItem(...);
if(b==0) b=md_Popup.OnDrawMeasureItem(...); 

The above code optimizes and speeds up owner draw process.
However, the following four samples do not call the two functions in the above optimizing way because count of menu items are not too heavy. 

The following function is optionally used: 

C++
void SetChechedItemStyle (BOOL bUseObmBitmap);

If bUseObmBitmap is true (default), image for checked menu item is Windows OBM check box. Otherwise:

  1. If the checked menu item has an image, the image is displayed with a sunken 3D border to indicate it is checked.
  2. If the checked menu item doesn't have an image, Windows OBM check box is displayed.

Inner Classes

There are five inner classes:

  1. BitmapBuffer

    Buffer to hold bitmap bits, which is simple and easy-to-use.

  2. BitmapUnit

    Contains enabled and disabled bitmaps. 

  3. MenuUser

    Pointer of the class is added as parameter lpszNewItem in Windows menu function:

    C++
    ModifyMenu( UINT nPosition, UINT nFlags, UINT nIDNewItem, LPCTSTR lpszNewItem);
  4. MenuUserArray

    A sorted array to hold pointers of MenuUser, which can search pointer of MenuUser in the array faster.
    Windows menu, when in owner draw, is not properly object-oriented, which measures and draws menu item directly without considering top most menu ID.
    Fast searching in the array is important and efficient if a window has multiple menus (i.e. main menu and track popup menus) with heavy items.  

  5. ObmChecked

    Load OBM_CHECK image for checked menu item.

Samples

There are four samples inside the zipped file. 

1. SDI Sample

The sample modifies 3 menus: one main frame menu and two track popup menus.
So the sample contains two instances of CMenuModifier, one for main menu, another for 2 track popup menus.

2. MDI Sample

The sample contains three instances of CMenuModifier, one for main frame menu, one for child window menu, another for 2 track popup menus.

3. Dialog Sample

The sample modifies 3 menus: one main menu and two track popup menus.
So the sample contains two instances of CMenuModifier, one for main menu, another for 2 track popup menus.
CToolBar instances are local variables in the sample.

The sample changes enable image styles.
Menu displayed in the figure below is a track popup menu.

Image 1

Enabled images are raised and nicer than flat style in dark menu vertical bar.

4. Colored Dialog Sample 

CToolBar instances are global variables (class members) in the sample.

The sample changes menu bar color to fit to colored dialog.
  
Dialog background color is determined by CWinApp::SetDialogBkColor(RGB(191,223,223)),.

Last results are:

Image 2

Two steps are needed to do this:

1. Change menu bar color as dialog color in SetOwnerDraw(...) function.

At this time, menu looks like:

Image 3

I have tried to add an extra long menu after last menu ("Help" menu in this sample) programmatically in the class to cover right part of light-gray menu bar, but Windows OS doesn't allow to do that - the extra long menu either disappears or is moved to second row by OS. 

2. Add several more blank, grayed menu items

Those menu items are added from VC resource manually.
At this time, menu looks like:

Image 4

Then using a modeless dialog box to cover right part of light-gray menu bar - those blank menu items make covering easier.

Please note, this step has nothing related to the class, I just want to show how to use the class in colored dialog.

Limitations

  1. Invalid for 256 color system setting

    The class analyzes bitmaps of CToolBar then generates and stores enabled and disabled images inside the class. So CToolBar can be a local variable and can be destroyed at once after passing to SetOwnerDraw(...) function.

    Limitation is that color setting of computer system must be 16 or 32 bits. If computer setting is 256 colors, images will not be displayed - because I never modify 256 color bitmap - I think no one uses this setting today. If you need, please modify P_CreateUserImage(...) function of the class.

  2. Invalid for dynamic menu items

    The class modifies menu items created before calling SetOwnerDraw(...) function.
    Dynamic menu items, such as "Recent Files" or "Window" in MDI, may be added at any time, so those menu items are not set as Owner-draw and are not modified.

More Information

Images size inside the class is two pixels larger than original image of CToolBar button.

Disabled image requires one extra pixel at right and bottom borders. Enabled image in styles needs one extra pixel at all borders.

The figure below shows how a flat image is modified to raised-both style by adding while line at left and top sides, and dark-gray line at right and bottom sides.

Image 5

Last Hints

  1. Remind again

    First pixel of tool bar stands for transparent color. Normally, the color should be light-gray. "Transparent" means the color is changed to image background color or menu vertical bar color.

  2. Super Calculator

    This calculator can calculate matrix, vector, complex, definite integral, equation roots, common functions (such as sine, log), statistics, draw-locus, etc. with many different features, which is one of my satisfied programs, I hope you enjoy it, it is free.
    This hint is a gift and not related to the menu class.

History

  • 3rd June, 2008: Initial post

License

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