Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

CFontListBox

0.00/5 (No votes)
29 Jan 2006 3  
A ListBox which could display and allow selection of fonts, with special features.

Content

Introduction

First of all, I beg your pardon for any errors in my English and in the content of this article (it's my first submission).

For a personal project (a PropertyList control), I needed a ListBox which could display and allow selection of fonts. After reading several articles on this subject, I decided to write my own control with the following main behaviors:

  • performance during the detection of font type (when I started this work, I had a Pentium 333 Mhz!),
  • can display the font names as standard text or using the font itself,
  • can display the tooltip (tracking the mouse pointer or the selected items) using the sample text or facename,
  • having MRU sublist, shared or not by multiple CFontListBox instances in the application,
  • no dependencies on a bitmap or an ImageList resource file in the project,
  • can be used as a dialog subclassing control or created by hand.

Background

To manage a MRU sublist, shared or not, CFontListBox uses the services offered by an instance of the CFontListManager. Only one instance of CFontListManager is created automatically during the application initialization. This class is responsible for loading the font list, managing the MRU list (private to a CFontListBox instance, shared between two or more controls, or global to the application) through the CFontDescriptor and CFontMruList objects and its various collections.

This control gets the bitmap used to display the list from a bitmap resource of COMDLG32.DLL, so there is no specific resource to be included in your project.

To allow the loading process of the font list, this control does not use the way used by other controls. I tried to distinguish between the font types (TrueType, OpenType and so on). It uses the flags given by the ::EnumFontFamiliesEx API to the callback function (see EnumFontProc and CFontListManager::FillFontList() functions in FontListManager.cpp).

The re-creation of the base ListBox window is needed to allow modifications of the control after its creation to follow the modifications of the CFontListBox style flags.

The persistence of the MRU list needs two functions called in the CWinApp::InitInstance and CWinApp::ExitInstance methods. This is due to the fact that when an instance of CFontListManager is created, the CWinApp::SetCurrentHandles() is not called (this function of the MFC library is responsible for the initialization of the data members of CWinApp object, like m_pszRegistryKey and m_pszProfileName). Persistence acts only on the global MRU list and the custom named ones, if these lists are marked to do so.

CFontListManager has a feature that allows it to react to the system-wide broadcasted message WM_FONTCHANGE. This feature needs a top-level window to receive the message which is created the first time a CFontListBox is created (which can only occur after the CWinApp object and other frame or dialog windows are initialized).

Using the code

All the classes going with the CFontListBox are located in a static library (FontLBLibxx.lib). The library project directory tree has a special structure because I needed (for other projects) a unique place for the include header files and the lib files.

Note: If you have a ListBox without the style flag LBS_NOINTEGRALHEIGHT set, and if you change the height of the items multiple times (for example, adding, and then removing the MRU list), you will see that the ListBox shrinks: it's due to the design!

The list style flags

  • FNTLB_GRAPHIC: The list items are displayed using the corresponding font.
  • FNTLB_ITEMHEIGHTEXPANDED: The spacing between is the same as a ComboBox ListBox.
  • FNTLB_NOTYPEIMAGE: No font image type is displayed before the font facename.
  • FNTLB_MANUAL: Not initialized font ListBox (discussed below).

They are set or modified via the SetFlags or the ModifyFlags functions or with the constructor CFontListBox(DWORD dwStyleFlags, LPCTSTR lpszMruListName=NULL).

When the FNTLB_GRAPHIC flag is set and if the font is SYMBOL, then the item text also contains the facename displayed with the GUI system font (like in the Office 2003 font ComboBox).

Note that not all the symbol fonts are marked SYMBOL, as you can see it in Office 2003 font ComboBox or in the demo program.

The FNTLB_ITEMHEIGHTEXPANDED flag gives the same item spacing as in a ComboBox. If it is not set and there is no MRU list attached, the spacing will be the same as in a Win32 FontDialog.

If the list style flag FNTLB_MANUAL is set, then the ListBox is not initialized with the list of system font resources. This type of CFontListBox is intended to receive and display a list of fonts selected through other means (see in the demo). All the MRU flags are automatically disabled for this type of windows.

The tooltip style flags

The CFontListBox style flags affecting the tooltip window are the following:

  • FNTLB_HAS_TOOLTIPS: The control has tooltip.
  • FNTLB_TOOLTIPTEXT_FONT: The tooltip text is the font facename.
  • FNTLB_TOOLTIPTEXT_SAMPLE: The tooltip text is a sample text (default).
  • FNTLB_TRACKING_TOOLTIP: The tooltip follows the mouse pointer and has a display text corresponding to the font item under it.
  • FNTLB_TOOLTIPSELECTED: The tooltip also displays the list of selected fonts.

They are set or modified via the SetFlags or the ModifyFlags functions, or with the constructor CFontListBox(DWORD dwStyleFlags, LPCTSTR lpszMruListName=NULL).

Note that if the FNTLB_GRAPHIC is set, the tooltip displays the facename (or the sample text) with the GUI system font and, if not, the tooltip displays the text using the corresponding font.

If the FNTLB_TRACKING_TOOLTIP is not set, then the tooltip is only shown when there is one or more selected items.

If the FNTLB_TRACKING_TOOLTIP is set, the tooltip shows the font facename under the mouse pointer and, if the FNTLB_TOOLTIPSELECTED flag is also set, it also shows the list of selected fonts.

The MRU list type flags

You can assign a MRU list to your CFontListBox instance using the style flags:

  • FNTLB_MRULIST: A private MRU list.
  • FNTLB_MRUGLOBALLIST: The application level MRU list.
  • FNTLB_MRUCUSTOMLIST: A named MRU list shared by two or more controls.

Private MRU list

A private MRU list is local to the control: the modifications made on its MRU list are not reflected on other controls.

Custom MRU list

A custom MRU list is intended to be shared by more than one control in the application. It has a name (a string) which identifies it and can be used to refer to it. It is created the first time a control references it, and not destroyed when no more controls are attached to it (feature to be used in CFontComboBox, coming soon ...). All the modifications made via a control are reflected on the other controls attached to the MRU list.

The global MRU list

The global MRU list is a special custom MRU list which is valid when the application runs.

Attaching a control to a MRU list

You can attach a MRU list to your control using the AttachToMruFontList or SetMruFontListName (which are synonyms) functions as follows:

// Attach the control to a private MRU list    

m_listFont1.AttachToMruFontList(NULL);

// Attach the control to a custom MRU list    

m_listFont2.AttachToMruFontList(_T("MruTest"));

// Attach the control to the global MRU list    

m_listFont3.AttachToMruFontList(GLOBAL_MRULIST);

The DetachFromMruFontList() function allows you to detach a control from any type of MRU list previously attached. You do not need to use this function to attach a previously attached control to another MRU list.

You can also do this:

// Attach the control to a private MRU list    

m_listFont1.ModifyFlags(FNTLB_MRUGLOBALLIST | 
         FNTLB_MRUCUSTOMLIST, FNTLB_MRULIST);
// or this :

m_listFont1.SetMruFontListName(NULL);

// Attach the control to a custom MRU list

m_listFont2.SetMruFontListName(_T("MruTest2));    
m_listFont2.ModifyFlags(FNTLB_MRULIST | 
     FNTLB_MRUGLOBALLIST, FNTLB_MRUCUSTOMLIST);
// or, more simple, this :
m_listFont2.SetMruFontListName(_T("MruTest2));

// Attach the control to the global MRU list    

m_listFont3.ModifyFlags(FNTLB_MRULIST | 
  FNTLB_MRUCUSTOMLIST, FNTLB_MRUGLOBALLIST);
// or this :

m_listFont3.SetMruFontListName(GLOBAL_MRULIST);

To use CFontListBox in a CDialog as an auto subclassing control

  • Step 1: Add a ListBox control in your dialog resource using the Resource Editor. Specify the styles you want (?? the Selection mode).
  • Step 2: If not created, create the CDialog derived class corresponding to your dialog resource.
  • Step 3: Add FontListBox.h to your dialog class header.
  • Step 4: Via the ClassWizard, add a member control variable of type CFontListBox.
  • Step 5: Modify (in your dialog implementation file):
    ////////////////////////////////////////////////////
    
    // CPpManual property page
    
    
    IMPLEMENT_DYNCREATE(CPpManual, CPropertyPage)
    
    CPpManual::CPpManual() : CPropertyPage(CPpManual::IDD)
    {
    ...

    as follows, adding or modifying the CFontListBox style flags (where m_listFont1 is the member variable added before, and "MruTest" the name of a shared MRU font list):

    ////////////////////////////////////////////////////
    
    // CPpManual property page
    
    
    IMPLEMENT_DYNCREATE(CPpManual, CPropertyPage)
    
    CPpManual::CPpManual() : CPropertyPage(CPpManual::IDD),
    m_listFont1(FNTLB_HAS_TOOLTIPS | FNTLB_TOOLTIPTEXT_FONT | 
             FNTLB_TRACKING_TOOLTIP | 
             FNTLB_MRUCUSTOMLIST, _T("MruTest"))
    {
    ...
  • Step 6: Add:
    m_listFont1.Initialize();

    in the OnInitDialog function of your dialog class.

To use CFontListBox in a CDialog as a manual subclassing control

Follow the above steps 1 to 3. Then, add a CFontListBox variable (or a CFontListBox* variable, as needed) to your CDialog derived class.

Create the object if needed and use the SubClassDlgItem. Don't forget to call the Initialize() function of CFontListBox.

To dynamically create a CFontListBox control

See how to dynamically add and create a CFontListBox control in the CPpManual::OnInitDialog() function of the sample.

The CFontListBox class

Member functions

Constructors

CFontListBox();
CFontListBox(DWORD dwStyleFlags, 
      LPCTSTR lpszMruListName=NULL);
           // (see above, in "Using the code").

void Initialize();
           // Always needed after the window

           // creation to configurate

           // and fill in the listbox.

Style flags and style

DWORD SetFlags(DWORD dwFlags);
DWORD GetFlags();

BOOL ModifyFlags(DWORD dwRemove, DWORD dwAdd);

BOOL ModifyStyle( DWORD dwRemove, 
         DWORD dwAdd, UINT nFlags = 0 ); 
            // Same as the CWnd similar functions,

            // but needed here to recreate the listbox.

Images

BOOL SetImages(CBitmap* pBitmap);
BOOL LoadImages(LPCTSTR lpszResourceName);
BOOL LoadImages(UINT nIDResource);
           // Allows the control to use

           // a different bitmap image set

           // for the font types.

void SetStdImages();
           // Reset the default bitmap

           // (extract from Comdlg32.dll).

Overridable filter functions

virtual BOOL OnAddItem(CFontDescriptor* lpFont);
// return TRUE (default)

// if the font can be added to the list


virtual BOOL OnAddMruItem(CFontDescriptor* lpFont);
// by default, return the result of OnAddItem

// If these functions returns FALSE,

// the item is not added to the listbox.

MRU relative functions

BOOL AttachToMruFontList(LPCTSTR lpszMruListName);
BOOL SetMruFontListName(LPCTSTR lpszMruListName);
BOOL DetachFromMruFontList();

int AddFontsToMruList(LPCTSTR lpszFontName);
int AddFontsToMruList(CFontDescriptor* lpFont);
int AddFontsToMruList(CStringArray* lpStrFaceNames);
int AddFontsToMruList(CFontDescriptorArray* lpFontArray);
int AddSelectedToMruList();

int RemoveFontsFromMruList(LPCTSTR lpStrFaceName);
int RemoveFontsFromMruList(CFontDescriptor* lpFont);
int RemoveFontsFromMruList(CStringArray* lpStrFaceNames);
int RemoveFontsFromMruList(CFontDescriptorArray* lpFontArray);
int RemoveSelectedFontsFromMruList();

BOOL HasMruList();

void RefreshMruList();
void ClearMruList();
const CString& GetCustomMruListName();

int GetMruFontCount();

BOOL EnableMruListPersistence (BOOL bPersistence = TRUE);
// If you call this function (and the current MRU

// list is not a private one and is valid), 

// then the content of the MRU list will

// not be saved and the key and values are removed 

// immediately from the registry

// (or the .INI file) of the application.         


BOOL IsMruListPersistenceEnabled ();            

BOOL ReloadMruListSettings();
BOOL SaveMruListSettings();
Miscellaneous functions
int GetFontCount();

int GetSelectedCount();
int GetSelectedFontNames(CStringArray* pStrArray);
int GetSelectedFontDescriptors(CFontDescriptorArray* lpArray);
int ClearSelection();

BOOL SubclassDlgItem(UINT nID, CWnd* pParent);
BOOL SubclassWindow(HWND hWnd);

void SetSampleText(LPCTSTR lpszSampleText);
CString& GetSampleText();
// For the tooltip window (see above in the article).

Data members

No public data members.

The CFontDescriptor class

Member functions

CFont* GetFontObject(int nHeight = 0);
    // returns a CFont pointer corresponding

    // to the facename and the height

    // (or default height). Due to the

    // fact the CFontListManager create CFont 

    // objects only when needed,

    // the protected data member m_pFont can be not 

    // initialized. If you want a valid

    // CFont object, use this function.

Data members

// Facename of the font

CString    m_strFaceName;
// Flags describing the font type

// (see possible values in FontListManager.h)

int        m_nFontType;

// The tooltip text is a sample text (default) 

DWORD      m_dwCharSets;
// The index of the image

// corresponding to the font type

int        m_nImage;

Global functions

MRU list persistence functions

void LoadFontListSettings();
void SaveFontListSettings();

BOOL ReloadMruListSettings(LPCTSTR lpszMruListName);
BOOL SaveMruListSettings(LPCTSTR lpszMruListName);

BOOL EnableMruListPersistence (LPCTSTR lpszMruListName, 
                              BOOL bPersistence = TRUE);
// See also the

// CFontListBox::EnableMruListPersistence function. 

// The lpszMruListName parameter

// can be the predefined value GLOBAL_MRULIST. 


BOOL IsMruListPersistenceEnabled (LPCTSTR lpszMruListName);

Installation guide

The source files are divided into two projects: the library project and the demo project.

Library project installation

  1. Unzip the extlib.zip (respecting folder names) on your disk. You must get something like this:

  2. Open the FontControl.dsw project in Visual Studio 6 and recompile all the libraries you need (menu Build->Batch build-> and your choice).
  3. Prepare your development environment by adding xxx/ExtLib/Include and xxx/ExtLib/Lib to the VS6 search directories (menu Tools->Options->Directories).

Now, you are ready to use the CFontListBox class in your program.

Demo project installation

Unzip demo.zip respecting folder names on your disk, open the project file, and recompile it.

Acknowledgements

Many CodeProject contributors will find here some pieces of their code, and I thank them for their (involuntary) help (but I have not noted the references of their articles: please send me a mail if you feel you are a contributor to this code and I shall modify this article with links to your code).

Those I remember are:

History

  • 10th Jan, 2006 - Version 1.0
    • First version.
  • 25th Jan, 2006 - Version 1.1  
    • Fixed a bug when sorting the font names.
    • Fixed a bug when calculating the image index for some printer fonts.
    • Fixed a bug in font enumeration.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here