Creating Popup Menus with Titles

This article was contributed by Mark E. Pennell.


Introduction

Popups, popups everywhere!  Have you noticed that we now live in an age of the endless collection of popup menus?  In practically every modern application, there exists several UI objects which have their own popup menus.  One must admit that these "context" menus have provided a needed boost in working with applications with 'heavy' UIs.  However, there are times where the user of your application may be a little confused about to which 'item' the invoked popup menu refers.

Let's give an example, however contrived it may be. Imagine using a home architecting product in which you can click items in your room layout to invoke context menus. When you click on the chair in the living room, you get the context menu to the right. Now, you select the 'Remove' command.  Oops!  Your carefully designed Persian rug disappears from under the chair you thought you had selected.    menu1.gif (2879 bytes)

Okay ... maybe not the best example I could have found, but hopefully you are both laughing (not just at me) and understanding that there are cases where a slightly more user friendly approach to context menus might be useful.  So what might this be?

Titled Popup Menus

There has been a recent UI trend involving the addition of titles to popup menus.   These titles are used to convey the exact object to which the context applies.   These are also being used to prompt the user with clear information on why they should make a menu selection.  As an example of the latter, you may have seen pulldown toolbar buttons where you can also select the 'default' action to occur when the toolbar button is left clicked.  It is common to see the popup menu for the button start with the title "Set Default" appearing in bold as the first item in the menu.  This item is not selectable, but is a visual indicator better describing the use/reason for the menu.

So to finish our example above, to the right is a titled context menu which might have better helped our user.   menu2.gif (3813 bytes)

Disclaimer for UI purists - This article is not meant to give an opinion on whether or not this is good UI style, but rather to provide information on implementing this feature to those who are interested.  If you wish to add titled popup menus to your product, I hope this code helps you accomplish your goal.

Method 1 - SetMenuDefaultItem(...)

The Win32 API provides a single function which will "bold" a menu item to indicate that it is the default menu item. You can disable the menu item as well to meet most of our goals above. The item will still invert as you move the mouse over it, which is not as desireable as our example/description above.


//
// This function adds a title entry to a popup menu
//
void AddMenuTitle(CMenu* popup, LPCSTR title)
{
    // insert a separator item at the top
    popup->InsertMenu(0, MF_BYPOSITION | MF_SEPARATOR, 0, title);

    // insert title item
    // note: item is not selectable (disabled) but not grayed
    popup->InsertMenu(0, MF_BYPOSITION | MF_STRING | MF_DISABLED, 0, title);

    SetMenuDefaultItem(popup->m_hMenu, 0, TRUE);
}

Method 2 - Owner Drawn Item

This method is more involved, but it gives you full control over the display of the titled item. With this method, moving the mouse over the item does not cause it to highlight as in Method 1.

This method also doubles as a good tutorial on owner drawn menu items in general.

Utilization Example Code

The following steps can be used to create titled popup menus in your application utilizing the supplied implementation code for Method 2:

  1. Add the supplied helper code and header to your project
  2. Add OnMeasureItem and OnDrawItem to the window handling the popup menu
  3. Call the helper functions to handle the measuring and drawing of your popup menu title item
  4. Setup the popup menu to have a title just before displaying (tracking) the popup menu

The following is a code snippet from a simple dialog class which added the above example titled popup menu:


void CExampleDlg::OnContextMenu(CWnd* pWnd, CPoint point) {    // load the popup menu to display    CMenu menu;    menu.LoadMenu(idrMenu1);    CMenu* popup = menu.GetSubMenu(0);    // call the helper function to setup this as a titled popup menu    AddMenuTitle(popup);    // display the popup menu    popup->TrackPopupMenu(TPM_LEFTALIGN, point.x, point.y, this); } void CExampleDlg::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct) {    if (ODT_MENU == lpDrawItemStruct->CtlType)    {       // call our helper function to draw the item title       DrawPopupMenuTitle(lpDrawItemStruct, "Persian Rug");    }    else    {       CDialog::OnDrawItem(nIDCtl, lpDrawItemStruct);    } } void CExampleDlg::OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct) {    if (ODT_MENU == lpMeasureItemStruct->CtlType)    {       // call our helper function to measure the item title       MeasurePopupMenuTitle(lpMeasureItemStruct, "Persian Rug");    }    else    {       CDialog::OnMeasureItem(nIDCtl, lpMeasureItemStruct);    } }

The header file.... (TitledMenu.h)...


#ifndef __TitledMenu_h #define __TitledMenu_h void AddMenuTitle(CMenu* popup); void MeasurePopupMenuTitle(LPMEASUREITEMSTRUCT mi, LPCSTR menuTitle); void DrawPopupMenuTitle(LPDRAWITEMSTRUCT di, LPCSTR menuTitle); #endif

The Implementation File (TitledMenu.cpp)


#include "stdafx.h" #include "TitledMenu.h" // // This function adds a title entry to a popup menu // void AddMenuTitle(CMenu* popup) {     // insert a separator item at the top     popup->InsertMenu(0, MF_BYPOSITION | MF_SEPARATOR, 0, (LPSTR)0);     // insert an empty owner-draw item at top to serve as the title     // note: item is not selectable (disabled) but not grayed     popup->InsertMenu(0, MF_BYPOSITION | MF_OWNERDRAW | MF_STRING | MF_DISABLED, 0, (LPSTR)0); } // // This function creates the bold font for the popup menu's title // static HFONT CreatePopupMenuTitleFont() {     // start by getting the stock menu font     HFONT font = (HFONT)GetStockObject(ANSI_VAR_FONT);     if (font)     {         // now, get the complete LOGFONT describing this font         LOGFONT lf;         if (GetObject(font, sizeof(LOGFONT), &lf))         {             // set the weight to bold             lf.lfWeight = FW_BOLD;             // recreate this font with just the weight changed             font = CreateFontIndirect(&lf);         }     }     // return the new font - Note: Caller now owns this GDI object     return font; } // // This is a helper function to measure the popup menu's title item // void MeasurePopupMenuTitle(LPMEASUREITEMSTRUCT mi, LPCSTR menuTitle) {     // create the font we will use for the title     HFONT font = CreatePopupMenuTitleFont();     if (font)     {         // get the screen dc to use for retrieving size information         CDC dc;         dc.Attach(::GetDC(NULL));         // select the title font         CFont* old = (CFont*)dc.SelectObject(CFont::FromHandle(font));         // compute the size of the title         CSize size = dc.GetTextExtent(menuTitle);         // deselect the title font         dc.SelectObject(old);         // delete the title font         DeleteObject(font);         // add in the left margin for the menu item         size.cx += GetSystemMetrics(SM_CXMENUCHECK)+8;         // return the width and height         mi->itemWidth = size.cx;         mi->itemHeight = size.cy;     } } // // This is a helper function to draw the popup menu's title item // void DrawPopupMenuTitle(LPDRAWITEMSTRUCT di, LPCSTR menuTitle) {     // create the font we will use for the title     HFONT font = CreatePopupMenuTitleFont();     if (font)     {         // create the background menu brush         HBRUSH bgb = CreateSolidBrush(GetSysColor(COLOR_MENU));         // fill the rectangle with this brush         FillRect(di->hDC, &di->rcItem, bgb);         // delete the brush         DeleteObject(bgb);         // set text mode to transparent         int mode = SetBkMode(di->hDC, TRANSPARENT);         // set text color to menu text color         COLORREF color = SetTextColor(di->hDC, GetSysColor(COLOR_MENUTEXT));         // select this font into the dc         HFONT old = (HFONT)SelectObject(di->hDC, font);         // add the menu margin offset         di->rcItem.left += GetSystemMetrics(SM_CXMENUCHECK)+8;         // draw the text left aligned and vertically centered         DrawText(di->hDC, menuTitle, -1, &di->rcItem, DT_SINGLELINE|DT_VCENTER|DT_LEFT);         // deselect the font         SelectObject(di->hDC, old);         // restore the text background mode         SetBkMode(di->hDC, mode);         // restore the text color         SetTextColor(di->hDC, color);     } }

About the Author

Mark E. Pennell is a Senior Engineer for Visioneer, Inc. of Fremont, CA., and a former Windows Programming instructor for the University of California - Santa Cruz Extension.  Mark telecommutes to work from his home in Austin, TX.

Last updated: 13 May 1998



Comments

  • Software Engineer

    Posted by Manish on 05/08/2012 11:05pm

    Hi, Thanks In Advance !!! I wanted to create a dynamic popup menu and then wanted to see the index of each menu. For example if i have following dynamic popup menu: popup1 popup2 popup3 Now as you see above three menus are there, and now I wanted that if I select popup1 it should return 1 or if I select popup2 it should return 2 or if I select popup3 it should return 3. Basically i need to know that which menu selected 1st,second or third. Please suggest.

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

Top White Papers and Webcasts

  • Live Event Date: December 11, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT Market pressures to move more quickly and develop innovative applications are forcing organizations to rethink how they develop and release applications. The combination of public clouds and physical back-end infrastructures are a means to get applications out faster. However, these hybrid solutions complicate DevOps adoption, with application delivery pipelines that span across complex hybrid cloud and non-cloud environments. Check out this …

  • On-demand Event Event Date: October 29, 2014 It's well understood how critical version control is for code. However, its importance to DevOps isn't always recognized. The 2014 DevOps Survey of Practice shows that one of the key predictors of DevOps success is putting all production environment artifacts into version control. In this webcast, Gene Kim discusses these survey findings and shares woeful tales of artifact management gone wrong! Gene also shares examples of how high-performing DevOps …

Most Popular Programming Stories

More for Developers

RSS Feeds