CCombobox font ownerdraw in Visual C++/MFC


This project illustrates an owner-draw combo box which can show different fonts using CFont. The class uses an abstract object to draw the items. In this case a class derived from the abstract object is created to make the control list of fonts using CFont.
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 ot variable.

virtual void InitLBox(CListBox* pLBox) = 0;

Initializes the control holding the class if it is a CListBox (or derived from it)

virtual void InitCombo(CComboBox* pCombo) = 0;

Initializes the control holding the class if it is a CComboBox (or derived from it)

virtual int AddItemLst(CListBox* pLBox, UINT nItem) = 0;

Adds an item to the control holding the class if it is a CListBox (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)

This class is added to the combo box via it?s method SetDrawC. It must be added before the control is created, so you had better call it in the parent dialog class constructor.The Init() method of the control should be called immediately after it?s creation.Items are 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 and Init. Combo boxes must be created with the following styles: CBS_DROPDOWNLIST, CBS_HASSTRINGS, CBS_OWNERDRAWFIXED or CBS_OWNERDRAWVARIABLE.

In this particular example a combo box listing the system fonts is implemented. The init method does the main font initialization routine. The AddItem method is obsolete and is not to be used for this class.

A class wrapper for the LOGFONT structure is defined for these controls. The LOGFONTX class has an extra variable to store the font type, a copy-constructor and a = operator. The init function of the CFontDrawC class fills the internal list of fonts of the class and then fills the control with the fonts. Notice that it checks if the internal list is filled and if it is doesn?t fill it again. This is done so because getting the system fonts is a relatively time consuming job and it is best to be done as seldom as possible.
The internal list is filled with the Win API function EnumFontFamilies which calls a custom function that actually inserts the fonts in the list, which is passed as a parameter between the functions.The control is filled with the names of the fonts as strings, so FindString and SelectString can be used with these controls. Again SetItemData is used to add a pointer to a LOGFONTX class instance for each font.

After that in the drawing function font is received as a pointer to the LOGFONTX structure and is drawn with that code

LOGFONTX* pLF = ( LOGFONTX* )lpdis->itemData;
CFont font;
font.CreateFontIndirect( pLF );
pDC->SelectObject( &font );

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

CFont font;
font.CreateFontIndirect((LOGFONT*)m_fontCombo.GetItemData(m_fontCombo.GetCurSel()));
m_sampleText.SetFont(&font);

You can see that to get just the pointer to the LOGFONT structure you should write:

(LOGFONT*)m_fontCombo.GetItemData(m_fontCombo.GetCurSel());

Download the Sample Source here. Read here to know the MFC General Owner draw issues & how to solve them.