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






4.56/5 (9 votes)
Nov 30, 2003
2 min read

88303

4329
This article describes an owner drawn menu with icons, fade 2D-3D horizontal/vertical.
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
-
"Recent Files"
If you have "Recent Files" you must use
LoadStdProfileSettingsEx
instead ofLoadStdProfileSettings
, on the derived class fromCWinApp
.A new class is used in
LoadStdProfileSettingsEx
:CRecentFileListEx
derived fromCRecentFileList
.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 eliminateCRecentFileListEx
class andvoid LoadStdProfileSettingsEx(UINT nMaxMRU);
function.All this are used for handling items which are added at run time on "Recent Files" section menu.
-
"Active Windows"
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 theColorMenu
that "Active Windows" menu was changed withRefreshInternalMenu
function.On
CMainFrame
, you must defineRefreshInternalMenu()
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. -
"Final step"
On
CMainFrame
, you must define all menus: one forCMainFrame
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!