Click here to Skip to main content
Click here to Skip to main content

Font Combo

, , 7 Jul 2003
Rate this:
Please Sign up or sign in to vote.
An MFC-based Font selection and preview combo

Sample Image - FontCombo.gif

Introduction

This control gives you a font selection combo with font preview and optional sample text. It also features self-adjusting drop-width, to handle any size font name and sample text.

Updates

Dave Schumann has updated Chris' font selector dialog control. It was almost exactly what he needed. It lacked, however, an easy way to sync it up with data, so he added a simple DDX function for data exchange. Performance was also improved by creating the fonts once and caching them in a map (Chris' version creates a font every time a list box item needs to be drawn).

Usage

Original version:

  1. Add the following files to your workspace:
    1. FontPreviewCombo.cpp
    2. FontPreviewCombo.h
    3. res/Font_tru.bmp - make sure this resource is called IDB_TTF_BMP. This is the TrueType Font glyph.
  2. Add a combobox to your dialog.
  3. Make sure the following styles are selected:
    1. Type: Drop List
    2. Owner draw: Variable
    3. Has Strings (yes)
    4. Sorting is OK. It will sort on font name.
  4. Add a member of type CFontPreviewCombo to your dialog and attach it to your control.
  5. Set the combo style, font size, sample text, etc..
  6. In your dialog's OnInitDialog, call the Init function of the CFontPreviewCombo. This will load the fonts into the control.

Dave Schumann's updated version

  1. Set the combo style, font size, sample text, etc.
    Now that fonts are pre-made, font size and combo style are no longer public members; instead, they have Get and Set accessors. The Set accessors, by default, re-initialize the control, since changing these variables requires re-making the fonts. IF you're going to initialize the control yourself, you can disable this automatic behavior (so as to avoid unnecessary reinitialization). For example:

        m_fontCombo.SubclassDlgItem (IDC_FONTCOMBO, this);
        m_fontCombo.m_csSample = "No Fate But What We Make";
        m_fontCombo.SetFontHeight (19, false);
        m_fontCombo.SetPreviewStyle (CFontPreviewCombo::SAMPLE_ONLY, false);
        m_fontCombo.Init();
    

    The second argument to SetFontHeight() and SetPreviewStyle() is an optional bool that defaults to true and specifies whether to reinitialize the dialog. There's only a benefit to setting it to false if you're going to set both font height AND preview style, in which case you should do both and then call Init(). (Note that if you only set one, you can simply omit the second argument as well as your own call to Init(). If you call neither accessor you need to call Init() yourself.)

  2. If it wasn't done above, then in your dialog's OnInitDialog(), call the Init() function of the CFontPreviewCombo. This will load the fonts into the control. You should call the base class method (CDialog::OnInitDialog()) after initializing the font combo box control.
  3. In this version of the control, you can add code to your override of the DoDataExchange() member function to handle getting and setting the font facename. If you create a member m_fontFaceName, then you can add a call like this in DoDataExchange() after the AFX calls:
    DDX_FontPreviewCombo (pDX, IDC_FONTCOMBO, m_fontFaceName);
    

    When the dialog is displayed, this will select the font that corresponds to the value of m_fontFaceName, if such a font exists. When the dialog is closed, m_fontFaceName will contain the selected font's name.

How it works

This is a very basic owner-drawn CComboBox. In MFC, an "owner-drawn" combo box means it handles two functions: MeasureItem and DrawItem.

MeasureItem

MeasureItem is called once per item, and it's where you provide the height of each list item. This way, Windows knows how large to make the drop list and where to draw your item within the drop list. In this case, we're allowing each item to be a different height (that's the Owner Draw : Variable part, above). We're also measuring the width of each item, so we can tell how wide we need to make the drop list (we'll get to that in OnDropDown)

DrawItem

This is a bit more complicated than MeasureItem, but not much. DrawItem is called each time Windows needs to draw an individual item in the control. A set of flags are passed in that describe the item itself and what kind of drawing we should do (focused, disabled, edit only, drop list, etc.). All you need to do is draw the item in the rectangle provided, in the right style. So, we draw the TTF glyph if we need to, then we draw the font name and sample text in the style that the user has asked for (font + sample, no sample, etc). Again, this happens once for each item - you only draw one item at a time.

Other info

The rest of the code in the class handles getting the list of fonts, and setting the drop list width:

EnumFonts

This function tells Windows to give you the list of available fonts. You provide a callback function with a specific signature and Windows will call that function once per font. We use the callback as a place to do an AddString into the combo, to add the font name and type for each font. FYI, each of these AddString calls will result in one call to MeasureItem.

OnDropDown

This is the last little bit of interesting code. This gets called when you hit the little drop-down arrow. In this function, we determine how wide we need our drop list to be. We want it to be wide enough to hold the largest item, based on the style. When we know how wide we want it to be, we call CComboBox::SetDroppedWidth(nWidth) to tell Windows that out list should be nWidth units wide. FYI, it will always be at least as wide as the edit control, but sometimes we need to make it wider.

Credits

This is an adaptation of "A WTL-based Font preview combo box", by Ramon Smits. I converted from WTL to MFC, added the sample text, the preview styles, colors, self-expanding drop list, variable text height, etc..

As always, enjoy responsibly.

History

March 25th, 2002 - First release.
March 26th, 2002 - Function desciptions.
July 8th, 2003 - updated by Dave Shumann 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Authors

Chris Losinger
Software Developer
United States United States
Chris Losinger is the president of Smaller Animals Software, Inc..

Dave Schumann

United States United States
No Biography provided

Comments and Discussions

 
QuestionHow to Change Font on ComboBox Control PinmemberJoerg Tiedemann23-Mar-13 5:50 
AnswerRe: How to Change Font on ComboBox Control PinmemberChris Losinger23-Mar-13 5:52 
AnswerRe: How to Change Font on ComboBox Control PinmemberJoerg Tiedemann26-Mar-13 12:55 
QuestionError in CFontPreviewCombo::AddFont Pinmemberaragornx14-Mar-13 1:49 
AnswerRe: Error in CFontPreviewCombo::AddFont PinmemberChris Losinger14-Mar-13 2:13 
GeneralMy vote of 5 Pinmembermanoj kumar choubey20-Feb-12 1:49 
Answererror C2664: 'strlen' PinmemberJD8111-Dec-07 17:34 
GeneralNot working properly in Visual Studio 2003 or 5 [modified] Pinmemberhowardive21-Oct-06 3:21 
GeneralDroplist and ScrollWheel Pinmemberc-sharp11-Jan-06 18:08 
GeneralRe: Droplist and ScrollWheel PinmemberMichael Gunlock27-Jan-06 18:31 
GeneralMemory leak while runing with Visual Studio NET 2003 PinmemberDarius Mikalauskas28-May-04 1:58 
GeneralRe: Memory leak while runing with Visual Studio NET 2003 PinmemberChris Losinger28-May-04 2:26 
i can't reproduce it, so i'm guessing it's because the font's are being placed in a map based on their name, and if it turns out you have two fonts with the same face name you end up losing the pointer to the first one.
 
this might fix it:
 
void
CFontPreviewCombo::AddFont (const CString& faceName)
{
	if (m_style != NAME_GUI_FONT)
	{
		CFont* cf = new CFont;
		BOOL createFontResult = 
			cf->CreateFont(m_iFontHeight,0,0,0,FW_NORMAL,FALSE, FALSE, 
			FALSE,DEFAULT_CHARSET,OUT_DEFAULT_PRECIS,
			CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,DEFAULT_PITCH, faceName);
		_ASSERTE (createFontResult);
 
// changes start here....
		CFont *pDummy;
		BOOL lookupResult = m_fonts.Lookup (faceName, (void*&)pDummy) != NULL;
 
		int i = 2;
		CString originalFaceName = faceName;
		CString newFaceName = faceName;
		while (lookupResult)
		{	
			newFaceName.Format("%s [%d]", originalFaceName, i);
			lookupResult = m_fonts.Lookup (newFaceName , (void*&)pDummy) != NULL;
			i++;
		}
 
		m_fonts.SetAt(newFaceName, cf);
// changes end here....

	}
}
 
note that this only applies to the updated code, as my original code doesn't use a map.
 
Cleek / Software
GeneralRe: Memory leak while runing with Visual Studio NET 2003 PinmemberDarius Mikalauskas28-May-04 2:38 
GeneralRe: Memory leak while runing with Visual Studio NET 2003 PinmemberDave Schumann28-May-04 6:31 
GeneralRefreshing the list and other things... Pinmemberslsmith000030-Nov-03 17:52 
GeneralControling Combo Client size (edit box+ dropdown arrow) PinmemberYonatan10-Nov-03 3:41 
GeneralRe: Controling Combo Client size (edit box+ dropdown arrow) PinmemberDave Schumann10-Nov-03 10:12 
GeneralRe: Controling Combo Client size (edit box+ dropdown arrow) PinmemberYonatan10-Nov-03 10:29 
GeneralProblem in combo box as ActiveX Ctrl PinmemberTRZ18-Jul-03 2:53 
GeneralA problem with Property Sheet PinsussChe-Bin Liu13-Dec-02 12:00 
GeneralError PinsussAnonymous5-Aug-02 15:13 
GeneralRe: Error PinmemberChris Losinger5-Aug-02 15:40 
GeneralRe: Error PinsussAnonymous5-Aug-02 19:20 
GeneralRe: Error PinmemberChris Losinger5-Aug-02 19:39 
GeneralBuffering combo items PinmemberTomasz Sowinski26-Mar-02 23:38 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140721.1 | Last Updated 8 Jul 2003
Article Copyright 2002 by Chris Losinger, Dave Schumann
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid