Owner Drawn Menu with Icons

CCustomMenu: This class is the owner drawn version of the CMenu. You will be able to
  1. Set the Text Color.
  2. Set the highlight color
  3. Set the Highlight Style.
  4. Set the selected and normal Icon for the Menu item.
  5. Set the Icon size.

In order to use it, you need to create MENUDATA stuctrure and fill it with the text and the selected and normal Icon IDs for each menu item. Then, you have to pass the pointer to that structure via the LPCTSTR argument of the AppendMenu or the InsertMenu. This is how the code snippet using this looks like. The following are declared as the class members so that they can stay till the class is present.

In the .H file of the containing class

	CCustomMenu m_cCustomMenu;
	MENUDATA menuData [8];	// as many menu items are present , You should be able to use 
				//new and do the same

In the .CPP file of the class where you want to use the menu
	m_customMenu.CreateMenu ();


	m_customMenu.SetIconSize (25,25);	//This is to set the size of the Icon. 
						// This should be used only once for any menu
	// in order to resize it, destroy and create the menu again with  different size.

	m_customMenu.SetHighlightStyle (Normal); //Or TextOnly, if you want the
	// background color to remain the same
	// and the Text color to change to the Highlight color.

The following setXXXColor sets the menu colors. If you dont want to change any, Dont call these member functions.

	m_customMenu.SetTextColor (RGB (255,0,0));
	m_customMenu.SetBackColor (RGB (255,255,255));
	m_customMenu.SetHighlightColor (RGB (0,0,255));

	lstrcpy (menuData[0].menuText , "text1");
	menuData[0].menuIconNormal= IDI_ICON1;
	m_customMenu.AppendMenu (MF_OWNERDRAW,3,(LPCTSTR)menuData);

Do this for each menu item. For the Popup menu items, this will not work since, the 2nd argument of AppendMenu is actually a HMENU instead of the ID. So, for that particular case, Store the HMENU which you pass in the 2 argument and Handle OnMeasureItem for the main window class and do the following:

void CMyView::OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
	if ( lpMeasureItemStruct->CtlType == ODT_MENU &&
			IsMenu((HMENU)lpMeasureItemStruct->itemID) &&
			(lpMeasureItemStruct->itemID == (UINT)m_hMenuSub) )
	{
		m_customMenu.MeasureItem (lpMeasureItemStruct);
	}
	else
		// let MFC's self-drawing handle it
		CView::OnMeasureItem(nIDCtl, lpMeasureItemStruct);
}
The following are the member functions you can use to customize your Menu: (names are pretty self explanatory).
	void SetTextColor (COLORREF );
	void SetBackColor (COLORREF);
	void SetHighlightColor (COLORREF);
	void SetIconSize (int, int);
	void SetHighlightStyle (HIGHLIGHTSTYLE ); // HIGHLIGHTSTYLE : enum {Normal, TextOnly}
	void SetHighlightTextColor (COLORREF);




//*************************************************************************
// CustomMenu.h : header file
//

#if
!defined(AFX_CUSTOMMENU_H__FE5B01C3_1E02_11D1_B87A_0060979CDF6D__INCLUDED_)
#define AFX_CUSTOMMENU_H__FE5B01C3_1E02_11D1_B87A_0060979CDF6D__INCLUDED_

#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000
class MENUDATA
{
public:
	MENUDATA () { menuIconNormal = -1; menuIconSelected = -1;};
	char  menuText[32];
	UINT menuIconNormal;
	UINT menuIconSelected;
};


typedef enum {Normal,TextOnly} HIGHLIGHTSTYLE;

///////////////////////////////////////////////////////////////////////////
//
// CCustomMenu window

class CCustomMenu : public CMenu
{
// Construction
public:
	CCustomMenu();

// Attributes
public:

// Operations
public:

// Overrides
	// ClassWizard generated virtual function overrides
	//{{AFX_VIRTUAL(CCustomMenu)
	//}}AFX_VIRTUAL

// Implementation
public:
	virtual ~CCustomMenu();
	virtual void DrawItem( LPDRAWITEMSTRUCT);
	virtual void MeasureItem( LPMEASUREITEMSTRUCT );
	void SetTextColor (COLORREF );
	void SetBackColor (COLORREF);
	void SetHighlightColor (COLORREF);
	void SetIconSize (int, int);
	void SetHighlightStyle (HIGHLIGHTSTYLE );
	void SetHighlightTextColor (COLORREF);

	// Generated message map functions
protected:
	COLORREF m_crText;
	COLORREF m_clrBack;
	COLORREF m_clrText;
	COLORREF m_clrHilight;
	COLORREF m_clrHilightText;
	LOGFONT m_lf;
	CFont m_fontMenu;
	UINT m_iMenuHeight;
	BOOL m_bLBtnDown;
	CBrush m_brBackground,m_brSelect;
	CPen m_penBack;
	int m_iconX,m_iconY;
	HIGHLIGHTSTYLE m_hilightStyle;

	//{{AFX_MSG(CCustomMenu)
		// NOTE - the ClassWizard will add and remove member functions here.
	//}}AFX_MSG
};

///////////////////////////////////////////////////////////////////////////
//

//{{AFX_INSERT_LOCATION}}
// Microsoft Developer Studio will insert additional declarations immediately before the previous line.

#endif //!defined(AFX_CUSTOMMENU_H__FE5B01C3_1E02_11D1_B87A_0060979CDF6D__INCLUDED_)

//*************************************************************************
// CustomMenu.cpp : implementation file
//

#include "stdafx.h"
#include "CustomMenu.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

///////////////////////////////////////////////////////////////////////////
//
// CCustomMenu

CCustomMenu::CCustomMenu()
{
	m_clrText =  GetSysColor (COLOR_MENUTEXT);
	m_clrBack = GetSysColor (COLOR_MENU);
	m_brBackground.CreateSolidBrush (m_clrBack);
	m_penBack.CreatePen (PS_SOLID,0,m_clrBack);
	m_crText = m_clrText;
	m_bLBtnDown = FALSE;
	m_iconX =		GetSystemMetrics ( SM_CXMENUCHECK);
	m_iconY =		GetSystemMetrics (SM_CYMENUCHECK );

	m_clrHilight = GetSysColor (COLOR_HIGHLIGHT);
	m_brSelect.CreateSolidBrush (m_clrHilight);
	m_clrHilightText = GetSysColor (COLOR_HIGHLIGHTTEXT);

	ZeroMemory ((PVOID) &m_lf,sizeof (LOGFONT));
	NONCLIENTMETRICS nm;
	nm.cbSize = sizeof (NONCLIENTMETRICS);

	//Get the system metrics for the Captionfromhere
	VERIFY (SystemParametersInfo (SPI_GETNONCLIENTMETRICS,0,&nm,0)); 

	m_lf =  nm.lfMenuFont;
	m_iMenuHeight = nm.iMenuHeight;
	m_fontMenu.CreateFontIndirect (&m_lf);
}

CCustomMenu::~CCustomMenu()
{
	if ((HBRUSH) m_brBackground != NULL)
		m_brBackground.DeleteObject ();
	if ((HFONT)m_fontMenu !=NULL)
		m_fontMenu.DeleteObject ();
	if ((HBRUSH)m_brSelect != NULL)
		 m_brSelect.DeleteObject ();
}



///////////////////////////////////////////////////////////////////////////
//
// CCustomMenu message handlers


void CCustomMenu::DrawItem (LPDRAWITEMSTRUCT lpDIS)
{
	ASSERT(lpDIS != NULL);

	CDC* pDC = CDC::FromHandle(lpDIS->hDC);
	CRect rect;
	HICON hIcon;
	COLORREF crText = m_crText;
	// draw the colored rectangle portion
	rect.CopyRect(&lpDIS->rcItem);

	// draw the up/down/focused/disabled state

	UINT action = lpDIS->itemAction;
	UINT state = lpDIS->itemState;
	CString strText;
	LOGFONT lf;
	lf = m_lf;

	CFont dispFont;
	CFont *pFont;
	//GetWindowText(strText);
	if (lpDIS->itemData != NULL)
	{
		strText = (((MENUDATA*) (lpDIS->itemData))->menuText);
		if ((((MENUDATA *)(lpDIS->itemData))->menuIconNormal) == -1)
			hIcon = NULL;
		else if (state & ODS_SELECTED)
		{
			if ((((MENUDATA *)(lpDIS->itemData))->menuIconSelected) != -1)
				hIcon = AfxGetApp ()->LoadIcon (((MENUDATA *)(lpDIS->itemData))->menuIconSelected);
			else
				hIcon = AfxGetApp()->LoadIcon (((MENUDATA*)(lpDIS->itemData))->menuIconNormal);
		}
		else
			hIcon = AfxGetApp()->LoadIcon (((MENUDATA*)(lpDIS->itemData))->menuIconNormal);

		TRACE1 ("Draw for %s\n", strText);
	}
	else
	{
		strText.Empty();
		hIcon = NULL;
	}

	if ( (state & ODS_SELECTED) )
	{
		// draw the down edges

		CPen *pOldPen = pDC->SelectObject (&m_penBack);

		//You need only Text highlight and thats what you get
		if (m_hilightStyle != Normal)
		{
			pDC->FillRect (rect,&m_brBackground);
		}
		else
		{
			pDC->FillRect (rect,&m_brSelect);
		}

		pDC->SelectObject (pOldPen);
		pDC->Draw3dRect (rect,GetSysColor (COLOR_3DHILIGHT),GetSysColor(COLOR_3DSHADOW));
		lf.lfWeight = FW_BOLD;
		if ((HFONT)dispFont != NULL)
			dispFont.DeleteObject ();
		dispFont.CreateFontIndirect (&lf);
		crText = m_clrHilightText;

		//While selected move the text a bit
		TRACE0 ("SELECT,SELECTED\n");
	}
	else
	{
		CPen *pOldPen = pDC->SelectObject (&m_penBack);
		pDC->FillRect (rect,&m_brBackground);
		pDC->SelectObject (pOldPen);
		// draw the up edges
		pDC->Draw3dRect (rect,m_clrBack,m_clrBack);
		if ((HFONT)dispFont != NULL)
			dispFont.DeleteObject ();
		dispFont.CreateFontIndirect (&lf); //Normal

		TRACE0 ("SELECT, NORMAL\n");
	}

	// draw the text if there is any
	//We have to paint the text only if the image is nonexistant
	if (hIcon != NULL)
	{
		if(DrawIconEx (pDC->GetSafeHdc(),rect.left,rect.top,hIcon,
			(m_iconX)?m_iconX:32,(m_iconY)?m_iconY:32,0,NULL,DI_NORMAL))
			TRACE0("Wrote the icon successfully\n");
		else
			TRACE0 ("SORRY.NOGO\n");
	}
	//This is needed always so that we can have the space for check marks
	rect.left = rect.left +((m_iconX)?m_iconX:32); 

	if ( !strText.IsEmpty())
	{
		//		pFont->GetLogFont (&lf);

		int		  iOldMode = pDC->GetBkMode();

		pDC->SetBkMode( TRANSPARENT);

		pDC->SetTextColor( crText);

		pFont = pDC->SelectObject (&dispFont);
		TRACE1( "About To DrawText %s\n",strText);
		pDC->DrawText (strText,rect,DT_LEFT|DT_SINGLELINE|DT_VCENTER);
		TRACE0("Done\n");
		pDC->SetBkMode( iOldMode );
		pDC->SelectObject (pFont); //set it to the old font
	}
	dispFont.DeleteObject ();
}

void CCustomMenu::MeasureItem( LPMEASUREITEMSTRUCT lpMIS )
{
	CDC *pDC = AfxGetApp()->m_pMainWnd->GetDC();
	CFont* pFont = pDC->SelectObject (&m_fontMenu);
	int iconX = 0,iconY= 0;
	TEXTMETRIC tm;
	pDC->GetTextMetrics (&tm);
	pDC->SelectObject (pFont);
	AfxGetApp()->m_pMainWnd->ReleaseDC (pDC);

	if (m_iconX)
		iconX = m_iconX;
	if (m_iconY)
		iconY = m_iconY;

	lpMIS->itemWidth = iconX + tm.tmAveCharWidth *  lstrlen(((MENUDATA*)(lpMIS->itemData))->menuText) +10;
	lpMIS->itemHeight = (iconY > (m_iMenuHeight+1))?iconY:m_iMenuHeight + 1;
}

void CCustomMenu::SetIconSize (int width, int height)
{
	m_iconX = width;
	m_iconY = height;
}

void CCustomMenu::SetTextColor (COLORREF clrText)
{
	m_crText = clrText;
}

void CCustomMenu::SetBackColor (COLORREF clrBack)
{
	m_clrBack = clrBack;
	if ((HBRUSH)m_brBackground != NULL)
		m_brBackground.DeleteObject ();
	m_brBackground.CreateSolidBrush (clrBack);
}

void CCustomMenu::SetHighlightColor (COLORREF clrHilight)
{
	m_clrHilight = clrHilight;
	if ((HBRUSH)m_brSelect != NULL)
			m_brSelect.DeleteObject ();
	m_brSelect.CreateSolidBrush (clrHilight);
}

void CCustomMenu::SetHighlightTextColor (COLORREF clrHilightText)
{
	m_clrHilightText = clrHilightText;
}


void CCustomMenu::SetHighlightStyle (HIGHLIGHTSTYLE hilightStyle)
{
	m_hilightStyle = hilightStyle;
}
//*************************************************************************



Comments

  • Small bug fix .........

    Posted by Legacy on 01/15/2001 12:00am

    Originally posted by: Michael Kuelshammer

    Works great, there is only a small bug, that I recognized, when I implemented your owner drawn menu:
    
    

    it must be:

    lstrcpy (menuData[0].menuText , "text1");
    menuData[0].menuIconNormal= IDI_ICON1;
    m_customMenu.AppendMenu (MF_OWNERDRAW,3,(LPCTSTR)&menuData[0]);

    Bye,
    Michael Kuelshammer

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

Top White Papers and Webcasts

  • 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 …

  • If you need new tools and tricks to make your meetings profitable and productive, then 5 Tips in 5 Minutes: A Quick Guide for More Profitable Sales Meetings is for you. Timely, practical tips that you can incorporate in just seconds will save you literally hours in travel and meeting time, not to mention help you to focus on what your sales prospects really want to know and how you can meet their needs. Get 5in5: A Quick Guide for More Profitable Sales Meetings and start building your sales the smarter, faster …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds