本主题中的示例在菜单中使用所有者绘制的菜单项。菜单项选择特定的字体属性,应用程序使用具有相应属性的字体显示每个菜单项。例如,斜体菜单项以斜体字显示。菜单栏上的字符菜单名称打开菜单。
菜单栏和下拉菜单最初由扩展菜单模板资源定义。因为菜单模板不能指定所有者绘制的项目,菜单最初包含四个文本菜单项,其中包含以下字符串:“常规”,“粗体”,“斜体”和“下划线”。应用程序的窗口过程在处理WM_CREATE消息时将其更改为所有者绘制的项目。当接收到WM_CREATE消息时,窗口过程调用应用程序定义的OnCreate函数,该函数对每个菜单项执行以下步骤:
1.分配应用程序定义的MYITEM结构。
2.获取菜单项的文本并将其保存在应用程序定义的MYITEM结构中。
3.创建用于显示菜单项的字体,并将其句柄保存在应用程序定义的MYITEM结构中。
4.将菜单项类型更改为MFT_OWNERDRAW,并将一个指针指向应用程序定义的MYITEM结构作为项目数据。
因为指向每个应用程序定义的MYITEM结构的指针被保存为项目数据,所以将它传递给窗口过程,并结合相应菜单项的WM_MEASUREITEM和WM_DRAWITEM消息。该指针包含在MEASUREITEMSTRUCT和DRAWITEMSTRUCT结构的的ItemData成员中。
首次显示每个所有者绘制的菜单项时会发送WM_MEASUREITEM消息。应用程序通过将菜单项的字体选择到设备上下文中,然后确定显示该字体中的菜单项文本所需的空间来处理此消息。字体和菜单项文本都由菜单项的MYITEM结构(应用程序定义的结构)指定。应用程序使用GetTextExtentPoint32函数确定文本的大小。
窗口过程通过以适当的字体显示菜单项文本来处理WM_DRAWITEM消息。字体和菜单项文本都由菜单项的MYITEM结构指定。应用程序选择适合菜单项状态的文本和背景颜色。
窗口过程处理WM_DESTROY消息以销毁字体和可用内存。应用程序删除字体,并为每个菜单项释放应用程序定义的MYITEM结构。
以下是应用程序头文件的相关部分。
//字符菜单的菜单项标识符
#define IDM_BOLD 11
#define IDM_REGULAR 11
#define IDM_BOLD 12
#define IDM_ITALIC 13
#define IDM_UNDERLINE 14
//与菜单项相关联的结构
typedef struct tagMYITEM {en
HFONT hfont;
int cchItemText;
char szItemText[1];
} MYITEM;
#定义CCH_MAXITEMTEXT 256
以下是应用程序的窗口过程及其相关功能的相关部分。
LRESULT CALLBACK MainWindowProc(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
if(!OnCreate(hwnd))
)
{
开关(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;
.
. //附加消息处理在这里。
.
默认:
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;
//获取弹出菜单的句柄。
mii.fMask = MIIM_SUBMENU; //获取信息
GetMenuItemInfo(hmenuBar, IDM_CHARACTER, FALSE, &mii);
hmenuPopup = mii.hSubMenu;
//修改每个菜单项。假设ID为IDM_REGULAR
//通过IDM_UNDERLINE是连续的数字。
for (uID = IDM_REGULAR; uID <= IDM_UNDERLINE; uID++) {
//分配一个项目结构,留出一个空格
//字符串最多为CCH_MAXITEMTEXT个字符。
pMyItem = (MYITEM *) LocalAlloc(LMEM_FIXED,
sizeof(MYITEM) + CCH_MAXITEMTEXT);
//将项目文本保存在项目结构中。
mii.fMask = MIIM_TYPE;
mii.dwTypeData = pMyItem->szItemText;
mii.cch = CCH_MAXITEMTEXT;
GetMenuItemInfo(hmenuPopup, uID, FALSE, &mii);
pMyItem->cchItemText = mii.cch;
//将结构重新分配到最小所需大小。
pMyItem = (MYITEM *) LocalReAlloc(pMyItem,
sizeof(MYITEM) + mii.cch, LMEM_MOVEABLE);
//创建用于绘制项目的字体。
pMyItem->hfont = CreateMenuItemFont(uID);
//将项目更改为所有者绘制的项目,并保存
//项目结构的地址作为项目数据。
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;
//获取菜单的句柄。
mii.fMask = MIIM_SUBMENU; //获取信息
GetMenuItemInfo(hmenuBar, IDM_CHARACTER, FALSE, &mii);
hmenuPopup = mii.hSubMenu;
//与每个菜单项相关联的空闲资源。
for (uID = IDM_REGULAR; uID <= IDM_UNDERLINE; uID++) {
//获取项目数据。
mii.fMask = MIIM_DATA;
GetMenuItemInfo(hmenuPopup, uID, FALSE, &mii);
pMyItem = (MYITEM *) mii.dwItemData;
//破坏字体并释放项目结构。
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;
//设置相应的前景色和背景色。
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));
}
//确定要绘制的位置,并留出空格用于复选标记。
x = lpdis->rcItem.left;
y = lpdis->rcItem.top;
x += LOWORD(GetMenuCheckMarkDimensions());
//选择字体并绘制文字。
hfntPrev = SelectObject(lpdis->hDC, pMyItem->hfont);
ExtTextOut(lpdis- > hDC,x,y,ETO_OPAQUE,
& lpdis- > rcItem,pMyItem- > szItemText,
pMyItem->cchItemText, NULL);
//恢复原始字体和颜色。
SelectObject(lpdis->hDC, hfntPrev);
SetTextColor(lpdis->hDC, clrPrevText);
SetBkColor(lpdis->hDC, clrPrevBkgnd);
}