You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何为CView派生类中的上下文菜单设置整体黄色背景且正常显示所有菜单项?

如何为CView派生类中的上下文菜单设置整体黄色背景且正常显示所有菜单项?

你遇到的这个问题其实挺典型的:第一次你给第一个菜单项加了MF_OWNERDRAW,结果整个菜单背景确实变黄了,但第一个菜单项完全看不到;第二次去掉自绘标记后,所有菜单项都显示了,但只有菜单项本身的背景是黄色,菜单的空白区域还是默认颜色。咱们先拆解下为啥会出现这种情况:

问题原因分析

  1. 当你给某个菜单项加上MF_OWNERDRAW标记时,Windows就会把这个菜单项的全部绘制工作交给你处理——系统不会再帮你画文字、选中状态这些内容,你必须自己编写对应的绘制逻辑。你现在只设置了菜单的整体背景,却没写自绘项的绘制代码,所以第一个菜单项就变成空白了。
  2. 当你不加MF_OWNERDRAW时,系统会自动绘制所有菜单项,但默认情况下SetMenuInfo的背景设置只作用于单个菜单项的区域,不会覆盖整个弹出菜单的空白背景,所以就出现了“只有菜单项后面黄,菜单边缘还是默认色”的情况。

解决方案(按需求选择)

方案一:只想整个菜单背景变黄,不需要自绘菜单项(最简单)

这种情况完全不用给任何菜单项加MF_OWNERDRAW,只需要给MENUINFO加上MIM_APPLYTOSUBMENUS标记,让背景设置应用到整个弹出菜单的容器(包括空白区域),而不是只针对单个菜单项。

修改后的完整代码:

CMenu menu;
menu.CreatePopupMenu();

// 所有菜单项都保留默认的非自绘标记
InsertMenu(menu, 0, MF_BYCOMMAND | MF_STRING | MF_ENABLED, 1, L"Item 1");
InsertMenu(menu, 0, MF_BYCOMMAND | MF_STRING | MF_ENABLED, 2, L"Item 2");

CBrush m_BKBrush;
m_BKBrush.CreateSolidBrush(0x00ffee/*黄色*/);
MENUINFO mnInfo;
memset(&mnInfo, 0, sizeof(MENUINFO));
mnInfo.cbSize = sizeof(MENUINFO);
// 加上MIM_APPLYTOSUBMENUS,让背景设置覆盖整个弹出菜单
mnInfo.fMask = MIM_BACKGROUND | MIM_APPLYTOSUBMENUS;
mnInfo.hbrBack = m_BKBrush;
::SetMenuInfo(menu, &mnInfo);
m_BKBrush.Detach();

menu.TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, this);

这样修改后,整个弹出菜单的背景(包括空白区域)都会变成黄色,所有菜单项的文字也会正常显示,完美符合你的需求。

方案二:确实需要某个菜单项是自绘的(比如要做特殊样式)

如果你的场景里必须有自绘菜单项,那你得在CView派生类里处理WM_DRAWITEM消息,自己把自绘项的内容画出来:

  1. 在View类的头文件里添加函数声明
afx_msg void OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct);
  1. 在消息映射里添加关联
ON_WM_DRAWITEM()
  1. 实现绘制逻辑
void CMyView::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct)
{
    // 判断是不是菜单的自绘项
    if (lpDrawItemStruct->CtlType == ODT_MENU)
    {
        // 1. 先画菜单项的背景(和菜单整体背景统一为黄色)
        CBrush bgBrush(0x00ffee);
        lpDrawItemStruct->hDC->FillRect(&lpDrawItemStruct->rcItem, &bgBrush);

        // 2. 准备绘制文字:通过itemID判断是哪个菜单项,动态获取文字
        CString itemText;
        if (lpDrawItemStruct->itemID == 1)
        {
            itemText = L"Item 1";
        }
        // 其他自绘项可以在这里继续加判断分支

        // 3. 设置文字颜色(选中时变白,默认黑色)
        COLORREF textColor = (lpDrawItemStruct->itemState & ODS_SELECTED) ? RGB(255, 255, 255) : RGB(0, 0, 0);
        lpDrawItemStruct->hDC->SetTextColor(textColor);
        lpDrawItemStruct->hDC->SetBkMode(TRANSPARENT); // 文字背景透明,避免盖住黄色背景

        // 4. 把文字画到菜单项区域里(左对齐、垂直居中)
        lpDrawItemStruct->hDC->DrawText(itemText, itemText.GetLength(), &lpDrawItemStruct->rcItem, DT_LEFT | DT_VCENTER | DT_SINGLELINE);
    }
    else
    {
        // 非菜单的自绘项,交给基类处理
        CView::OnDrawItem(nIDCtl, lpDrawItemStruct);
    }
}

之后你原来的代码里给第一个菜单项加MF_OWNERDRAW就没问题了,自绘项会正常显示文字,同时整个菜单背景也是黄色的。

备注:内容来源于stack exchange,提问作者RocketSearcher

火山引擎 最新活动