Owner Draw Menu 在动态修改后文字截断的问题

最近遇到这个问题,具体现象和这里提到的差不多:

http://www.cnblogs.com/vibration/archive/2004/11/22/66904.html

 

简单来说就是,自绘菜单依赖一个只会在第一次popup的时候才被调用的MeasureItem来计算合理的菜单宽度/高度,导致在动态修改菜单项为比原本更长的文本时仍以旧的宽度画菜单。

贴个截图可能更清楚:

ScreenShot

 

Google了一下,发现这个:

http://www.eggheadcafe.com/forumarchives/NETFrameworkNETWindowsFormscontrols/Oct2005/post24408815.asp

 

于是试了一下临时增加菜单项再删除的方法,结果证明不行,后来发现下面这个方法才可行:

 

?
1
2
3
4
5
MENUITEMINFO menuItemInfo = {0};
...
pMenu->GetMenuItemInfo(nMenuIndex, &menuItemInfo, TRUE); // 拿ItemInfo,以免后面SetMenuItemInfo冲掉fType里面的MFT_OWNERDRAW标记
pMenu->ModifyMenu(nMenuIndex, MF_BYPOSITION, pMenu->GetMenuItemID(nMenuIndex), lpcszMenuText);
::SetMenuItemInfo(pMenu->GetSafeHmenu(), nMenuIndex, TRUE, &menuItemInfo);   // 使得MeasureItem被调用

转自:http://www.cnblogs.com/yonken/archive/2010/03/18/Modify_Owner_Drawn_Menu_Mess_Up_Item_Text.html
在 MFC 中,如果你尝试通过 `ModifyStyle(0, SS_OWNERDRAW)` 将一个标准的 `CStatic` 控件设置为自定义绘制(Owner Draw),但出现断言失败(Assertion Failed),通常是因为你试图修改一个**不支持 Owner Draw 的控件**,或者控件本身没有正确响应 `WM_DRAWITEM` 消息。 --- ## ✅ 问题原因 `SS_OWNERDRAW` 样式表示控件由父窗口负责绘制,而不是由系统自动绘制。但标准的 `CStatic` 控件并不会自动处理 `WM_DRAWITEM` 消息,除非你手动重写 `DrawItem` 方法。 如果你只是设置了 `SS_OWNERDRAW`,但没有实现 `DrawItem`,MFC 在绘制时会触发断言错误,提示你必须重写 `DrawItem` 函数。 --- ## ✅ 解决方案:自定义 `CStatic` 子类实现 `DrawItem` 你需要创建一个继承自 `CStatic` 的自定义类,并重写 `DrawItem` 方法。 --- ### ✅ 步骤 1:创建自定义静态控件类 #### MyStatic.h ```cpp #pragma once class CMyStatic : public CStatic { DECLARE_DYNAMIC(CMyStatic) public: CMyStatic(); virtual ~CMyStatic(); // 设置背景颜色 void SetBackColor(COLORREF color); // 覆盖 DrawItem 方法 virtual void DrawItem(LPDRAWITEMSTRUCT /*lpDrawItemStruct*/); private: COLORREF m_colorBk; }; ``` #### MyStatic.cpp ```cpp #include "pch.h" #include "MyStatic.h" IMPLEMENT_DYNAMIC(CMyStatic, CStatic) CMyStatic::CMyStatic() { m_colorBk = RGB(255, 255, 255); // 默认白色 } CMyStatic::~CMyStatic() { } void CMyStatic::SetBackColor(COLORREF color) { m_colorBk = color; Invalidate(); UpdateWindow(); } void CMyStatic::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) { CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC); CRect rect = lpDrawItemStruct->rcItem; // 填充背景 CBrush brush(m_colorBk); pDC->FillRect(rect, &brush); // 绘制文本 CString text; GetWindowText(text); pDC->SetBkMode(TRANSPARENT); pDC->SetTextColor(RGB(0, 0, 0)); pDC->DrawText(text, &rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE); } ``` --- ### ✅ 步骤 2:在对话框中使用自定义控件 #### 1. 在对话框类中声明成员变量: ```cpp CMyStatic m_StaIdle; ``` #### 2. 在 `DoDataExchange` 中绑定控件: ```cpp void CMyDialog::DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); DDX_Control(pDX, IDC_STATIC_IDLE, m_StaIdle); } ``` #### 3. 初始化控件颜色: ```cpp m_StaIdle.SetBackColor(RGB(0, 255, 0)); // 初始绿色 ``` #### 4. 更新状态时刷新颜色: ```cpp void CMyDialog::UpdateIdleStatus() { BOOL bIdle = m_driLoadport->Idle(); if (m_bIdle != bIdle) { m_bIdle = bIdle; m_StaIdle.SetBackColor(m_bIdle ? RGB(0, 255, 0) : RGB(255, 255, 255)); } } ``` --- ### ✅ 注意事项 - 不需要再调用 `ModifyStyle(0, SS_OWNERDRAW)`,因为你已经在子类中处理了 `DrawItem`。 - 如果你使用的是资源编辑器中已有的 `CStatic` 控件,确保它没有设置 `SS_ICON`、`SS_BITMAP` 等与 OwnerDraw 冲突的样式。 - 使用自定义控件后,不再依赖 `OnCtlColor` 来改变背景颜色。 --- ### ✅ 总结 | 方法 | 说明 | |------|------| | `OnCtlColor` | 适用于简单背景颜色变化,但不能精细控制绘制 | | `SS_OWNERDRAW + DrawItem` | 更灵活,适合完全自定义外观(如图标、渐变、文字特效) | | `Invalidate()` + `UpdateWindow()` | 控制刷新时机 | | 自定义控件 | 推荐方式,结构清晰,易于维护和扩展 | --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值