Update for Owner Drawn Menu with Icons (2)

This menu is another modification of owner draw menus (#2 if you want to know). I have corrected an error I found (use of uninitialized variable in DrawItem), change item measurement (now the height is the bigger from icon height or menu height) and changed the menu concept from using separate icons for each item to use of image list (CImageList) so all the icons are in one bitmap. For more information see original article by Ben Ashley

What's new

  • Icon size is not determined every time it's needed but is stored in member variables
  • Fixed few unnecesary lines of code

What to use them ?

To be able to use owner draw popup menu items it is neccesary to add following to the main window class:
In the message map:
ON_WM_MEASUREITEM()
In the declaration (header) file:
afx_msg void OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct);
And in the definition (source) file:

static CMenu* FindPopupMenuFromID(CMenu* pMenu, UINT nID)
{
	ASSERT_VALID(pMenu);
	// walk through all items, looking for ID match
	UINT nItems = pMenu->GetMenuItemCount();
	for (int iItem = 0; iItem < (int)nItems; iItem++)
	{
		CMenu* pPopup = pMenu->GetSubMenu(iItem);
		if (pPopup != NULL)
		{
			// recurse to child popup
			pPopup = FindPopupMenuFromID(pPopup, nID);
			// check popups on this popup
			if (pPopup != NULL)
				return pPopup;
		}
		else if (pMenu->GetMenuItemID(iItem) == nID || (UINT)pMenu->GetSafeHmenu() == nID)
		{
			// it is a normal item inside our popup
			pMenu = CMenu::FromHandlePermanent(pMenu->m_hMenu);
			return pMenu;
		}
	}
	// not found
	return NULL;
}

void CMainWnd::OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
	if (lpMeasureItemStruct->CtlType == ODT_MENU)
	{
		ASSERT(lpMeasureItemStruct->CtlID == 0);
		CMenu* pMenu;

		_AFX_THREAD_STATE* pThreadState = AfxGetThreadState();
		if (pThreadState->m_hTrackingWindow == m_hWnd)
		{
			// start from popup
			pMenu = CMenu::FromHandle(pThreadState->m_hTrackingMenu);
		}
		else
		{
			// start from menubar
			pMenu = GetMenu();
		}

		pMenu = FindPopupMenuFromID(pMenu, lpMeasureItemStruct->itemID);
		if (pMenu != NULL)
			pMenu->MeasureItem(lpMeasureItemStruct);
		else
			TRACE1("Warning: unknown WM_MEASUREITEM for menu item 0x%04X.\n", lpMeasureItemStruct->itemID);
	}
	else
	{
		CWnd* pChild = GetDescendantWindow(lpMeasureItemStruct->CtlID, TRUE);
		if (pChild != NULL && pChild->SendChildNotifyLastMsg())
			return;     // eaten by child
	}
	// not handled - do default
	Default();
}

Download code 5K

Last updated on: August 27, 1998.



Comments

  • good

    Posted by xxl19792003 on 08/24/2010 03:33am

    good

    Reply
  • Thanks to Jaroslav Pisk !

    Posted by Legacy on 01/29/2004 12:00am

    Originally posted by: RyuSungMin

    Modified FindMenuItem returns pointer of submenu and uId
    CMenu* CCatDDEMgr::FindMenuItem(CMenu* pMenu, CString MenuString, UINT& uId)
    {
    ASSERT_VALID(pMenu);
    int cnt = pMenu->GetMenuItemCount();
    for(int i = 0; i < cnt; i++)
    {
    CString str;
    CMenu* pPopup = pMenu->GetSubMenu(i);
    if(pPopup)
    {
    if (pPopup->GetMenuString(i, str, MF_BYPOSITION) &&
    (strcmp(str, MenuString) == 0))
    {
    uId = pPopup->GetMenuItemID(i);
    return pPopup;
    }
    else
    {
    pPopup = FindMenuItem(pPopup, MenuString, uId);
    if(pPopup)
    {
    return pPopup;
    }
    }
    }
    else
    if (pMenu->GetMenuString(i, str, MF_BYPOSITION) &&
    (strcmp(str, MenuString) == 0))
    {
    uId = pMenu->GetMenuItemID(i);
    return pMenu;
    }
    }
    return NULL;
    }

    Reply
  • One logical error

    Posted by Legacy on 09/12/2001 12:00am

    Originally posted by: Paul Nurminsky

    Your code contain a logical error
    
    if the item with nID is a submenu
    FindPopupMenuFromID will return the submenu instead of menu.
    To fix this problem you should :
    static CMenu* FindPopupMenuFromID(CMenu* pMenu, UINT nID)
    {
    ASSERT_VALID(pMenu);
    // walk through all items, looking for ID match
    UINT nItems = pMenu->GetMenuItemCount();
    for (int iItem = 0; iItem < (int)nItems; iItem++)
    {
    CMenu* pPopup = pMenu->GetSubMenu(iItem);
    if (pPopup != NULL)
    {
    if((UINT)pPopup->GetSafeHmenu() == nID )
    {
    //!!! return our menu
    return pMenu;
    }
    // recurse to child popup
    pPopup = FindPopupMenuFromID(pPopup, nID);
    // check popups on this popup
    if (pPopup != NULL)
    return pPopup;
    }
    else
    if (pMenu->GetMenuItemID(iItem) == nID )
    {
    // it is a normal item inside our popup
    pMenu = CMenu::FromHandlePermanent(pMenu->m_hMenu);
    return pMenu;
    }
    }
    // not found
    return NULL;
    }

    Reply
  • Update to CMenuSpawn

    Posted by Legacy on 11/02/1998 12:00am

    Originally posted by: Alex Waddell

    The Menu Spawn class currently only supports checked menu items if there is no bitmap associated with the menu item. Add this line to the Draw() routine to enable "checked" menu items for items that have a bitmap.

    Add this line in to the Draw() and you can get "checked" items with the bitmap (instead of just checked items without bitmaps).

    if (pItem->iImageIdx >= 0)
    {
    int ay = (rcImage.Height() - szImage.cy) / 2;
    int ax = (rcImage.Width() - szImage.cx) / 2;
    >> if (bChecked)
    >> pDC->DrawEdge (rcImage, BDR_SUNKENOUTER, BF_RECT);
    >> else if (bSelect && bEnab)
    pDC->Draw3dRect(rcImage,cr3dHilight,cr3dShadow);
    else
    if (!bBackBitmap) pDC->Draw3dRect(rcImage,crMenu,crMenu);

    if (bEnab)
    {
    ...
    }

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

Top White Papers and Webcasts

  • Today's agile organizations pose operations teams with a tremendous challenge: to deploy new releases to production immediately after development and testing is completed. To ensure that applications are deployed successfully, an automatic and transparent process is required. We refer to this process as Zero Touch Deployment™. This white paper reviews two approaches to Zero Touch Deployment--a script-based solution and a release automation platform. The article discusses how each can solve the key …

  • Learn How A Global Entertainment Company Saw a 448% ROI Every business today uses software to manage systems, deliver products, and empower employees to do their jobs. But software inevitably breaks, and when it does, businesses lose money -- in the form of dissatisfied customers, missed SLAs or lost productivity. PagerDuty, an operations performance platform, solves this problem by helping operations engineers and developers more effectively manage and resolve incidents across a company's global operations. …

Most Popular Programming Stories

More for Developers

RSS Feeds