Creating Popup Menus with Titles | CodeGuru

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 […]

Written By
CodeGuru Staff
CodeGuru Staff
Aug 6, 1998
3 minute read
CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More

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.

Advertisement

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);
   }
}

Advertisement

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

CodeGuru Logo

CodeGuru covers topics related to Microsoft-related software development, mobile development, database management, and web application programming. In addition to tutorials and how-tos that teach programmers how to code in Microsoft-related languages and frameworks like C# and .Net, we also publish articles on software development tools, the latest in developer news, and advice for project managers. Cloud services such as Microsoft Azure and database options including SQL Server and MSSQL are also frequently covered.

Property of TechnologyAdvice. © 2026 TechnologyAdvice. All Rights Reserved

Advertiser Disclosure: Some of the products that appear on this site are from companies from which TechnologyAdvice receives compensation. This compensation may impact how and where products appear on this site including, for example, the order in which they appear. TechnologyAdvice does not include all companies or all types of products available in the marketplace.