[Self-drawn ComboBox] ComboBox with icon

Example image:

download

Source code downloadSample
program download

[The source code] contains two files:
1. ComboBoxXI.h
2. ComboBoxXI.cpp

Instructions for use

provided interface

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);

Instructions:

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);

Precautions:

  1. For DropList, the height of EditCtrl is determined in MeasureItem, and the default height is used for DropDown and Simple.
  2. SetItemHeight, the first item passing -1 indicates that the height of EditCtrl is set.
  3. In DrawItem,

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

The effect is to not draw in EditCtrl if no item is currently selected.

source code

[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 :
     // associated CImageList
    CImageList *m_pImageList;
    CSize      m_imageSize;

    // EditCtrl height 
    unsigned  int   m_nEditHeight;

    // Whether it is a DropList: 
    // -1(to be determined, not detected), 1(Yes), 0(No) 
    // Only set the height of EditCtrl for DropList, and use the default height for DropDown and Simple 
    int m_iIsDropList;

    struct  CBDataXI 
    { 
        // Image serial number, if it is -1, it means no image 
        int iImageIndex;

        // pointer to the associated data
        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 ()
{
}

//------------------------------------------------ --------- 
// Function: SetImageList 
// Function: Set the associated ImageList 
// Parameters: 
// pImageList --- Pointer to ImageList 
// Return: None 
//--------- -------------------------------------------------
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();
}

//------------------------------------------------ --------- 
// Function: GetImageList 
// Function: Get the pointer of the associated ImageList 
// Parameters: None 
// Return: The pointer of the associated ImageList, if there is no associated ImageList, return NULL 
//----- -------------------------------------------------- -- 
CImageList* CComboBoxXI::GetImageList() const
{
    return m_pImageList;
}

//------------------------------------------------ --------- 
// Function: GetItemImage 
// Function: Get the image index of the item 
// Parameters: 
// nIndex --- Item index (starting at 0) 
// Return: 
// Taking 0 as the starting point The starting picture number 
// If there is an error, such as nIndex is out of range, return -1 
// If the combo box item has no associated picture, return -1 
//---------------- -----------------------------------------
int CComboBoxXI::GetItemImage(int nIndex)
{
    CBDataXI* pCBData = (CBDataXI*)CComboBox::GetItemData(nIndex);
    if (pCBData == (CBDataXI*)CB_ERR || pCBData == NULL)
    {
        return -1;
    }

    return pCBData->iImageIndex;
}

//------------------------------------------------ --------- 
// Function: SetItemImage 
// Function: Set the image index of the item 
// Parameters: 
// nIndex --- item index (starting from 0) 
// nImageIndex --- image index ( Starting from 0) 
// bRepaint --- whether to repaint 
// return: 
// success returns 0, failure returns -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;
}

//------------------------------------------------ --------- 
// Function: AddString 
// Function: Add item 
// Parameters: lpszString --- string pointer 
// nImageIndex --- image index (starting at 0) 
// return: new The index value of the added item 
// If it fails, return CB_ERR or 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;
}

//------------------------------------------------ --------- 
// Function: InsertString 
// Function: Insert item at specified position 
// Parameters: 
// nIndex --- specified position 
// lpszString --- string pointer 
// nImageIndex --- Image index (starting at 0) 
// Return: 
// Insert index value 
// If it fails, return CB_ERR or 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;
}

//------------------------------------------------ --------- 
// function: DeleteString 
// function: delete item 
// parameters: 
// nIndex --- index 
// return: 
// return the current number of items left on success 
// fail on failure return CB_ERR 
//-------------------------------------------- -------------
int CComboBoxXI::DeleteString(UINT nIndex)
{
    DeleteItemData(nIndex);

    return CComboBox::DeleteString(nIndex);
}

//------------------------------------------------ --------- 
// Function: ResetContent 
// Function: Clear content 
//---------------------------- -----------------------------
void CComboBoxXI::ResetContent()
{
    int nCnt = CComboBox::GetCount();
    for (int i = 0; i < nCnt; ++i)
    {
        DeleteItemData(i);
    }

    CComboBox::ResetContent();
}

//------------------------------------------------ --------- 
// Function: GetItemData 
// Function: Get the data associated with the item 
// Parameters: 
// nIndex --- Index 
// Return: 
// The associated data, if it fails, return 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;
}

//------------------------------------------------ --------- 
// Function: SetItemData 
// Function: Set item associated data 
// Parameters: 
// nIndex --- index 
// dwItemData --- associated data 
// return: 
// success Return CB_OKAY, return CB_ERR on failure 
//-------------------------------------------- ---------------
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;
}

//------------------------------------------------ --------- 
// Function: GetItemDataPtr 
// Function: Get the data associated with the item 
// Parameters: 
// nIndex --- Index 
// Return: 
// The associated data, if it fails, return 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;
}

//------------------------------------------------ --------- 
// Function: SetItemDataPtr 
// Function: Set item associated data 
// Parameters: 
// nIndex --- index 
// pData --- associated data 
// return: 
// success Return CB_OKAY, otherwise return 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;
    }

    // is it selected
    BOOL bIsSelected = (lpDrawItemStruct->itemAction | ODA_SELECT) 
        && (lpDrawItemStruct->itemState & ODS_SELECTED);

    // Is it in focus
    BOOL bIsFocused = (lpDrawItemStruct->itemAction | ODA_FOCUS) 
        && (lpDrawItemStruct->itemState & ODS_FOCUS);

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

    // draw 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);
        }
    }

    // draw 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;

    // Check if it is a DropList 
    if ( -1 == m_iIsDropList)
    {
        DWORD dwStyle = GetStyle();
        if (DWORD(CBS_DROPDOWNLIST & dwStyle) == (DWORD)CBS_DROPDOWNLIST)
        {
            m_iIsDropList = 1;
        }
        else
        {
            m_iIsDropList = 0;
        }
    }

    // Set the height of 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

Leave a Comment

Your email address will not be published. Required fields are marked *