CCombobox Owner Draw Picture Dialog

   This project using MFC CComboBox class illustrates an owner-draw combo box. The class uses an abstract object to draw the items. This is similar to the combo box owner control color dialog describing how to re-use the derived class for similar functionality.

   In this particular case a class derived from the abstract object is created to make the control list the pictures, but this class can be modified into any other types to list some other objects too.

Parts of the owner draw ccombobox

The abstract class contains methods that are called from the combo box class only. They are

virtual void DrawItem(LPDRAWITEMSTRUCT lpdis, bool bHasFocus) = 0;
 Actually draws the item using the Win32 DRAWITEMSTRUCT.
virtual void MeasureItem(LPMEASUREITEMSTRUCT lpmis) = 0;
 Sets the item size once or every time depending of the owner-draw style: fixed or variable.
virtual void InitCombo(CComboBox* pCombo) = 0;
 Initializes the control holding the class if it is a CComboBox (or derived from it)
virtual int AddItemCmb(CComboBox* pCombo, UINT nItem) = 0;
 Adds an item to the control holding the class if it is a CComboBox (or derived from it)

   Using this class with the CCombobox class should be through the method SetDrawC. It must be added before the control is created. It should be called in the parent dialog class constructor. The derived class of the CComboBox should call this in its constructor.

Using the owner draw CComboBox

   The Init() method of the control should be called immediately after it?s creation. Items should be added with the AddItem function of the control.

   Combo boxes using from this type can be created either dynamically or using the dialog editor and after adding a variable of type CComboBox just rename it with CODrawCombo. Don?t forget to call SetDrawC in the constructor of the combobox class. Combo boxes must be created with the following styles: CBS_DROPDOWNLIST, CBS_HASSTRINGS, CBS_OWNERDRAWFIXED or CBS_OWNERDRAWVARIABLE.

Implementation of the Owner draw combo box:

   In this particular example a combo box listing pictures that are bitmaps in the project?s resources is implemented. The init method is obsolete because there is no initialization needed for this case. The AddItem implementation inserts a not used string in the control and gets the bitmap resource id and from it gets the handle to the bitmap, then it inserts the handle in the item data field of the control. Also two class constants are used to set the size of the pictures (PicSizeX and PicSizeY). When different pictures are going to be used these constants should be changed so that the new pictures fit in the device context.

int CPicDrawC::AddItemCmb(CComboBox* pCombo, UINT nItem)
  {
       int n; n = pCombo->AddString("nu"); 
       HBITMAP hbmp = (HBITMAP)LoadImage(AfxGetApp()->m_hInstance, MAKEINTRESOURCE(nItem), IMAGE_BITMAP, PicSizeX, PicSizeY, 0L);
      pCombo->SetItemData(n, (DWORD)hbmp);
      return n; 
 }

   After that in the drawing function the handle to the bitmap is received as item data and like this:

HBITMAP hbmp = (HBITMAP)lpdis->itemData;
 CDC MemDC;
 MemDC.CreateCompatibleDC(&dc);
  MemDC.SelectObject(hbmp);
 dc.BitBlt(rect.left+5, rect.top+1, PicSizeX, PicSizeY, &MemDC, 0, 0, SRCCOPY);

   When the selection in the combo box is changed an event is raised. We catch this event to update the sample picture box like this:

CRect rect;
 m_samplePic.GetClientRect(rect);
 HBITMAP hbmp = (HBITMAP)m_picCombo.GetItemData(m_picCombo.GetCurSel());
 CDC MemDC, *pDC = m_samplePic.GetDC();
 MemDC.CreateCompatibleDC(pDC);
 MemDC.SelectObject(hbmp); 
 pDC->BitBlt(rect.left+10, rect.top+10, m_picDrawc.PicSizeX, m_picDrawc.PicSizeY, &MemDC, 0, 0, SRCCOPY);

   You can see that to get just the handle to the bitmap currently selected you need to use this code:

(HBITMAP)m_picCombo.GetItemData(m_picCombo.GetCurSel());

   You can download the Source Files and Project files here.