Owner Drawn Menu with Icons (4) (automatically uses toolbar res)

This is another way to draw a menu with bitmaps item. Basically, this thing was created to let the user see visually the correspondence of a menu item and a toolbar button, so that he can learn easily the meanings of sometime strange button drawing.

Now, the Visual C++ give us that new nice toolbar resources .. why bother with icons or bitmaps and link them to menus while we can automatically "connect" the toolbar resource to them ?

So I wrote a small class, CMenuSpawn, which takes care with some help from FrameWnd of menu remapping and drawing.
ownerdrawmenu4.gif (2479 bytes)

What has been updated, changed, added since the last version:
06.22.1998:
  • fixed something in documentation
06.14.1998:
  • fixed again standard menu font different from control panel settings
  • add support for having the main menu (on title bar) highlighted with 3d boxes instead of standard highlight color (this has been implemented in both MDI and SDI code, but tested only under MDI .. hope it will work with SDI too)
05.14.1998:
  • fix a bug that crashed when trying a not-existing shortcut under NT (thanks, Craig!)
  • enhanced drawing of disabled bitmap (thanks to Michael Santoro)
  • fixed standard menu font different from control panel settings
04.28.1998:
  • fix some memory/resource leaks reported from BoundsChecker but not from Msdev (I'm not an owner of BoundsChecker - thanks to H.Tome, C. Schmidt, and all the others who report them)
  • added support for shortcut for menu items and popups
  • added support for a user font for the menu
  • added support for user defined color of menu items
  • added support for using bitmap as menu background
  • (hope to have) fixed the bug who sometimes make the text (especially of popups) disappear and/or enlarge VERY much the popup - however, yet to understand why it did so :)
  • added a contructor with a boolean value to be used when creating popup menu (look at demo, in the view right mouse click)
  • added some base classes for CFrameWnd, CMDIFrameWnd and CMDIChildFrameWnd to make it easier the work; you can now simply rederive your CMainFrame from CSpawnFrameWnd or CSpawnMDIFrameWnd and eventually your CChildFrame from CSpawnMDIChildWnd to get the WM_MEASUREITEM, WM_DRAWITEM, WM_INITMENU, WM_INITMENUPOPUP handled

Now I will explain how to use it in a MDI application:

Step 1: including files

We need to include the CSpawnMenu class in our application and the Bitmap resource named IDB_MENUCHK from the sample application which comes along (it will be used to draw "check marks" aside menu item which need it - you can freely change it).

Step 2: handling frame messages

We also need to manage some of the frame windows handler; we have 2 ways to do this. The simplest is to derive the CMainFrame from the CSpawnMDIFrameWnd class instead of CMDIFrameWnd. This means you have to (using Search/Replace) replace all istance of CMDIFrameWnd with CSpawnMDIFrameWnd (and of course include in your project file the files SpawnMDIFrameWnd.cpp and SpawnMDIFrameWnd.h). You will also need to do this for the CChildFrame class, deriving from CSpawnMDIChildWnd and including SpawnMDIChildWnd.cpp/.h. The other way is to look at how this CSpawnFramexxx classes are been made and copy it (that means putting a CMenuSpawn member in the frame class header and handling the WM_DRAWITEM, WM_MEASUREITEM, WM_MENUCHAR, WM_INITMENU and WM_INITMENUPOPUP).

If you are making an SDI application instead of an MDI, derive the CFrameWnd from CSpawnFrameWnd and include the SpawnFrameWnd.cpp/.h files.

Step 3: initializing the CSpawnClass

We need to modify the CMainFrame class in this way:
  • add a CMenuSpawn cSpawn instance in the include file; note that if you use a CSpawnFramexxx class as base class for mainframe, you already get an instance of CMenuSpawn named cSpawn.
  • initialize the CMenuSpawn class in the CMainFrame constructor (cSpawn.LoadToolBarResource(IDR_MAINFRAME)) this will load the toolbar resource into the class; you can alse use the AddToolBarResource function to add more than toolbar resource to the class. In the demo app, I add a toolbar for the system menu
CMainFrame::CMainFrame()
{
	cSpawn.LoadToolBarResource(IDR_MAINFRAME);
}

Step 4: Note to Stingray Software's Objective Toolkit Pro users

If you are using the OT Pro, maybe along with the SECMenuBar to obtain Office97 menu button look and feel, you will also need to override the WindowProc virtual of the CMainFrame as noted below:
LRESULT CMainFrame::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) 
{
	if (message != WM_MEASUREITEM) return SECFrameWnd::WindowProc(message, wParam, lParam);
	else return CFrameWnd::WindowProc( message, wParam, lParam );	
}
If you are using another OTPro class other than SECFrameWnd as base class, change SECFrameWnd to reflect your base class. Note however that their very last release of OTPro support bitmapped menu (but not popups).

Note for misterious bug in the system menu (usually in SDI application)

It has been reported (thanks to Alexander V. Kudakov) that often in SDI application the system menu get corrupted. The fix is simple and mystic enought (magic here, guy!):
in the CMainframe::OnCreate() you'll have to call:
GetSystemMenu(false);
and thak's all, folks! If someone is able to explain why this fix works to us, Alexander and I will be glad to know.

Note for new 3d highlight of main menu items on menubar

I've added the 3d highlight of main menu items; look at source code for CSpawnMDIFrameWnd for understanding how. I've written code for those function:
void OnEnterMenuLoop(BOOL bIsTrackPopupMenu);
void OnExitMenuLoop(BOOL bIsTrackPopupMenu);
UINT OnNcHitTest(CPoint point);
void OnTimer(UINT nIDEvent);
int OnCreate(LPCREATESTRUCT lpCreateStruct);
void OnDestroy();
Actually, if you don't want this highlight system, you'll have to remove it by yourself .. I've not yet included a way to enable/disable it. If you are making a MDI application, look at the InitInstance function of the app class for the code between the // 3D HIGHLIGHT CODE and // 3D HIGHLIGHT CODE END comments; you'll need to do this in your app too or the initial menu will be ugly to see until you use them almost once.

And that's all to have basic coolmenu support.
Looking at the demo application, which implements an MDI Edit application, you will find a way to implement popup coolmenu.

Here follows a small description of the most useful function of the CSpawnMenu class:

bool MeasureItem(..)
To be called from the CWnd OnMeasureItem(..); call the base class if it return false

bool DrawItem(..)
To be called from the CWnd OnDrawItem(..); call the base class if it return false

bool LoadToolBarResource(unsigned int resId)
Load a toolbar resource in the class; an array of command id will be created and mapped to index of an imagelist

bool AddToolBarResource(unsigned int resId)
Add a toolbar resource to the class; works as the LoadToolBarResource

void RemapMenu(CMenu * pMenu)
Makes all the items of the menu OwnerDraw, (eventually) maps them with the appropiate images index based on the item command id; has to be called from the WM_INITMENU and WM_INITPOPUPMENU and before the TrackPopupMenu function

void EnableMenuItems(CMenu * pMenu, CWnd * pWnd)
Use the MFC command enabler mechanism to enable/disable menu items; need the pointer of the menu and the pointer of the CWnd of which the command enablers are to be used; it is designed to be used with popups menu from the right button click in Views (see the CEditView in sample apps for an example)

bool SetFont(LOGFONT * lf)
If someone, for evil pourpose, want to use a different font to draw the menu items, it is possible to call this function (before the menu are displayed, for example in the CMainFrame contructor after the LoadToolBarResource()). Look at the demo, there is commented code.

bool FindKeyboardShortcut(UINT nChar, UINT nFlags, CMenu * pMenu, LRESULT & lRes)
This is needed to handle the shortcuts when the menu is displayed (the &Letter shortcurs); you need to call it in response to WM_MENUCHAR messages of CMainFrame, CChildFrame if MDI app, and CView if using Spawn popups; the usage is a bit complex, look at demo and copy/paste the code.

void SetTextColor(const COLORREF crNormal, const COLORREF crSelected)
You can use this to specify different colors from default for the normal and selected menu items

void SetBackBitmap(const int iRes)
void SetBackBitmap(const int iRes, COLORREF crBackColor)

Now you can use bitmaps as background for menu using one of this 2 function. The first simply set a bitmap, the second change the color you specify to the menu background obtaining a simple transparency. Pay attention to the bitmap you use, which can make the menu text unreadable; you may need to use SetFont to set a bold font to read the text and the SetTextColor to use brighter color if you use strange bitmaps. In the demo, bitmap are enabled in the app menu, and disabled in the popup of the view. To see how to disable it, comment the SetBackBitmap in the CMainFrame constructor of the demo.

bool GetMenuItemText(CString &csText, CMenu * pMenu, const int cmd, bool bByPos)
Use this function to retrieve the text of a menu item; csText will contain the text, cmd is the menu identifier or position, bByPos is true if cmd is the position of menu identifier or false if it is an identifier. If the funtion returns true, csText contain valid text.

bool IsSpawnMenu(CMenu * pMenu, const int iItem, const bool bByPos)
This function is used internally; however, you can use it to see if a menu item as been handled by SpawnMenu or not (not usually means bitmap menu item, like icons of mdi child and maximize/minimize/close buttons of mdi child, which are handled by Evil Windows as menus, and not as buttons as everybody could imagine).

Very thanks to Craig Schmidt for support!

Feel free to use and modify this class with all your software - I will only be glad to know for which apps it will be used and of any improvement you will do to it!

Download demo project 107K

Last updated: 26 May 1998



Comments

  • Resource leak

    Posted by Legacy on 09/17/2003 12:00am

    Originally posted by: Heiko Gschwind

    Hello,

    there seems to be a resource leak in method DrawItem() of class CMenuSpawn:

    ...
    {
    HICON hIcon = m_ilList.ExtractIcon( pItem->iImageIdx );
    pDC->DrawState( CPoint(rcImage.left + ax, rcImage.top + ay ), m_szImage, (HICON)hIcon, DST_ICON | DSS_DISABLED, (CBrush *)NULL );
    }
    ...

    After extracting an icon the icon handle has to be destroyed with ::DestroyIcon(...);

    ...
    {
    HICON hIcon = m_ilList.ExtractIcon( pItem->iImageIdx );
    pDC->DrawState( CPoint(rcImage.left + ax, rcImage.top + ay ), m_szImage, (HICON)hIcon, DST_ICON | DSS_DISABLED, (CBrush *)NULL );
    ::DestroyIcon( hIcon );
    }
    ...

    Heiko

    Reply
  • Flat Menus

    Posted by Legacy on 03/23/2002 12:00am

    Originally posted by: CosmoS

    Your Menus are rally cool, but could you use flat Menus?

    Reply
  • How should I do in a SDI application has two toolbars?

    Posted by Legacy on 03/31/2001 12:00am

    Originally posted by: Joshua Liu

    How should I do in a SDI application that has two toolbars?
    

    Reply
  • Fixing the problem with the Recent File List

    Posted by Legacy on 09/30/2000 12:00am

    Originally posted by: Richard Hollis

    If you have been using this class you might of noticed that the recent file list
    
    sizing is wrong. It looks like the menus did before you added the extended menu class.

    To make the recent file list look size correctly with the rest of the menus
    add the following code to your CWinApp dervied class:

    //
    // myapp.cpp
    //////////////////////////////////////////////////////

    BEGIN_MESSAGE_MAP(CMapperApp, CWinApp)
    //...
    //...
    ON_UPDATE_COMMAND_UI_RANGE( ID_FILE_MRU_FIRST, ID_FILE_MRU_LAST, OnUpdateRecentFile)
    //...
    //...
    END_MESSAGE_MAP()

    void CMapperApp::OnUpdateRecentFile(CCmdUI* pCmdUI)
    {
    m_pRecentFileList->UpdateMenu(pCmdUI);

    CMainFrame* pFrameWnd = (CMainFrame*)AfxGetMainWnd();
    ASSERT(pFrameWnd);

    if(pFrameWnd)
    {
    CMenu *pMenu = pFrameWnd->GetMenu();
    ASSERT(pMenu);

    if(pMenu)
    {
    CMenu *pSubMenu = pMenu->GetSubMenu(0);
    ASSERT(pSubMenu);

    if(pSubMenu)
    pFrameWnd->cSpawn.RemapMenu(pSubMenu);
    }
    }
    }

    And then add the following line to the header file:

    //
    // myapp.h
    //////////////////////////////////////////////////////

    // Implementation
    //{{AFX_MSG(CMapperApp)
    //....
    //....
    afx_msg void OnUpdateRecentFile(CCmdUI* pCmdUI);
    //....
    //....
    //}}AFX_MSG
    DECLARE_MESSAGE_MAP()

    Reply
  • Using with SECMenuBar class

    Posted by Legacy on 05/23/2000 12:00am

    Originally posted by: Scott MacLaughlin

    How do you use these menu classes if you are also using the SECMenuBar class from Stringray's Objective Toolkit Pro 6.03 for a dockable menu bar ?
    

    Reply
  • Solution for Windows 2000 Problem

    Posted by Legacy on 04/06/2000 12:00am

    Originally posted by: Walter Hahn

    The Problem could be fixed by modifying the handler for WM_INITMENUPOPUP in the class deviated from CFrameWnd. In the example from Iuri Apollonio, replace the code segment:

    void CSpawnFrameWnd::OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu)
    {
    CFrameWnd::OnInitMenuPopup(pPopupMenu, nIndex, bSysMenu);
    cSpawn.RemapMenu(pPopupMenu);
    }

    found in file SpawnFrameWnd.cpp by the following code, that calls RemapMenu only for system menus:

    void CSpawnFrameWnd::OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu)
    {
    CFrameWnd::OnInitMenuPopup(pPopupMenu, nIndex, bSysMenu);
    if (bSysMenu)
    cSpawn.RemapMenu(pPopupMenu);
    }

    With this modification the menus with icons work on both platforms (NT, W2K) as expected.

    Examination of the WM_INITMENU and WM_INITMENUPOPUP calls show, that on Windows 2000 the handler for WM_INITMENUPOPUP is also called for popup items included in regular menus and not only for system popup menus. By this, RemapMenu() get confused and CMenuSpawn::MeasureItem() handles popup menus within regular menus like "title Items", which have no icons and a size only depending on the text size

    Reply
  • Windows 2000 Problems

    Posted by Legacy on 03/20/2000 12:00am

    Originally posted by: Chris Ryan

    I have been trying to use this class with the supplied sample in Windows 2000. A problem occurrs with Pop-out menus. The size of these menu items is scruched way down, making it unreadable. I will bee looking into this a bit further, however was wondering if there were any solutions already out there. Thanks

    Reply
  • How do I disable a menu item in COOL MENU?

    Posted by Legacy on 08/13/1999 12:00am

    Originally posted by: Sujatha

    Hi,
    I would like to enable/disable a sub menu item on the event of
    clicking another menu item.I tried with OnUpdatexxxx() by
    setting pCmdUI->Enable(TRUE|FALSE). Also I tried using your
    EnableMenuItems()... to enable/disable my item (Actually I passed
    CWnd::GetMenu() as the first argument to this call but it seems to crash
    the application. Could you please help me out in this?
    Thanks,
    Sujatha.

    Reply
  • About Create a Owerdraw menu

    Posted by Legacy on 05/25/1999 12:00am

    Originally posted by: Why Zhang

    I want to create a popup menu whose menuitems are image(or ownerdraw) without text when sending TOOLBARDROPDOWN message, like selecting the Line mode in Ms Word.

    I have failed some times. I can't let the image-menuitem
    drawn according to my idea. I can't find out the reason.

    Who can help you? Better give me a example.
    SOS!

    Reply
  • Dialog based application?

    Posted by Legacy on 04/13/1999 12:00am

    Originally posted by: C. Natal

    Hi,

    I'm a beginner on MFC and I really like your menu.
    I just would like to know if it is possible, in some way, to use your code on my dialog based application.

    Thanks!
    Regards,
    Carlos Natal

    Reply
  • Loading, Please Wait ...

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Savvy enterprises are discovering that the cloud holds the power to transform IT processes and support business objectives. IT departments can use the cloud to redefine the continuum of development and operations—a process that is becoming known as DevOps. Download the Executive Brief DevOps: Why IT Operations Managers Should Care About the Cloud—prepared by Frost & Sullivan and sponsored by IBM—to learn how IBM SmartCloud Application services provide a robust platform that streamlines …

  • Live Event Date: August 20, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT When you look at natural user interfaces as a developer, it isn't just fun and games. There are some very serious, real-world usage models of how things can help make the world a better place – things like Intel® RealSense™ technology. Check out this upcoming eSeminar and join the panel of experts, both from inside and outside of Intel, as they discuss how natural user interfaces will likely be getting adopted in a wide variety …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds