Click here to Skip to main content
15,878,945 members
Articles / Desktop Programming / MFC
Article

Owner Drawn Menu with Icons, Fade 2D-3D Horizontal/Vertical

Rate me:
Please Sign up or sign in to vote.
4.56/5 (10 votes)
29 Nov 20032 min read 87.7K   4.3K   45   3
This article describes an owner drawn menu with icons, fade 2D-3D horizontal/vertical.

Image 1

The used colors are just for highlighting the power of the menu, they can be changed.

Introduction

Principal class is CColorMenu:

#define FADE_NONE    0x00 // No 3D Fade
#define FADE_HORZ    0x01 // Fade 3D HORZ
#define FADE_VERT    0x02 // Fade 3D VERT
#define FADE2D_NONE    0x00 // No 2D Fade
#define FADE2D_HORZ    0x10 // Fade 2D HORZ
#define FADE2D_VERT    0x20 // Fade 2D VERT
#define MENU_NONE    0x00 // undefined type of menu
#define MENU_SIMPLE    0x01 // normal menu item
#define MENU_POPUP    0x02 // pop-up menu item

Members

COLORREF m_Bk2DPop; // Start color (background) at 2D on pop-up menus
COLORREF m_Txt2DPop; // Text color at 2D on pop-up menus
COLORREF m_Bk2DMen; // Start color (background) at 2D on menus
COLORREF m_Txt2DMen; // Text color at 2D on menus

COLORREF m_Bk3DPop; // Start color (background) at 3D on pop-up menus
COLORREF m_Txt3DPop; // Text color at 3D on pop-up menus
COLORREF m_Bk3DMen; // Start color (background) at 3D on menus
COLORREF m_Txt3DMen; // Text color at 2D on menus

COLORREF m_Fade2DPop; // End color for 2D used at FADE on pop-up menus
COLORREF m_Fade3DPop; // End color for 3D used at FADE on pop-up menus
 
COLORREF m_Fade2DMen; // End color for 2D used at FADE on menus
COLORREF m_Fade3DMen; // End color for 3D used at FADE on menus

protected:         // settings of control

COLORREF m_ColCheck; // Color of Check
COLORREF m_ColSepar; // Color of separator(MF_SEPARATOR)

COLORREF m_BkGray; // Color background for Gray value
COLORREF m_TxtGray; // Color text for Gray value
 
UINT m_FadeTypeMen;   // At 2D FADE for menu start 
                      //color m_Bk2DMen TO end 
                      //color m_Fade2DMen for menus
                      // At 3D FADE for menu start color 
                      //m_Bk3DMen TO end color m_Fade3DMen for menus

UINT m_FadeTypePop;   // At 2D FADE for menu start 
                      //color m_Bk2DPop TO end 
                      //color m_Fade2DPop for pop-up menus
                      // At 3D FADE for menu start color 
                      //m_Bk3DPop TO end color 
                      //m_Fade3DPop for pop-up menus

int m_Val3D; // Deep 3D value
int m_HeightSep; // Height of separator
int m_HeightCel; // Height of a menu item
int m_WidthCel; // Width of a menu item
VTYPE_ITEMCONT* m_itemsStr; // internal Use
VTYPE_MENUCOLOR* m_HandlesMenu; // internal Use

Steps

  1. "Recent Files"

    Image 2

    If you have "Recent Files" you must use LoadStdProfileSettingsEx instead of LoadStdProfileSettings, on the derived class from CWinApp.

    A new class is used in LoadStdProfileSettingsEx: CRecentFileListEx derived from CRecentFileList.

    BOOL CSampleMenuApp::InitInstance()
    
    {
    
    //...................
        // Load standard INI file options (including MRU)    
        LoadStdProfileSettingsEx(_AFX_MRU_COUNT);  
    
    //...................
    
        return TRUE;
    
    };

    If you don't have "Recent Files" or _AFX_MRU_COUNT = 0, you can eliminate CRecentFileListEx class and void LoadStdProfileSettingsEx(UINT nMaxMRU); function.

    All this are used for handling items which are added at run time on "Recent Files" section menu.

  2. "Active Windows"

    Image 3

    If you use "Active Windows" it is a little bit hard to handle items which are inserted at run time with message WM_MDISETMENU.

    On each child frame, you must implement virtual void OnUpdateFrameMenu(BOOL bActive, CWnd* pActivateWnd, HMENU hMenuAlt); as follows:

    void CChildFrame::OnUpdateFrameMenu(BOOL bActivate, 
                              CWnd* pActivateWnd, HMENU hMenuAlt)
    {
    
        CMDIChildWnd::OnUpdateFrameMenu(bActivate, pActivateWnd, hMenuAlt);
        ((CMainFrame*)GetParentFrame())->RefreshInternalMenu();
    
    }

    This is a virtual function from CMDIChildWnd which updates the frame menu, and you must inform the ColorMenu that "Active Windows" menu was changed with RefreshInternalMenu function.

    On CMainFrame, you must define RefreshInternalMenu() as following:

    void CMainFrame::RefreshInternalMenu()
    {
        m_IdTimer = SetTimer(INTERNAL_UPDATE_TIMER, 50, NULL);
    }

    and on timer function you must do this:

    void CMainFrame::OnTimer(UINT nIDEvent) 
    {
        if(nIDEvent == INTERNAL_UPDATE_TIMER)
        {
    
            KillTimer(m_IdTimer);
            m_IdTimer = NULL;
            HMENU hMenuWindow = GetWindowMenuPopup(GetMenuOfView());
            CColorMenu* pMenu = m_menuChildFrame.FindSubMenu(hMenuWindow);
            if(pMenu)
                pMenu->RefreshMenusForMDIs();
        }
        
        CMDIFrameWnd::OnTimer(nIDEvent);
    }

    All this must be done with a timer because UpdateFrameMenu is made as "delayed refresh". If you will not use the timer, you will "not see the new updated menu", you will see menu unchanged.

  3. "Final step"

    On CMainFrame, you must define all menus: one for CMainFrame and the rest for child frames (if you have more than one).

    class CMainFrame : public CMDIFrameWnd
    {
        HMENU GetMenuOfView(){return m_menuChildFrame.GetSafeHmenu();}
        CColorMenu      m_menuMainFrame;
        CColorMenu      m_menuChildFrame;
    }

    So, if you have more child frames with different menus, you must define more CColorMenu m_menuChildFrame;

    On CMainFrame::OnCreate you must just load menus, set the needed icons and replace the default menu as follows:

    int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
    {
        if (CMDIFrameWnd::OnCreate(lpCreateStruct) == -1)
            return -1;
    
        // Load menus
        m_menuMainFrame.LoadMenu(IDR_MAINFRAME);
        m_menuChildFrame.LoadMenu(IDR_SAMPLETYPE);
    
        // set the icon to items if you want
        m_menuMainFrame.SetIconToHandle(ID_FILE_NEW, IDI_ICON_NEW);
        //....................
    
        // replace the default menu
        HMENU OldMenuDefault = m_hMenuDefault;
        SetMenu(&m_menuMainFrame);
        m_hMenuDefault = m_menuMainFrame.GetSafeHmenu();
        ::DestroyMenu(OldMenuDefault);
    
        // if you set skin please make so:
        /*
        CSSkin skin;
        // ...........
    
        m_menuMainFrame.SetSkin(&skin);
        DrawMenuBar();
                */
     }

    After that you must implement CMainFrame::OnMeasureItem as follows:

    void CMainFrame::OnMeasureItem(int nIDCtl, 
        LPMEASUREITEMSTRUCT lpMeasureItemStruct)
    {
        if(lpMeasureItemStruct->CtlType == ODT_MENU)
        {
    
            if(m_menuMainFrame.IsMenu((HMENU)lpMeasureItemStruct->itemID))
            {
                m_menuMainFrame.MeasureItem(lpMeasureItemStruct);
                return;
            }
    
            if(m_menuChildFrame.IsMenu((HMENU)lpMeasureItemStruct->itemID))
            {
                m_menuChildFrame.MeasureItem(lpMeasureItemStruct);
                return;
            }
    
            //……………………………………
    
        }
    
        CMDIFrameWnd::OnMeasureItem(nIDCtl, lpMeasureItemStruct);
    
    };

    And destroy the menus... on CMainFrame::OnDestroy().

    void CMainFrame::OnDestroy() 
    {
    
        SetMenu(NULL);
        ::DestroyMenu(m_hMenuDefault);
        m_hMenuDefault = NULL;
        m_menuMainFrame.DestroyMenu();
        m_menuChildFrame.DestroyMenu();
        CMDIFrameWnd::OnDestroy();
    }

    And last, on each child frame, on CChildFrame::OnCreate, replace the default menu.

    int CChildFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) 
    {
    
        if (CMDIChildWnd::OnCreate(lpCreateStruct) == -1)
            return -1;
    
        CMainFrame* pFram = (CMainFrame*)GetParentFrame();
        HMENU OldMenuDefault = m_hMenuDefault;
        m_hMenuDefault = pFram->GetMenuOfView();
        m_hMenuShared = m_hMenuDefault;
        ::DestroyMenu(OldMenuDefault);
        // if you set skin please make so:
        /*
        CSSkin skin;
        // ...........
        pFram->m_menuChildFrame.SetSkin(&skin);
        DrawMenuBar();
        */
     }

    And on destroy, prevent not to destroy the default menu.

    void CChildFrame::OnDestroy() 
    {
        m_hMenuShared = NULL;
        m_hMenuDefault = NULL;
        CMDIChildWnd::OnDestroy();
    }

    Have luck!

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
France France
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionApplication CDialog Based Pin
Member 205896324-Sep-08 3:38
Member 205896324-Sep-08 3:38 

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

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.