如何创建拥有者绘制菜单——分步教程






4.34/5 (14投票s)
2004年5月17日
3分钟阅读

114839

2587
本教程将分步介绍如何创建拥有者绘制菜单。
引言
这只是一个关于如何逐步创建您自己的 - 自定义绘制 - 菜单的教程!
教程
步骤 1
首先,您应该创建自己的类。我将其命名为 COwnMenu
。基类应该是 CMenu
。然后创建一个 struct
,它可能看起来像这样
struct MenuObject {
HICON m_hIcon;
CString m_strCaption;
};
然后,您必须在您的主类(例如,CMainFrame
或 CTestDlg
)中声明一个 COwnMenu
的实例。 建议您还声明两个向量,用于保存您已分配的每个项目的地址!
COwnMenu menu;
std::vector<DWORD> deleteItem;
std::vector<DWORD> deleteMenu;
第二步
接下来要做的事情是创建一个将菜单的所有项目更改为 MF_OWNERDRAW
的函数。 我创建了一个递归函数来逐步遍历每个菜单项... 所有已分配项目的地址都保存在 deleteItem
中(如果它是一个项目的话 :))和 deleteMenu
中(如果它是一个菜单的话 :))
该函数可能看起来像这样
void COwnMenu::MakeItemsOwnDraw(BOOL bFirst)
{
int iMaxItems = GetMenuItemCount();
for(int i = 0; i < iMaxItems; i++)
{
MenuObject* pObject = new MenuObject;
deleteItem.push_back((DWORD)pObject);
pObject->m_hIcon = NULL;
GetMenuString(i, pObject->m_strCaption, MF_BYPOSITION);
MENUITEMINFO mInfo;
ZeroMemory(&mInfo, sizeof(MENUITEMINFO));
UINT uID = mInfo.wID;
/*I don't use GetMenuItemID because
it doesn't return 0/-1 when it's a Popup
(so the MSDN is wrong)*/
ModifyMenu(i, MF_BYPOSITION | MF_OWNERDRAW,
uID, (char*)pObject);
if(GetSubMenu(i))
{
COwnMenu* pSubMenu = new COwnMenu;
deleteMenu.push_back((DWORD)pSubMenu);
pSubMenu->Attach(GetSubMenu(i)->GetSafeHmenu());
pSubMenu->MakeItemsOwnDraw();
}
}
}
解释
首先,您遍历所有菜单项。 然后,您创建一个新的 MenuObject
并将其地址添加到 deleteItem
。 如果您有图标,您可以将 pObject->m_hIcon
更改为其地址。 接下来您要做的就是获取该项目的标题并将其保存到 pObject->m_strCaption
。 然后将菜单的样式更改为 MF_OWNERDRAW
。 您必须了解的是,ModifyMenu
的最后一个参数是指向您的 pObject
的指针,我们稍后必须在 DrawItem
和 MeasureItem
中使用它!
接下来您要做的是检查该项目是否为弹出项目,这意味着它有子项。 如果是,您创建一个新的 COwnMenu
,将其地址添加到 deleteMenu
,以便我们可以在销毁程序时清除整个内存。 完成此操作后,我们对该项目执行整个函数。
所以 - 现在你可以告诉我,有什么难以理解的。 :)
步骤 3
现在,您必须添加这两个函数
void COwnMenu::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
CRect rectFull(lpDrawItemStruct->rcItem);
CRect rectIcon(rectFull.left,rectFull.top,rectFull.left+20,rectFull.top+20);
CRect rectText(rectIcon.right,rectFull.top,rectFull.right,rectFull.bottom);
COLORREF IconRectLeft = COLORREF(RGB(246,245,244));
COLORREF IconRectRight = COLORREF(RGB(0,209,201));
COLORREF TextRect = COLORREF(RGB(249, 248, 247));
CRect rectBorder = rectFull;
rectBorder.right -= 1;
CRect rectFill = rectBorder;
rectFill.left += 1;
rectFill.right -= 1;
rectFill.top += 1;
rectFill.bottom -= 1;
if(((MenuObject*)lpDrawItemStruct->itemData)->bFirstMenu)
{
ZeroMemory(&rectIcon, sizeof(CRect));
rectText = rectFull;
TextRect = GetSysColor(COLOR_BTNFACE);// COLORREF(RGB(192,192,192));
}
CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
FillFluentRect(pDC->GetSafeHdc(),
rectIcon, 246,245,244,213,209,201);
pDC->FillSolidRect(&rectText,
TextRect);
if ((lpDrawItemStruct->itemState & ODS_SELECTED) &&
(lpDrawItemStruct->itemAction & (ODA_SELECT | ODA_DRAWENTIRE)))
{
TextRect = COLORREF(RGB(182, 189, 210));
pDC->FillSolidRect(&rectBorder, COLORREF(RGB(10, 36, 106)));
pDC->FillSolidRect(&rectFill, TextRect);
}
pDC->SetBkColor(TextRect);
rectText.left += 5;
rectText.top += 1;
rectText.bottom += 1;
pDC->TextOut(rectText.left,
rectText.top,
((MenuObject*)lpDrawItemStruct->itemData)->m_strCaption);
}
void COwnMenu::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
lpMeasureItemStruct->itemHeight = 20;
lpMeasureItemStruct->itemWidth =
((MenuObject*)
lpMeasureItemStruct->itemData)->m_strCaption.GetLength()*8;
}
在 MeasureItem
中,您必须告诉菜单您的项目有多大! 我认为,这真的很容易理解 :)。
在 DrawItem
中,您检查该项目是否被选中。 如果是,我们绘制这个很酷的 XP 风格的 Rect
。 我制作了一个名为 FillFluentRect
的很酷的函数,它为我们的图标位置绘制很酷的特殊效果 ;)。 看起来像这样
void COwnMenu::FillFluentRect(HDC hDC, RECT rect,
byte r1, byte g1, byte b1, byte r2, byte g2, byte b2)
{
int iWidth = rect.right - rect.left;
int iHeight = rect.bottom - rect.top;
short rDif = r2 - r1;
short gDif = g2 - g1;
short bDif = b2 - b1;
for(int i = 0; i < iWidth; i++)
{
byte rCur, gCur, bCur;
rCur = r1 + (short)(float)(((float)rDif/(float)iWidth)*(float)i);
gCur = g1 + (short)(float)(((float)gDif/(float)iWidth)*(float)i);
bCur = b1 + (short)(float)(((float)bDif/(float)iWidth)*(float)i);
for(int y = 0; y < iHeight; y++)
SetPixel(hDC, rect.left + i, rect.top + y,
RGB(rCur, gCur, bCur));
}
}
我想,我不必在这里解释任何事情。
当您想在 DrawItem
中绘制图标或位图时,您只需使用 BitBlt
将其绘制到图标 rect
中... 如果有人对此有问题 - 欢迎提问 :) ... 但我认为这并不难!
步骤 4
接下来是我们在程序结束时清理内存,这并不难理解...所以看看这个
COwnMenu::~COwnMenu()
{
for(int i = 0; i < deleteItem.size(); i++)
{
delete ((MenuObject*)deleteItem[i]);
}
{
for(int i = 0; i < deleteMenu.size(); i++)
{
delete ((COwnMenu*)deleteMenu[i]);
}
}
}
步骤 5
接下来是激活我们的菜单。 :) 它可以在 CDialog::OnInitDialog()
或 CMainFrame::OnCreate()
中完成。 它可能看起来像这样
menu.LoadMenu(IDR_MENU);
menu.MakeItemsOwnDraw(TRUE);
SetMenu(&menu);
附加信息
如果您希望菜单具有平面边框,那么您必须设置一个 WindowsHook
,并且在 WindowProc
中,您必须确定该窗口是否是您的菜单... 如果您不知道这是如何工作的,请查看 CMenuXP
示例。
结束
现在,我们完成了... 理解起来真的不难...是吗? 我希望我能帮助你们中的一些人。
许可证
本文没有明确的许可证附加到它,但可能包含在文章文本或下载文件本身中的使用条款。如有疑问,请通过下面的讨论区联系作者。
作者可能使用的许可证列表可以在此处找到。