A Flat Popup Menu for Controls

CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

CFlatPopupMenu

One thing has always bothered me about web-style controls that pop up menus,
and that’s the way that the menus don’t look as if they are owned by the
control. The default Windows menus with their 3D look always look like they
belong to the browser and not to the control, hence I wrote this popup menu
replacement that looks a lot better when owned by a control.

The class doesn’t actually use any MFC so you’re OK to go ahead and use it in
ATL projects designed for web-based distribution. It also supports keyboard
shortcuts and the bitmaps seen in recent Microsoft Office applications. All the
colors are independently configurable at run-time so that you can make it fit in
with your control’s look and feel.

How to use it

  1. I use the STL implementations of string and vector so you’ll have to
    ensure that you add #include <string> and
    #include <vector> to your stdafx.h
    file.
  2. Declare as many instances of CFlatPopupMenu
    as you require. You will need one for each menu and submenu.
  3. Call any SetXXX functions that you need in
    order to change the font, colors etc.
  4. Call Create() once for each menu and submenu
    object.
  5. Call AppendItem() and AppendPopup()
    as many times as necessary for your menu.
  6. Call Track() to display the menu.

OK, here’s a breakdown of the class members you should know about as they
stand today.

void SetFont(LPCTSTR pszFont)

Sets the font used in the menus. The default font is "MS Sans
Serif"

void SetFontSize(const int size)

Sets the size of the font (in points) used in the menus. The default font
size is 8 points. At present you should ensure that the font size does not
exceed 17 pixels.

void SetPopupDelay(const int delay)

Sets the delay, in milliseconds, between a mouse entering a menu item that
has a submenu attached, and that submenu being displayed. The user can always
bypass the delay by clicking with a mouse button, just like the standard menus.
The default value is 400ms.

void SetColor(const menuColor id,const COLORREF cr)

Sets one of the colors used in displaying the menu. The cr
parameter specifies the color and the id parameter
specifies one from the menuColor enumeration as
listed below.

colorBorder Border color. Also used as the separator color
colorBackground Background area color
colorText Unselected menu item text color
colorGrayedText Grayed (disabled) menu item text color
colorHighlightText Highlighted (selected) menu item text color
colorHighlight Highlighted item bar (background) color
colorLightShadow Used to draw top and left 3D effect around selected icons
colorDarkShadow Used to draw bottom and right 3D effect around selected
icons
colorIconTransparent Background color of icons that you can see through.

bool Create(HINSTANCE hInstance,const UINT bitmap_id=(UINT)-1)

Called to create, but not display the menu. You must call this after calling
the formatting functions and before tracking the menu. MFC users will get the hInstance
parameter by calling AfxGetInstanceHandle(), ATL
users will get it by accessing _Module.m_hInst. The
bitmap_id parameter specifies the resource
identifier of the bitmap that the menu will load its icons from, the default
value of -1 means that the menu does not contain
icons. The bitmap consists of a strip of 16×15 icons (as used for toolbar
buttons) indexed from the left starting at zero, as illustrated by the snapshot
below which was taken from the bitmap editor in Developer Studio. The bright
pink background color is used as the transparent color in the menu.

The function returns true or false
to indicate success or failure.

bool AppendItem(const DWORD dwFlags,LPCTSTR pszName,const UINT itemid,const int icon=-1)

Call this function to add a new item to the end of the menu. dwFlags
contains any of the following flags combined with a bitwise OR.

itemSeparator Item is a separator. Should not be combined with any other
flags
itemNotSelectable Item is not selectable by the user.
itemGrayed Item is displayed in grayed text. Usually combined with itemNotSelectable.
itemBold Item is displayed using a bold font. Good for non-selectable
titles.

pszName contains the text to display as the menu
item, separators can pass NULL for this parameter.
To include a keyboard shortcut for an item, prefix the shortcut character with
an ampersand (&). itemid holds a non-zero
identifier that is returned when the item is selected by the user.
Non-selectable items and separators can pass zero for this parameter. icon
holds the zero-based index of the icon image in the bitmap strip passed to the Create()
function or -1 if there is no icon associated with
this item.

The function returns true or false
to indicate success or failure.

bool AppendPopup(const DWORD dwFlags,LPCTSTR pszName,CFlatPopupMenu&
popup,const int icon=-1)

Call this function to add a new popup submenu to the end of a menu. dwFlags,
pszName and icon are the same as in AppendItem()
but you should not set the separator flag. popup is
the actual submenu object that you want to associate with this menu item. This
function does not take ownership of popup which
means that if you allocated it with new then you
should delete it.

The function returns true or false
to indicate success or failure.

UINT Track(int x,int y,HWND hWnd,const bool bModal,const bool bPopup=false)

Call this function to display the menu and track the user selection. x
and y define the position of the top-left corner of
the menu in screen co-ordinates. if bModal is true
then hWnd is NULL, otherwise it is the window
handle to post the WM_COMMAND message of the
selected item to. bPopup is an internal parameter
and should never be set by the user, always use the default value of false.

If bModal is true then function returns the item
ID of the selected menu item, or zero if no item was selected. If bModal
is false then the function returns zero.

An Example

This little code snippet demonstrates how simple it is to create and track a
menu:

CFlatPopupMenu menu;
CPoint p;
int id;

// set some of the colors

menu.SetColor(CFlatPopupMenu::colorBorder,m_crText);
menu.SetColor(CFlatPopupMenu::colorText,m_crText);
menu.SetColor(CFlatPopupMenu::colorBackground,m_crBack);

// create it

menu.Create(AfxGetInstanceHandle(),IDB_FILE);

// add some items

menu.AppendItem(0,"&New...",1,0);
menu.AppendItem(0,"&Open...",2,1);
menu.AppendItem(0,"Close",3);
menu.AppendItem(CFlatPopupMenu::itemSeparator,NULL,0);
menu.AppendItem(0,"&Save",4,2);
menu.AppendItem(0,"Save &As...",5);
menu.AppendItem(0,"Save A&ll",6,3);
menu.AppendItem(CFlatPopupMenu::itemSeparator,NULL,0);
menu.AppendItem(0,"Page Set&up",7);
menu.AppendItem(0,"&Print...",8,4);

// track it

p.x=m_rcFile.left;
p.y=m_rcFile.bottom-1;
ClientToScreen(&p);

id=menu.Track(p.x,p.y,NULL,true);

if(id>0)
	// do something with 'id'
else
	// user cancelled the menu

Downloads

Download demo project – 48 Kb
Download source – 10 Kb

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read