自绘 MFC 控件 CComboBox

文章介绍了如何在Windows应用程序中自定义CComboBox控件,通过设置其为OwnerDraw模式,实现DrawItem和MeasureItem方法,以控制下拉列表的样式,包括字体、颜色、高度等,并提供了相应的C++源码示例。
摘要由CSDN通过智能技术生成

后附源码!

运行效果

第一步:在窗口中拖拽一个CComboBox控件,设置如下属性:

  • 类型,设置为:下拉列表
  • 包含字符串,设置为:True
  • 所有者描述,设置为:Variable

  注意

  1. 包含字符串,不设置为True,则使用GetLBText等函数无法获取到Item的text;
  2. 所有者描述,设置为No,不执行DrawItem、MeasureItem;
  3. 所有者描述,设置为Fixed,执行DrawItem,不执行MeasureItem;
  4. 所有者描述,设置为Variable,执行DrawItem、measureItem;
  5. DrawItem中绘制下拉列表;
  6. MeasureItem中设置下拉列表中向的高度。

第二步:选中CComboBox控件的下拉箭头,弹出下拉框,拖住拉大到至少能显示5个item的大小

        注意:如果不拉大下拉框大于5个item的大小,则运行时下拉框不会显示出来。

第三步:选中CComboBox鼠标右键为其添加变量

第四步:定义CMyComboBox类,并使用CMyComboBox类名替换刚才生成的CComboBox类型的变量的类型

第五步:CComboBox 控件无法通过拖拽增加其Height,可以通过如下消息进行设置:

        SendMessage(CB_SETITEMHEIGHT, -1, 37); // 37即为想要设置的Height

附代码:

CMyComboBox.h 

#pragma once
#include <afxwin.h>
#include <vector>

class CMyComboBox : public CComboBox
{
public:
    CMyComboBox();
    
    void SetItem(std::vector<CString> text);
    
    void SetTextColor(COLORREF rgb);

    void SetFont(const std::string& name, int size, bool bold = false);

    void SetBkColor(COLORREF color);

    virtual void PreSubclassWindow() override;
    
    virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);

    virtual void MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct);

    virtual int CompareItem(LPCOMPAREITEMSTRUCT lpCompareItemStruct);

protected:
    afx_msg void OnPaint();
    DECLARE_MESSAGE_MAP();

private:
    CString m_text;
    COLORREF m_text_color;
    
    std::string m_font;
    int m_font_size;
    bool m_bold;

    COLORREF m_bk_color;
};

CMyComboBox.cpp

#include "pch.h"
#include "CMyComboBox.h"

CMyComboBox::CMyComboBox()
    : m_text(_T("请选择"))
    , m_text_color(RGB(0,0,0)
    , m_font("Microsoft YaHei UI")
    , m_bold(false)
    , m_font_size(14)
	, m_bk_color(TRANSPARENT)
{

}

BEGIN_MESSAGE_MAP(CMyComboBox, CComboBox)
	ON_WM_PAINT()
END_MESSAGE_MAP()

void CMyComboBox::SetItemText(std::vector<CString> text)
{
	InsertString(GetCount(), m_text);

	for (auto item : text) {
		InsertString(GetCount(), item);
	}

	SetCurSel(0);
}

void CMyComboBox::SetTextColor(COLORREF rgb)
{
	m_text_color = rgb;
}

void CMyComboBox::SetFontName(const std::string& font, bool bold/* = false*/)
{
	m_font_name = font;
	m_bold = bold;
}

void CMyComboBox::SetFontSize(int size)
{
	m_font_size = size;
}

void CMyComboBox::SetBkColor(COLORREF color)
{
	m_bk_color= color;
    SendMessage(CB_SETITEMHEIGHT, -1, 37);
}

void CMyComboBox::PreSubclassWindow()
{
	ModifyStyle(0, BS_OWNERDRAW); 
	CComboBox::PreSubclassWindow();
}

void CMyComboBox::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
	if (ODT_COMBOBOX != lpDrawItemStruct->CtlType) {
		return CComboBox::DrawItem(lpDrawItemStruct);
	}

	CString text;
	if (-1 != lpDrawItemStruct->itemID) {
		GetLBText(lpDrawItemStruct->itemID, text);
	}
	else {
		return CComboBox::DrawItem(lpDrawItemStruct);
	}

	CDC dc;
	dc.Attach(lpDrawItemStruct->hDC);

	// Save these value to restore them when done drawing.
	COLORREF crOldTextColor = dc.GetTextColor();
	COLORREF crOldBkColor = dc.GetBkColor();

	CFont font;
	font.CreateFont(m_font_size - 5, 0, 0, 0, (m_bold ? FW_BOLD : FW_NORMAL), FALSE, FALSE, FALSE, ANSI_CHARSET,
	OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS,
		to_wstring(m_font_name).c_str());
	dc.SelectObject(&font);

	dc.SetTextColor(m_text_color);
	dc.SetBkColor(m_back_color);
	dc.FillSolidRect(&lpDrawItemStruct->rcItem, m_back_color);

	// Draw the text.
	dc.DrawText(
		text,
		(int)_tcslen(text),
		&lpDrawItemStruct->rcItem,
		DT_SINGLELINE);

	// Reset the background color and the text color back to their
	// original values.
	dc.SetTextColor(crOldTextColor);
	dc.SetBkColor(crOldBkColor);

	dc.Detach();
}

void CMyComboBox::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
	CRect rc;
	GetClientRect(&rc);

	lpMeasureItemStruct->itemHeight = 20;
}

int CMyComboBox::CompareItem(LPCOMPAREITEMSTRUCT lpCompareItemStruct)
{
	return 1;
}

void CMyComboBox::OnPaint()
{
	CRect rect;
	GetClientRect(&rect);
    
    CPaintDC pDC(this);
	pDC.SetBkMode(TRANSPARENT);
	pDC.SelectStockObject(NULL_BRUSH);

    CPen pen;
	pen.CreatePen(PS_SOLID, 1, m_bk_color);
	pDC.SelectObject(&pen);

    CFont font;
	font.CreateFontW(m_font_size - 5, 0, 0, 0, (m_bold ? FW_BOLD : FW_NORMAL), FALSE, FALSE, FALSE, ANSI_CHARSET,
			OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS,to_wstring(m_font_name).c_str());
    pDC.SelectObject(&font);

    CBrush brush;
	CBrush brush_flag;
	CBrush brush_angle;
	CRgn rgn;
    if (m_back_color != TRANSPARENT) {
		brush.CreateSolidBrush(m_bk_color);
		pDC.SelectObject(&brush);
		pDC.Rectangle(&rect);

		CRect rc(rect);
		rc.left = rect.right - 20;
		brush_flag.CreateSolidBrush(m_bk_color);
		pDC.SelectObject(&brush_flag);
		pDC.Rectangle(rc);

		int angleSideWidth = 10;
		CPoint ptAngle[3];
		ptAngle[0].x = rc.left + rc.Width() / 2 - angleSideWidth / 2;
		ptAngle[0].y = rc.top + rc.Height() / 2 - 2;

		ptAngle[1].x = ptAngle[0].x + angleSideWidth;
		ptAngle[1].y = ptAngle[0].y;

		ptAngle[2].x = rc.left + rc.Width() / 2;
		ptAngle[2].y = ptAngle[0].y + 5;

		rgn.CreatePolygonRgn(ptAngle, 3, ALTERNATE);
		brush_angle.CreateSolidBrush(RGB(123,123,123));
		pDC.FillRgn(&rgn, &brush_angle);
    }

	CString text;
	GetWindowTextW(text);
	if (0 == text.GetLength()) {
		text = m_text;
	}

	int len = text.GetLength();
	if (0 < len) {
		TEXTMETRIC text_metric;
		pDC.GetTextMetrics(&text_metric);

		int offset_move = ((text_metric.tmPitchAndFamily & 0x0F) * (len - 1) + text_metric.tmAveCharWidth * len);
		if (0 < offset_move) {
            pDC.MoveTo((len <= 6 ? offset_move + text_metric.tmAveCharWidth : offset_move), 0);
		}
        
		rect.top += (rect.Height() - text_metric.tmHeight) / 2;
		pDC.SetTextColor(m_text_color);
        pDC.DrawText(text.GetBuffer(), &rect, DT_CENTER | DT_SINGLELINE);
	}

    pen.DeleteObject();
	font.DeleteObject();

    if (m_back_color != TRANSPARENT) {
		brush.DeleteObject();
		brush_flag.DeleteObject();
		brush_angle.DeleteObject();
		rgn.DeleteObject();
	}

	ReleaseDC(&pDC);
	CComboBox::OnPaint();
}

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值