CBS_DROPDOWNLIST风格CComboBox自定义OnPaint后标准文本绘制问题求助
解决CComboBox自定义绘制时标准文本覆盖问题及选中项文本获取方法
问题根源分析
你遇到的标准文本覆盖自定义绘制内容的问题,核心原因是:
- 未给组合框设置所有者绘制风格,系统在选择切换/滚轮操作时会触发默认文本绘制流程;
- 若重写
OnPaint时调用了基类CComboBox::OnPaint(),会强制触发系统默认绘制逻辑; - 未处理
WM_DRAWITEM消息,无法完全接管所有场景下的绘制控制(包括选择变化后的显示区更新)。
解决步骤
1. 设置所有者绘制风格
确保你的CComboBoxX控件拥有CBS_OWNERDRAWFIXED(固定项高度)或CBS_OWNERDRAWVARIABLE(可变项高度)风格,同时保留CBS_DROPDOWNLIST:
- 资源编辑器中:选中组合框,在「属性」→「所有者绘制」选择「固定」或「可变」;
- 动态创建时:
m_comboX.Create(CBS_DROPDOWNLIST | CBS_OWNERDRAWFIXED | WS_VISIBLE | WS_CHILD, CRect(10,10,200,30), this, IDC_MY_COMBO);
2. 重写OnDrawItem完全接管绘制
这是所有者绘制控件的核心处理函数,覆盖所有场景(下拉列表项、选择后的显示区)的绘制:
void CComboBoxX::DrawItem(LPDRAWITEMSTRUCT lpDIS) { CDC dc; dc.Attach(lpDIS->hDC); // 获取当前项文本 CString strItemText; if (lpDIS->itemID != UINT(-1)) { GetLBText(lpDIS->itemID, strItemText); } // 处理选中/非选中状态的背景与文本色 if (lpDIS->itemState & ODS_SELECTED) { dc.SetTextColor(GetSysColor(COLOR_HIGHLIGHTTEXT)); dc.SetBkColor(GetSysColor(COLOR_HIGHLIGHT)); dc.FillSolidRect(&lpDIS->rcItem, GetSysColor(COLOR_HIGHLIGHT)); } else { dc.SetTextColor(GetSysColor(COLOR_WINDOWTEXT)); dc.SetBkColor(GetSysColor(COLOR_WINDOW)); dc.FillSolidRect(&lpDIS->rcItem, GetSysColor(COLOR_WINDOW)); } // 绘制焦点框(可选) if (lpDIS->itemState & ODS_FOCUS) { dc.DrawFocusRect(&lpDIS->rcItem); } // 自定义绘制文本,可调整对齐方式 dc.DrawText(strItemText, &lpDIS->rcItem, DT_CENTER | DT_VCENTER | DT_SINGLELINE); dc.Detach(); }
3. 按需重写OnMeasureItem(仅可变高度风格)
如果设置了CBS_OWNERDRAWVARIABLE,需要指定每个下拉项的高度:
void CComboBoxX::MeasureItem(LPMEASUREITEMSTRUCT lpMIS) { lpMIS->itemHeight = 22; // 自定义项高度 }
4. 修正OnPaint实现(可选,若仍需单独处理显示区)
如果保留OnPaint重写,必须去掉基类CComboBox::OnPaint()的调用,完全自行绘制显示区:
void CComboBoxX::OnPaint() { CPaintDC dc(this); CRect rcClient, rcDropBtn; GetClientRect(rcClient); GetDroppedControlRect(rcDropBtn); // 绘制背景 dc.FillSolidRect(rcClient, GetSysColor(COLOR_WINDOW)); // 绘制下拉按钮 dc.DrawFrameControl(rcDropBtn, DFC_SCROLL, DFCS_SCROLLCOMBOBOX); // 获取选中项文本并绘制 CString strSelText; int nSel = GetCurSel(); if (nSel != CB_ERR) { GetLBText(nSel, strSelText); } // 留出按钮空间,避免文本被遮挡 rcClient.right = rcDropBtn.left - 2; dc.DrawText(strSelText, rcClient, DT_LEFT | DT_VCENTER | DT_SINGLELINE); }
5. 获取选中项文本的正确方式
无需复杂操作,直接通过GetCurSel()+GetLBText()即可获取:
CString GetSelectedText() { CString strText; int nSelIndex = GetCurSel(); if (nSelIndex != CB_ERR) { GetLBText(nSelIndex, strText); } return strText; }
关键注意事项
- 绝对不要在自定义绘制的
OnPaint或OnDrawItem中调用基类的对应函数,否则会触发系统默认文本绘制; - 确保所有绘制逻辑都在
OnDrawItem中完成,它是所有者绘制控件的标准入口,能覆盖所有交互场景的绘制需求。
内容的提问来源于stack exchange,提问作者natefoust
相关产品推荐
相关产品推荐

