Home   Index   About
Ultimate Pack


Custom Search
Example of Owner-Drawn Menu Items

The example in this topic uses owner-drawn menu items in a menu. The menu items select specific font attributes, and the application displays each menu item using a font that has the corresponding attribute. For example, the Italic menu item is displayed in an italic font. The Character menu name on the menu bar opens the menu.

The menu bar and drop-down menu are defined initially by an extended menu-template resource. Because a menu template cannot specify owner-drawn items, the menu initially contains four text menu items with the following strings: "Regular," "Bold," "Italic," and "Underline." The application's window procedure changes these to owner-drawn items when it processes the WM_CREATE message. When it receives the WM_CREATE message, the window procedure calls the application-defined OnCreate function, which performs the following steps for each menu item:

  1. Allocates an application-defined MYITEM structure.

  2. Gets the text of the menu item and saves it in the application-defined MYITEM structure.

  3. Creates the font used to display the menu item and saves its handle in the application-defined MYITEM structure.

  4. Changes the menu item type to MFT_OWNERDRAW and saves a pointer to the application-defined MYITEM structure as item data.

Because a pointer to each application-defined MYITEM structure is saved as item data, it is passed to the window procedure in conjunction with the WM_MEASUREITEM and WM_DRAWITEM messages for the corresponding menu item. The pointer is contained in the itemData member of both the MEASUREITEMSTRUCT and DRAWITEMSTRUCT structures.

A WM_MEASUREITEM message is sent for each owner-drawn menu item the first time it is displayed. The application processes this message by selecting the font for the menu item into a device context and then determining the space required to display the menu item text in that font. The font and menu item text are both specified by the menu item's MYITEM structure (the structure defined by the application). The application determines the size of the text by using the GetTextExtentPoint32 function.

The window procedure processes the WM_DRAWITEM message by displaying the menu item text in the appropriate font. The font and menu item text are both specified by the menu item's MYITEM structure. The application selects text and background colors appropriate to the menu item's state.

The window procedure processes the WM_DESTROY message to destroy fonts and free memory. The application deletes the font and frees the application-defined MYITEM structure for each menu item.

Following are the relevant portions of the application's header file.

// Menu-item identifiers for the Character menu

#define IDM_CHARACTER 10

#define IDM_REGULAR 11

#define IDM_BOLD 12

#define IDM_ITALIC 13

#define IDM_UNDERLINE 14

// Structure associated with menu items

typedef struct tagMYITEM {

HFONT hfont;

int cchItemText;

char szItemText[1];

} MYITEM;

#define CCH_MAXITEMTEXT 256

Following are the relevant portions of the application's window procedure and its associated functions.

LRESULT CALLBACK MainWindowProc(

HWND hwnd,

UINT uMsg,

WPARAM wParam,

LPARAM lParam

)

{

switch (uMsg) {

case WM_CREATE:

if (!OnCreate(hwnd))

return -1;

break;

case WM_DESTROY:

OnDestroy(hwnd);

PostQuitMessage(0);

break;

case WM_MEASUREITEM:

OnMeasureItem(hwnd, (LPMEASUREITEMSTRUCT) lParam);

return TRUE;

case WM_DRAWITEM:

OnDrawItem(hwnd, (LPDRAWITEMSTRUCT) lParam);

return TRUE;

.

. // Additional message processing goes here.

.

default:

return DefWindowProc(hwnd, uMsg, wParam, lParam);

}

return 0;

}

BOOL WINAPI OnCreate(HWND hwnd)

{

HMENU hmenuBar = GetMenu(hwnd);

HMENU hmenuPopup;

MENUITEMINFO mii;

UINT uID;

MYITEM *pMyItem;

// Get the handle of the pop-up menu.

mii.fMask = MIIM_SUBMENU; // information to get

GetMenuItemInfo(hmenuBar, IDM_CHARACTER, FALSE, &mii);

hmenuPopup = mii.hSubMenu;

// Modify each menu item. Assume that the IDs IDM_REGULAR

// through IDM_UNDERLINE are consecutive numbers.

for (uID = IDM_REGULAR; uID <= IDM_UNDERLINE; uID++) {

// Allocate an item structure, leaving space for a

// string of up to CCH_MAXITEMTEXT characters.

pMyItem = (MYITEM *) LocalAlloc(LMEM_FIXED,

sizeof(MYITEM) + CCH_MAXITEMTEXT);

// Save the item text in the item structure.

mii.fMask = MIIM_TYPE;

mii.dwTypeData = pMyItem->szItemText;

mii.cch = CCH_MAXITEMTEXT;

GetMenuItemInfo(hmenuPopup, uID, FALSE, &mii);

pMyItem->cchItemText = mii.cch;

// Reallocate the structure to the minimum required size.

pMyItem = (MYITEM *) LocalReAlloc(pMyItem,

sizeof(MYITEM) + mii.cch, LMEM_MOVEABLE);

// Create the font used to draw the item.

pMyItem->hfont = CreateMenuItemFont(uID);

// Change the item to an owner-drawn item, and save

// the address of the item structure as item data.

mii.fMask = MIIM_TYPE | MIIM_DATA;

mii.fType = MFT_OWNERDRAW;

mii.dwItemData = (DWORD) pMyItem;

SetMenuItemInfo(hmenuPopup, uID, FALSE, &mii);

}

return TRUE;

}

HFONT CreateMenuItemFont(UINT uID)

{

LOGFONT lf;

ZeroMemory(&lf, sizeof(lf));

lf.lfHeight = 20;

lstrcpy(lf.lfFaceName, "Times New Roman");

switch (uID) {

case IDM_BOLD:

lf.lfWeight = FW_HEAVY;

break;

case IDM_ITALIC:

lf.lfItalic = TRUE;

break;

case IDM_UNDERLINE:

lf.lfUnderline = TRUE;

break;

}

return CreateFontIndirect(&lf);

}

VOID WINAPI OnDestroy(HWND hwnd)

{

HMENU hmenuBar = GetMenu(hwnd);

HMENU hmenuPopup;

MENUITEMINFO mii;

UINT uID;

MYITEM *pMyItem;

// Get the handle of the menu.

mii.fMask = MIIM_SUBMENU; // information to get

GetMenuItemInfo(hmenuBar, IDM_CHARACTER, FALSE, &mii);

hmenuPopup = mii.hSubMenu;

// Free resources associated with each menu item.

for (uID = IDM_REGULAR; uID <= IDM_UNDERLINE; uID++) {

// Get the item data.

mii.fMask = MIIM_DATA;

GetMenuItemInfo(hmenuPopup, uID, FALSE, &mii);

pMyItem = (MYITEM *) mii.dwItemData;

// Destroy the font and free the item structure.

DeleteObject(pMyItem->hfont);

LocalFree(pMyItem);

}

}

VOID WINAPI OnMeasureItem(HWND hwnd, LPMEASUREITEMSTRUCT lpmis)

{

MYITEM *pMyItem = (MYITEM *) lpmis->itemData;

HDC hdc = GetDC(hwnd);

HFONT hfntOld = SelectObject(hdc, pMyItem->hfont);

SIZE size;

GetTextExtentPoint32(hdc, pMyItem->szItemText,

pMyItem->cchItemText, &size);

lpmis->itemWidth = size.cx;

lpmis->itemHeight = size.cy;

SelectObject(hdc, hfntOld);

ReleaseDC(hwnd, hdc);

}

VOID WINAPI OnDrawItem(HWND hwnd, LPDRAWITEMSTRUCT lpdis)

{

MYITEM *pMyItem = (MYITEM *) lpdis->itemData;

COLORREF clrPrevText, clrPrevBkgnd;

HFONT hfntPrev;

int x, y;

// Set the appropriate foreground and background colors.

if (lpdis->itemState & ODS_SELECTED) {

clrPrevText = SetTextColor(lpdis->hDC,

GetSysColor(COLOR_HIGHLIGHTTEXT));

clrPrevBkgnd = SetBkColor(lpdis->hDC,

GetSysColor(COLOR_HIGHLIGHT));

}

else {

clrPrevText = SetTextColor(lpdis->hDC,

GetSysColor(COLOR_MENUTEXT));

clrPrevBkgnd = SetBkColor(lpdis->hDC,

GetSysColor(COLOR_MENU));

}

// Determine where to draw and leave space for a check-mark.

x = lpdis->rcItem.left;

y = lpdis->rcItem.top;

x += LOWORD(GetMenuCheckMarkDimensions());

// Select the font and draw the text.

hfntPrev = SelectObject(lpdis->hDC, pMyItem->hfont);

ExtTextOut(lpdis->hDC, x, y, ETO_OPAQUE,

&lpdis->rcItem, pMyItem->szItemText,

pMyItem->cchItemText, NULL);

// Restore the original font and colors.

SelectObject(lpdis->hDC, hfntPrev);

SetTextColor(lpdis->hDC, clrPrevText);

SetBkColor(lpdis->hDC, clrPrevBkgnd);

}


Last news from Greatis Software

Nostalgia .Net     Nostalgia .Net     .Net is powerful, but not all-powerful, so sometimes we need to use Win32 API for our .Net applications. It's simple enough with Platform Invoke if you have Win32 skill, but we do not always have time to dig the ancient documentation, declare the special types that are compatible with Win32, find the values of the Win32's constants and so on. Nostalgia .Net offers several simple-to-use classes, and components that will allow you to forget about the headache of Win32 and just use the power of Win32 in your application the same way as you use the native. Net classes.  More »

Recommended software for developers

Ultimate Pack for Delphi and C++ Builder     Ultimate Pack     Component pack for Delphi and C++ Builder that contains runtime form designer, runtime object inspector, print suite and much more for the very special price.  More »

Form Designer .Net     Form Designer .Net     Unique runtime form design solution that allows to edit any form in .Net WinForms application at runtime with full source codes for only 300 euro!  More »

Print Suite .Net     Print Suite .Net     Print Suite .Net is a set of components for easy printing texts, images and grids from your WinForms applications. Full C# source codes are available  More »

Gradient Controls .Net     Gradient Controls .Net     Gradient Controls .Net offers controls with gradient background feature. Labels, panels and so on... Full C# source codes are available  More »

iGrid     Greatis iGrid     iGrid plots drawing grid right over your desktop, so you can use it everywhere, with any drawing application without any special plugins for different graphic editors.  More »


All the contacts and projects

Dmitry Vasiliev (just.dmitry)

Related Links

Software for Visual Studio .NET developers
Software for Delphi and C++ Builder developers
Software for Visual Basic 6 developers
Delphi Tips&Tricks
MegaDetailed.NET

More Online Helps

Win32 Programmer's Reference
Win32 Multimedia Programmer's Reference
OLE Programmer's Reference
Microsoft Windows Pen API Programmer's Reference
Microsoft Windows Sockets 2 Reference
Microsoft Windows Telephony API (TAPI) Programmer's Reference
Unix Manual Pages

Free Tech Secrets ;) Copyright © 2008-2012 Free Tech Secrets ;) greatis just4fun network just4fun