如何使用复选标记正确测量和显示所有者绘制的上下文菜单项?

c00*_*0fd 5 c++ windows winapi mfc contextmenu

我只是想在我的上下文菜单中添加小颜色样本(通过TrackPopupMenu API 显示.)这是我想要实现的Photoshop版本:

在此输入图像描述

据我所知,默认菜单不支持它.顺便说一句,上面的样本(没有颜色样本)是通过这样做生成的:

MENUITEMINFO mii = {0};
mii.cbSize = sizeof(mii);
mii.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STATE | MIIM_STRING;
mii.fType = MFT_STRING;
mii.wID = ID_1_MARKER_01 + m;
mii.dwTypeData = L"Marker";
mii.cch = TSIZEOF(L"Marker");
mii.fState = m == 1 ? MFS_CHECKED : MFS_ENABLED;
if(m == 2)
    mii.fState |= MFS_GRAYED;

VERIFY(::InsertMenuItem(hMenu, ID_1_BEFORE, FALSE, &mii));
Run Code Online (Sandbox Code Playgroud)

所以我发现我需要使用MFT_OWNERDRAW样式自己绘制菜单项,但这就​​是问题的开始.

我更改了代码以显示我的菜单:

MENUITEMINFO mii = {0};
mii.cbSize = sizeof(mii);
mii.fMask = MIIM_FTYPE | MIIM_ID | MIIM_STATE;
mii.fType = MFT_OWNERDRAW;
mii.wID = ID_1_MARKER_01 + m;
mii.dwItemData = MARKER_ID_01 + m;
mii.fState = m == 1 ? MFS_CHECKED : MFS_ENABLED;
if(m == 2)
    mii.fState |= MFS_GRAYED;

VERIFY(::InsertMenuItem(hMenu, ID_1_BEFORE, FALSE, &mii));
Run Code Online (Sandbox Code Playgroud)

然后我需要覆盖WM_MEASUREITEMWM_DRAWITEM消息.但是,当我使用下面将要显示的代码执行此操作时,这是我得到的:

在此输入图像描述

所以请耐心等待.关于这个话题,我有几个问题:

1)在处理WM_MEASUREITEM如何既DC没有HWND提供文本也不提供菜单的情况下我应该如何知道文本的大小?换句话说,如果我这样做,菜单的大小是错误的:

#define TSIZEOF(f) ((sizeof(f) - sizeof(TCHAR)) / sizeof(TCHAR))

//hwnd = HWND supplied in WM_MEASUREITEM notification
HDC hDC = ::GetDC(hwnd);
HGDIOBJ hOldFont = ::SelectObject(hDC, ::SendMessage(hwnd, WM_GETFONT, 0, 0));

SIZE szTxt = {0};
::GetTextExtentPoint32(hDC, 
    L"Marker", 
    TSIZEOF(L"Marker"), 
    &szTxt);

//lpmis = MEASUREITEMSTRUCT*
lpmis->itemWidth = szTxt.cx;
lpmis->itemHeight = szTxt.cy;

::SelectObject(hDC, hOldFont);
::ReleaseDC(hwnd, hDC);
Run Code Online (Sandbox Code Playgroud)

2)然后在处理时WM_DRAWITEM如何知道偏移量以开始在左侧绘制文本?如果我这样做,我的菜单就没有向右偏移(正如你从上面的截图中看到的那样):

int nCheckW = ::GetSystemMetrics(SM_CXMENUCHECK);

//lpdis = DRAWITEMSTRUCT*
::ExtTextOut(lpdis->hDC, 
    lpdis->rcItem.left + nCheckW, 
    lpdis->rcItem.top, 
    ETO_OPAQUE, 
            &lpdis->rcItem, 
    L"Marker", 
    TSIZEOF(L"Marker"), 
    NULL);
Run Code Online (Sandbox Code Playgroud)

3)最后如何在菜单项的左侧绘制默认复选框?

rri*_*wer 2

虽然我不使用颜色样本,并且严格使用 MFC,但我确实在派生菜单项上渲染位图。您应该能够根据您的需要调整以下内容。

\n\n

在测量项目文本时,我使用桌面dc。

\n\n
CClientDC dc(CWnd::GetDesktopWindow());\nSIZE size;\n\nGetTextExtentPoint32(dc.m_hDC, buff, buff.GetLength(), &size );\nlpMeasureItemStruct->itemWidth = size.cx+12;\nlpMeasureItemStruct->itemHeight = size.cy+8;\n
Run Code Online (Sandbox Code Playgroud)\n\n

通过实验进行一些微小的调整,我想出了我需要的尺寸。

\n\n

为了渲染实际的位图和文本,我检查主题是否处于活动状态,并以两种不同的方式之一渲染菜单项。作为主题菜单项或标准菜单项。为了渲染文本,我从通过 LPDRAWITEMSTRUCT 传递的矩形开始。然后,我在渲染文本之前进行以下调整。

\n\n
//  adjust if non-themed.\nif (!IsThemeActive())\n    rectt.left+= m_bmWidth+4;\nelse\n    rectt.left+= BITMAP_ADJUSTMENT;\n
Run Code Online (Sandbox Code Playgroud)\n\n

通过反复试验,我发现将 BITMAP_ADJUSTMENT 设置为 30 对我来说很有效。然后,根据项目的说明(禁用、选择),进一步调整rectt 。

\n\n
//  draw disabled text.\nif (disabled)\n    {\n    //  draw selected text.\n    if (selected)\n        {\n        pDC->SetTextColor(::GetSysColor(COLOR_GRAYTEXT));\n        pDC->DrawText(text, &rectt, format);\n        }\n    else\n        {\n        offset = rectt;\n        offset.left+=   1;\n        offset.right+=1;\n        offset.top+=    1;\n        offset.bottom+= 1;\n        pDC->SetTextColor(::GetSysColor(COLOR_BTNHILIGHT));\n        pDC->DrawText(text, &offset, format);\n        pDC->SetTextColor(::GetSysColor(COLOR_GRAYTEXT));\n        pDC->DrawText(text, &rectt, format);\n        }\n    }\nelse\n    //  draw normal text.\n    pDC->DrawText(text, &rectt, format);\n
Run Code Online (Sandbox Code Playgroud)\n\n

最后,为了呈现复选标记,我使用预定义的位图创建图像列表(我可能已从 Microsoft dll 中提取了它)。同样,位图根据项目的状态进行渲染。

\n\n
    //  draw non-disabled bitmap.\n    if (!disabled)\n        {\n        bmp.GetBitmap(&bm);\n        m_bmWidth = bm.bmWidth;\n        imgList.Create(bm.bmWidth, bm.bmWidth, ILC_COLOR24|ILC_MASK, 1, 1);\n        imgList.Add(&bmp, COLOR_BITMAP_BACKGROUND);\n\n        if (checked)\n            {\n            if (!selected)\n                imgList.DrawEx(pDC, 0, CPoint(4,rect.top+4), CSize(bm.bmWidth, bm.bmWidth), COLOR_NOT_SELECTED, 0, ILD_NORMAL);\n            else\n                imgList.DrawEx(pDC, 0, CPoint(4,rect.top+4), CSize(bm.bmWidth, bm.bmWidth), 0, COLOR_SELECTED, ILD_SELECTED);\n            }\n        else\n            imgList.DrawEx(pDC, 0, CPoint(4,rect.top+4), CSize(bm.bmWidth, bm.bmWidth), 0, 0, ILD_TRANSPARENT);\n        }\n    else\n        //  draw a disabled bitmap.\n        AfxDrawGrayBitmap(pDC, 4, rect.top+4, bmp, ::GetSysColor(COLOR_3DFACE));\n
Run Code Online (Sandbox Code Playgroud)\n\n

大多数渲染是通过在每次尝试后调整矩形对象的迭代方法来完成的。

\n