如何为CView派生类中的上下文菜单设置整体黄色背景且正常显示所有菜单项?
如何为CView派生类中的上下文菜单设置整体黄色背景且正常显示所有菜单项?
你遇到的这个问题其实挺典型的:第一次你给第一个菜单项加了MF_OWNERDRAW,结果整个菜单背景确实变黄了,但第一个菜单项完全看不到;第二次去掉自绘标记后,所有菜单项都显示了,但只有菜单项本身的背景是黄色,菜单的空白区域还是默认颜色。咱们先拆解下为啥会出现这种情况:
问题原因分析
- 当你给某个菜单项加上
MF_OWNERDRAW标记时,Windows就会把这个菜单项的全部绘制工作交给你处理——系统不会再帮你画文字、选中状态这些内容,你必须自己编写对应的绘制逻辑。你现在只设置了菜单的整体背景,却没写自绘项的绘制代码,所以第一个菜单项就变成空白了。 - 当你不加
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消息,自己把自绘项的内容画出来:
- 在View类的头文件里添加函数声明:
afx_msg void OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct);
- 在消息映射里添加关联:
ON_WM_DRAWITEM()
- 实现绘制逻辑:
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




