【自绘ComboBox】带图标的ComboBox

示例图片:

这里写图片描述

这里写图片描述

下载

源码下载
示例程序下载

源码共包含两个文件:
1. ComboBoxXI.h
2. ComboBoxXI.cpp

使用说明

提供的接口

void SetImageList(CImageList* pImageList);
CImageList* GetImageList() const;
int GetItemImage(int nIndex);
int SetItemImage(int nIndex, int nImageIndex, BOOL bRepaint = TRUE);

int AddString(LPCTSTR lpszString, int nImageIndex = -1);
int DeleteString(UINT nIndex);
int InsertString(int nIndex, LPCTSTR lpszString, int nImageIndex);
void ResetContent();

DWORD_PTR GetItemData(int nIndex) const;
int SetItemData(int nIndex, DWORD_PTR dwItemData);
void* GetItemDataPtr(int nIndex) const;
int SetItemDataPtr(int nIndex, void* pData);

使用方法:

Owner Draw — Variable
Has Strings — True

CComboBoxXI m_combo1;
CImageList  m_imglist1;
m_imglist1.Create(32, 32, ILC_COLOR32 | ILC_MASK, 2, 1);
m_imglist1.Add(AfxGetApp()->LoadIcon(IDI_ICON1));
m_imglist1.Add(AfxGetApp()->LoadIcon(IDI_ICON2));

m_combo1.SetImageList(&m_imglist1);
m_combo1.AddString("123", 0);
m_combo1.AddString("456", 1);
m_combo1.SetCurSel(1);

注意事项:

1、对于DropList,在MeasureItem中确定了EditCtrl的高度,对于DropDown和Simple则采用默认的高度。
2、SetItemHeight,第一项传-1表明设置的是EditCtrl的高度
3、DrawItem中,

if (lpDrawItemStruct->itemID == -1)
{
    return;
}

的作用是,如果当前没有选中任何项,则不在EditCtrl中绘制。

源码

[ComboBoxXI.h]

#pragma once

// CComboBoxXI
class CComboBoxXI : public CComboBox
{
    DECLARE_DYNAMIC(CComboBoxXI)

public:
    CComboBoxXI();
    virtual ~CComboBoxXI();

    void SetImageList(CImageList* pImageList);
    CImageList* GetImageList() const;
    int GetItemImage(int nIndex);
    int SetItemImage(int nIndex, int nImageIndex, BOOL bRepaint = TRUE);

    int AddString(LPCTSTR lpszString, int nImageIndex = -1);
    int DeleteString(UINT nIndex);
    int InsertString(int nIndex, LPCTSTR lpszString, int nImageIndex);
    void ResetContent();

    DWORD_PTR GetItemData(int nIndex) const;
    int SetItemData(int nIndex, DWORD_PTR dwItemData);
    void* GetItemDataPtr(int nIndex) const;
    int SetItemDataPtr(int nIndex, void* pData);

protected:
    virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
    virtual void MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct);

    DECLARE_MESSAGE_MAP()

private:
    // 关联的CImageList
    CImageList *m_pImageList;
    CSize      m_imageSize;

    // EditCtrl的高度
    unsigned int  m_nEditHeight;

    // 是否是DropList:
    // -1(待定,未检测), 1(Yes), 0(No)
    // 只对DropList设置EditCtrl的高度,对DropDown和Simple则采用默认高度
    int m_iIsDropList;

    struct CBDataXI
    {
        // 图像序号,如果为-1,则表示无图像
        int iImageIndex;

        // 关联数据的指针
        LPVOID pData;

        CBDataXI()
        {
            iImageIndex = -1;
            pData = NULL;
        }
    };

    void DeleteItemData(int nIndex);
};

[ComboBoxXI.cpp]

/**********************************************************
 ComboBoxXI.cpp : implementation file
 Written by WangYao (wangyao1052@163.com)
 Version: V1.0 2014-06-07
**********************************************************/

#include "stdafx.h"
#include "ComboBoxXI.h"

#define CBXI_CX_BORDER   2
#define CBXI_CY_BORDER   2

// CComboBoxXI

IMPLEMENT_DYNAMIC(CComboBoxXI, CComboBox)

CComboBoxXI::CComboBoxXI()
{
    m_pImageList = NULL;
    m_imageSize.cx = 0;
    m_imageSize.cy = 0;
    m_nEditHeight = 0;
    m_iIsDropList = -1;
}

CComboBoxXI::~CComboBoxXI()
{
}

//---------------------------------------------------------
// 函数: SetImageList
// 功能: 设置关联的ImageList
// 参数: 
//       pImageList --- ImageList的指针
// 返回: 无
//---------------------------------------------------------
void CComboBoxXI::SetImageList(CImageList* pImageList)
{
    m_pImageList = pImageList;
    if (m_pImageList)
    {
        int cx;
        int cy;
        ImageList_GetIconSize(*m_pImageList, &cx, &cy);
        m_imageSize.cx = cx;
        m_imageSize.cy = cy;
    }
    else
    {
        m_imageSize.cx = 0;
        m_imageSize.cy = 0;
    }

    Invalidate();
}

//---------------------------------------------------------
// 函数: GetImageList
// 功能: 获取关联ImageList的指针
// 参数: 无
// 返回: 关联ImageList的指针,若没有关联ImageList返回NULL
//---------------------------------------------------------
CImageList* CComboBoxXI::GetImageList() const
{
    return m_pImageList;
}

//---------------------------------------------------------
// 函数: GetItemImage
// 功能: 获取项的图片索引
// 参数: 
//       nIndex  --- 项索引(以0为起点)
// 返回:
//       以0为起始的图片序号
//       若存在错误,如nIndex超出范围,返回-1
//       若组合框项没有关联图片则返回-1
//---------------------------------------------------------
int CComboBoxXI::GetItemImage(int nIndex)
{
    CBDataXI* pCBData = (CBDataXI*)CComboBox::GetItemData(nIndex);
    if (pCBData == (CBDataXI*)CB_ERR || pCBData == NULL)
    {
        return -1;
    }

    return pCBData->iImageIndex;
}

//---------------------------------------------------------
// 函数: SetItemImage
// 功能: 设置项的图片索引
// 参数: 
//       nIndex      --- 项索引(以0为起点)
//       nImageIndex --- 图片索引(以0为起点)
//       bRepaint    --- 是否重绘
// 返回:
//       成功返回0,失败返回-1
//---------------------------------------------------------
int CComboBoxXI::SetItemImage(int nIndex, int nImageIndex, BOOL bRepaint)
{
    CBDataXI* pCBData = (CBDataXI*)CComboBox::GetItemData(nIndex);
    if (pCBData == (CBDataXI*)CB_ERR || pCBData == NULL)
    {
        return -1;
    }
    pCBData->iImageIndex = nImageIndex;

    if (bRepaint)
    {
        Invalidate();
    }

    return 0;
}

//---------------------------------------------------------
// 函数: AddString
// 功能: 添加项
// 参数: lpszString  --- 字符串指针
//        nImageIndex --- 图片索引(以0为起点)
// 返回: 新增项的索引值
//        失败则返回CB_ERR或CB_ERRSPACE
//---------------------------------------------------------
int CComboBoxXI::AddString(LPCTSTR lpszString, int nImageIndex)
{
    int nIndex = CComboBox::AddString(lpszString);
    if (nIndex != CB_ERR && nIndex != CB_ERRSPACE)
    {
        CBDataXI *pData = new CBDataXI();
        pData->iImageIndex = nImageIndex;
        pData->pData = NULL;
        CComboBox::SetItemData(nIndex, (DWORD_PTR)pData);
    }

    return nIndex;
}

//---------------------------------------------------------
// 函数: InsertString
// 功能: 在指定位置插入项
// 参数: 
//       nIndex      --- 指定位置
//       lpszString  --- 字符串指针
//       nImageIndex --- 图片索引(以0为起点)
// 返回: 
//       插入项索引值
//       失败则返回CB_ERR或CB_ERRSPACE
//---------------------------------------------------------
int CComboBoxXI::InsertString(int nIndex, LPCTSTR lpszString, int nImageIndex)
{
    int nRet = CComboBox::InsertString(nIndex, lpszString);
    if (nRet != CB_ERR && nRet != CB_ERRSPACE)
    {
        CBDataXI *pData = new CBDataXI();
        pData->iImageIndex = nImageIndex;
        pData->pData = NULL;
        CComboBox::SetItemData(nIndex, (DWORD_PTR)pData);
    }

    return nRet;
}

//---------------------------------------------------------
// 函数: DeleteString
// 功能: 删除项
// 参数: 
//       nIndex  --- 索引
// 返回: 
//       成功则返回当前还剩的项数
//       失败则返回CB_ERR
//---------------------------------------------------------
int CComboBoxXI::DeleteString(UINT nIndex)
{
    DeleteItemData(nIndex);

    return CComboBox::DeleteString(nIndex);
}

//---------------------------------------------------------
// 函数: ResetContent
// 功能: 清空内容
//---------------------------------------------------------
void CComboBoxXI::ResetContent()
{
    int nCnt = CComboBox::GetCount();
    for (int i = 0; i < nCnt; ++i)
    {
        DeleteItemData(i);
    }

    CComboBox::ResetContent();
}

//---------------------------------------------------------
// 函数: GetItemData
// 功能: 获取项关联的数据
// 参数:
//       nIndex --- 索引
// 返回:
//       关联的数据,失败则返回CB_ERR
//---------------------------------------------------------
DWORD_PTR CComboBoxXI::GetItemData(int nIndex) const
{
    CBDataXI* pCBData = (CBDataXI*)CComboBox::GetItemData(nIndex);
    if (pCBData == (CBDataXI*)CB_ERR || pCBData == NULL)
    {
        return CB_ERR;
    }

    return (DWORD_PTR)pCBData->pData;
}

//---------------------------------------------------------
// 函数: SetItemData
// 功能: 设置项关联的数据
// 参数:
//       nIndex     --- 索引
//       dwItemData --- 关联的数据 
// 返回:
//       成功返回CB_OKAY, 失败返回CB_ERR
//---------------------------------------------------------
int CComboBoxXI::SetItemData(int nIndex, DWORD_PTR dwItemData)
{
    CBDataXI* pCBData = (CBDataXI*)CComboBox::GetItemData(nIndex);
    if (pCBData == (CBDataXI*)CB_ERR || pCBData == NULL)
    {
        return CB_ERR;
    }

    pCBData->pData = (LPVOID)dwItemData;
    return CB_OKAY;
}

//---------------------------------------------------------
// 函数: GetItemDataPtr
// 功能: 获取项关联的数据
// 参数:
//       nIndex --- 索引
// 返回:
//       关联的数据,失败则返回CB_ERR
//---------------------------------------------------------
void* CComboBoxXI::GetItemDataPtr(int nIndex) const
{
    CBDataXI* pCBData = (CBDataXI*)CComboBox::GetItemDataPtr(nIndex);
    if (pCBData == (CBDataXI*)CB_ERR || pCBData == NULL)
    {
        return (void*)CB_ERR;
    }

    return pCBData->pData;
}

//---------------------------------------------------------
// 函数: SetItemDataPtr
// 功能: 设置项关联的数据
// 参数:
//       nIndex --- 索引
//       pData  --- 关联的数据
// 返回:
//       成功返回CB_OKAY, 失败则返回CB_ERR
//---------------------------------------------------------
int CComboBoxXI::SetItemDataPtr(int nIndex, void* pData)
{
    CBDataXI* pCBData = (CBDataXI*)CComboBox::GetItemDataPtr(nIndex);
    if (pCBData == (CBDataXI*)CB_ERR || pCBData == NULL)
    {
        return CB_ERR;
    }

    pCBData->pData = pData;
    return CB_OKAY;
}

void CComboBoxXI::DeleteItemData(int nIndex)
{
    CBDataXI* pCBData = (CBDataXI*)CComboBox::GetItemData(nIndex);
    if (pCBData != (CBDataXI*)CB_ERR)
    {
        delete pCBData;
        CComboBox::SetItemData(nIndex, (DWORD_PTR)NULL);
    }
}

void CComboBoxXI::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
    if (lpDrawItemStruct->itemID == -1)
    {
        return;
    }

    CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
    pDC->SetBkMode(TRANSPARENT);
    RECT& rcItem = lpDrawItemStruct->rcItem;

    CString cstrItemText;
    CComboBox::GetLBText(lpDrawItemStruct->itemID, cstrItemText);

    CBDataXI* pData = (CBDataXI*)CComboBox::GetItemDataPtr(lpDrawItemStruct->itemID);
    if (NULL == pData)
    {
        return;
    }

    // 是否处于选中状态
    BOOL bIsSelected = (lpDrawItemStruct->itemAction | ODA_SELECT) 
        && (lpDrawItemStruct->itemState & ODS_SELECTED);

    // 是否处于焦点状态
    BOOL bIsFocused = (lpDrawItemStruct->itemAction | ODA_FOCUS) 
        && (lpDrawItemStruct->itemState & ODS_FOCUS);

    // 绘制背景
    if (bIsSelected)
    {
        COLORREF oldColor = pDC->GetBkColor();
        pDC->FillSolidRect(&rcItem, ::GetSysColor(COLOR_HIGHLIGHT));
        pDC->SetBkColor(oldColor);
    }
    else
    {
        pDC->FillSolidRect(&rcItem, pDC->GetBkColor());
    }

    // 绘制ICON
    CRect rcIcon(rcItem.left, rcItem.top, rcItem.left, rcItem.top);
    if (m_pImageList)
    {
        HICON hIcon = m_pImageList->ExtractIcon(pData->iImageIndex);
        if (hIcon)
        {
            rcIcon.right = rcItem.left + m_imageSize.cx + 2 * CBXI_CX_BORDER;
            rcIcon.bottom = rcItem.top + m_imageSize.cy + 2 * CBXI_CY_BORDER;

            CPoint pos(rcIcon.left + CBXI_CX_BORDER, rcIcon.top + CBXI_CY_BORDER);
            pDC->DrawState(pos, m_imageSize, hIcon, DSS_NORMAL, (CBrush*)NULL);
            ::DestroyIcon(hIcon);
        }
    }

    // 绘制Text
    if (bIsSelected)
    {
        pDC->SetTextColor(::GetSysColor(COLOR_HIGHLIGHTTEXT));
    }
    else
    {
        pDC->SetTextColor(::GetSysColor(COLOR_WINDOWTEXT));
    }
    CRect rcText(rcItem);
    rcText.left = rcIcon.right;
    rcText.top = rcIcon.top;
    pDC->DrawText(cstrItemText, rcText, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_WORDBREAK);
}

void CComboBoxXI::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
    CDC *pDC = GetDC();
    LPCTSTR lpszText = (LPCTSTR)lpMeasureItemStruct->itemData;
    CSize szText = pDC->GetTextExtent(lpszText);
    ReleaseDC(pDC);

    lpMeasureItemStruct->itemHeight = 
        szText.cy > m_imageSize.cy + 2 * CBXI_CY_BORDER ? szText.cy :  m_imageSize.cy + 2 * CBXI_CY_BORDER;

    // 检测是否是DropList
    if (-1 == m_iIsDropList)
    {
        DWORD dwStyle = GetStyle();
        if (DWORD(CBS_DROPDOWNLIST & dwStyle) == (DWORD)CBS_DROPDOWNLIST)
        {
            m_iIsDropList = 1;
        }
        else
        {
            m_iIsDropList = 0;
        }
    }

    // 设置EditCtrl的高度
    if (m_iIsDropList == 1 && lpMeasureItemStruct->itemHeight > m_nEditHeight)
    {
        m_nEditHeight = lpMeasureItemStruct->itemHeight;
        SetItemHeight(-1, m_nEditHeight);
    }
}

BEGIN_MESSAGE_MAP(CComboBoxXI, CComboBox)
END_MESSAGE_MAP()

// CComboBoxXI message handlers
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值