Features
The class, CMenuModifier
, is targeted to MFC programs, which has the following features:
- 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.
- 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.
- 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:
WM_DRAWITEM
and 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:
-
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*pMenu | Menu to be modified |
BOOL bMainMenu | True for main menu, false for track popup menu |
CToolBar*pBar | Image toolbar, or NULL if not use images |
int iImageStyle | Enabled image style, not used if pBar is NULL - refer to sample 3 below. |
COLORREF *pClrMenuBar | Pointer to color of menu bar, use default color if NULL |
COLORREF *pClrVertBar | Pointer to color of vertical bar, use default color if NULL |
COLORREF*pClrSelected | Pointer to color of selected item, use default color if NULL |
-
BOOL OnMeasureItem (int nIDCtl, MEASUREITEMSTRUCT*pMIS);
Call this function in message handler:
OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct)
-
BOOL OnDrawItem (int nIDCtl, DRAWITEMSTRUCT*pDIS);
Call this function in message handler:
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:
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:
void SetChechedItemStyle (BOOL bUseObmBitmap);
If bUseObmBitmap
is true
(default), image for checked menu item is Windows OBM check box. Otherwise:
- If the checked menu item has an image, the image is displayed with a sunken 3D border to indicate it is checked.
- If the checked menu item doesn't have an image, Windows OBM check box is displayed.
Inner Classes
There are five inner classes:
BitmapBuffer
Buffer to hold bitmap bits, which is simple and easy-to-use.
BitmapUnit
Contains enabled and disabled bitmaps.
MenuUser
Pointer of the class is added as parameter lpszNewItem
in Windows menu function:
ModifyMenu( UINT nPosition, UINT nFlags, UINT nIDNewItem, LPCTSTR lpszNewItem);
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.
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.
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:
Two steps are needed to do this:
1. Change menu bar color as dialog color in SetOwnerDraw(...) function.
At this time, menu looks like:
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:
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
- 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.
- 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.
Last Hints
- 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.
- 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