Add a Font Property Page

MFC provides three property page classes - CFontPropPage, CColorPropPage and CPicturePropPage. Unfortunately, these can be used with OLE Automation only. There's also been some discussion on the Usenet news groups about using the 'Choose Font' dialog as a property page. This too is a dead end. You can't use the 'Choose Font' dialog within a property sheet.

The solution is to implement the font property page yourself (or let someone else develop it for you). To allow you to customize the behaviour of the common dialogs, the SDK provides the dialog resources for these dialogs. The resource for the 'Choose Font' dialog is in the FONT.DLG file in the Include directory and the associated symbols can be found in the DLGS.H file. Using these files as a starting point, I created a resource for a Font Property Page and added a CPropertyPage derived class to use this resource. The implementation given below is somewhat limited but still useful. If you need to enhance this, you can look at the source file for CFontPropPage in the MFC source directory.

Step 1: Add the dialog resource

You can either open the rc file listed below in DevStudio and copy the dialog resource to you main resource file or you can include the rc file itself.

To copy the resource, open the 'fontpage.rc' file in DevStudio and expand the resource outline. Then open up the ResourceView for your application so that the dialog resources are visible. Finally drag the IDD_FONTPAGE resource from the 'fontpage.rc' over to your application resources in the ResourceView.

To include the fontpage resource file, copy the files 'fontpage.rc' and 'fontpagerc.h' (listed below) to the 'res' directory under the project directory. Then add the following line to the 'rc2' file already in this directory. The resource image is also shown below.

#include "fontpage.rc"	
Listing of FontPage.rc
/////////////////////////////////////////////////////////////////////////
// Listing of FontPage.rc
//
#include "winresrc.h"
#include "FontPageRc.h"


IDD_FONTPAGE DIALOG DISCARDABLE  13, 54, 264, 133
STYLE WS_CHILD | WS_CAPTION | WS_SYSMENU
CAPTION "Font"
FONT 8, "Helv"
BEGIN
    LTEXT           "&Font:",stc1,6,3,40,9
    COMBOBOX        IDC_FONT,6,13,131,54,CBS_SIMPLE | CBS_AUTOHSCROLL | 
                    CBS_SORT | CBS_DISABLENOSCROLL | WS_VSCROLL | WS_TABSTOP
    LTEXT           "Font St&yle:",stc2,153,3,44,9
    COMBOBOX        IDC_STYLE,153,13,64,54,CBS_SIMPLE | CBS_DISABLENOSCROLL | 
                    WS_VSCROLL | WS_TABSTOP
    LTEXT           "&Size:",stc3,224,3,30,9
    COMBOBOX        IDC_FONTSIZE,224,13,32,54,CBS_SIMPLE | 
                    CBS_DISABLENOSCROLL | WS_VSCROLL | WS_TABSTOP
    GROUPBOX        "Effects",grp1,6,72,84,34,WS_GROUP
    CONTROL         "Stri&keout",IDC_STRIKEOUT,"Button",BS_AUTOCHECKBOX | 
                    WS_TABSTOP,10,82,49,10
    CONTROL         "&Underline",IDC_UNDERLINE,"Button",BS_AUTOCHECKBOX,10,
                    94,51,10
    GROUPBOX        "Sample",grp2,98,72,160,49,WS_GROUP
    CTEXT           "AaBbYyZz",IDC_SAMPLE,104,81,149,37,SS_NOPREFIX
END
/////////////////////////////////////////////////////////////////////////
Listing of FontPageRc.h
/////////////////////////////////////////////////////////////////////////
// Listing of FontPageRc.h
//
#define IDC_STRIKEOUT                   0x0410
#define IDC_UNDERLINE                   0x0411
#define grp1                            0x0430
#define grp2                            0x0431
#define stc1                            0x0440
#define stc2                            0x0441
#define stc3                            0x0442
#define IDC_SAMPLE                      0x0444
#define IDC_FONT                        0x0470
#define IDC_STYLE                       0x0471
#define IDC_FONTSIZE                    0x0472
#define IDD_FONTPAGE                    1543
/////////////////////////////////////////////////////////////////////////

Step 2: Include the source files into your project The listing of the header and implementation files are listed below. Simply, include them into your project and you are all set to use the CFontPage class. The implementation of this class is fairly simple. The CFontPage constructor takes a pointer to a LOGFONT structure. It uses the information in this structure to initialize itself. If a LOGFONT structure is not supplied then the desktop window's font is used. In the OnInitDialog() function, we enumerate through the fonts and populate the 'Font' combobox. Whenever, any of the font characteristic changes, the OnSelChange() function gets called. All the entries in the message map point to this function. The OnSelChange() function updates the internal information and displays a sample text. Listing of FontPage.h
#if !defined(AFX_FONTPAGE_H__DE7EDEB3_056D_11D1_82DF_E2CDC9000000__INCLUDED_)
#define AFX_FONTPAGE_H__DE7EDEB3_056D_11D1_82DF_E2CDC9000000__INCLUDED_

#if _MSC_VER >= 1000
#pragma once
#endif // _MSC_VER >= 1000
// FontPage.h : header file
//


/////////////////////////////////////////////////////////////////////////////
// CFontPage dialog

class CFontPage : public CPropertyPage
{
	DECLARE_DYNCREATE(CFontPage)

// Construction
public:
	void GetCurrentFont(LPLOGFONT lplf);
	CFontPage(LOGFONT* plogfont = NULL);
	~CFontPage();

// Dialog Data
	//{{AFX_DATA(CFontPage)
	enum { IDD = IDD_FONTPAGE };
	CStatic	m_staticSample;
	CComboBox	m_comboStyle;
	CComboBox	m_comboSize;
	CComboBox	m_comboFont;
	BOOL	m_bStrikeOut;
	BOOL	m_bUnderline;
	CString	m_sFont;
	CString	m_sSize;
	CString	m_sStyle;
	//}}AFX_DATA


// Overrides
	// ClassWizard generate virtual function overrides
	//{{AFX_VIRTUAL(CFontPage)
	protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
	//}}AFX_VIRTUAL

// Implementation
protected:
	// Generated message map functions
	//{{AFX_MSG(CFontPage)
	virtual BOOL OnInitDialog();
	afx_msg void OnSelChange();
	//}}AFX_MSG
	DECLARE_MESSAGE_MAP()

private:
	CFont m_fontSample;
	int m_cyPixelsPerInch;
	static int CALLBACK FontEnumProc(ENUMLOGFONTEX *lpelfe, NEWTEXTMETRICEX *lpntme, 
		int FontType, CFontPage* pFontPage );


};

//{{AFX_INSERT_LOCATION}}
// Microsoft Developer Studio will insert additional declarations immediately 
// before the previous line.

#endif // !defined(AFX_FONTPAGE_H__DE7EDEB3_056D_11D1_82DF_E2CDC9000000__INCLUDED_)
Listing of FontPage.cpp
// FontPage.cpp : implementation file
//

#include "stdafx.h"
#include "resource.h"
#include "FontPage.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CFontPage property page

IMPLEMENT_DYNCREATE(CFontPage, CPropertyPage)

CFontPage::CFontPage(LOGFONT* plogfont /*= NULL*/) : CPropertyPage(CFontPage::IDD)
{
	//{{AFX_DATA_INIT(CFontPage)
	//}}AFX_DATA_INIT

	LOGFONT logfont;
	CWindowDC dc(GetDesktopWindow() );
	m_cyPixelsPerInch = GetDeviceCaps(dc, LOGPIXELSY);
	if( plogfont == NULL )
	{
		CFont *pfont = dc.GetCurrentFont();
		pfont->GetLogFont( &logfont );
		plogfont = &logfont;
	}
	m_fontSample.CreateFontIndirect( plogfont );

	m_bStrikeOut = plogfont->lfStrikeOut;
	m_bUnderline = plogfont->lfUnderline;
	m_sFont = plogfont->lfFaceName;
	m_sSize.Format( "%d", MulDiv(plogfont->lfHeight, 72, m_cyPixelsPerInch) );
	m_sStyle = _T("Regular");
	if( plogfont->lfWeight >= 700 && plogfont->lfItalic)
		m_sStyle = _T("Bold Italic");
	else if( plogfont->lfItalic )
		m_sStyle = _T("Italic");
	else if ( plogfont->lfWeight >= 700 )
		m_sStyle = _T("Bold");


	
}

CFontPage::~CFontPage()
{
}

void CFontPage::DoDataExchange(CDataExchange* pDX)
{
	CPropertyPage::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CFontPage)
	DDX_Control(pDX, IDC_SAMPLE, m_staticSample);
	DDX_Control(pDX, IDC_STYLE, m_comboStyle);
	DDX_Control(pDX, IDC_FONTSIZE, m_comboSize);
	DDX_Control(pDX, IDC_FONT, m_comboFont);
	DDX_Check(pDX, IDC_STRIKEOUT, m_bStrikeOut);
	DDX_Check(pDX, IDC_UNDERLINE, m_bUnderline);
	DDX_CBString(pDX, IDC_FONT, m_sFont);
	DDX_CBString(pDX, IDC_FONTSIZE, m_sSize);
	DDV_MaxChars(pDX, m_sSize, LF_FACESIZE);
	DDX_CBString(pDX, IDC_STYLE, m_sStyle);
	//}}AFX_DATA_MAP
}


BEGIN_MESSAGE_MAP(CFontPage, CPropertyPage)
	//{{AFX_MSG_MAP(CFontPage)
	ON_CBN_SELCHANGE(IDC_FONT, OnSelChange)
	ON_CBN_SELCHANGE(IDC_STYLE, OnSelChange)
	ON_CBN_SELCHANGE(IDC_FONTSIZE, OnSelChange)
	ON_BN_CLICKED(IDC_STRIKEOUT, OnSelChange)
	ON_BN_CLICKED(IDC_UNDERLINE, OnSelChange)
	ON_CBN_KILLFOCUS(IDC_FONT, OnSelChange)
	ON_CBN_KILLFOCUS(IDC_STYLE, OnSelChange)
	ON_CBN_KILLFOCUS(IDC_FONTSIZE, OnSelChange)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CFontPage message handlers


BOOL CFontPage::OnInitDialog() 
{
	CPropertyPage::OnInitDialog();
	
	CWindowDC dc(this);
	LOGFONT logfont;
	logfont.lfCharSet = DEFAULT_CHARSET;
	logfont.lfFaceName[0] = '\0';
	logfont.lfPitchAndFamily = 0;
	EnumFontFamiliesEx( dc.m_hDC, &logfont, (FONTENUMPROC)FontEnumProc, 
			(LPARAM) this, 0 );
	
	// Fill Size combobox with "common" sizes
	TCHAR* Defaults[] = { _T("8"), _T("9"), _T("10"), _T("11"), _T("12"), _T("14"),
				_T("16"), _T("18"), _T("20"), _T("22"), _T("24"), _T("26"), 
				_T("28"), _T("36"), _T("48") };
	for (int i = 0; i < (sizeof(Defaults)/sizeof(Defaults[0])); i++)
		m_comboSize.AddString(Defaults[i]);

	// Fill Style combobox with "common" styles
	m_comboStyle.AddString( _T("Regular") );
	m_comboStyle.AddString( _T("Bold") );
	m_comboStyle.AddString( _T("Italic") );
	m_comboStyle.AddString( _T("Bold Italic") );


	return TRUE;  // return TRUE unless you set the focus to a control
	              // EXCEPTION: OCX Property Pages should return FALSE
}

int CALLBACK CFontPage::FontEnumProc(ENUMLOGFONTEX *lpelfe, NEWTEXTMETRICEX *lpntme, 
		int FontType, CFontPage* pFontPage )
{

	if( pFontPage->m_comboFont.FindStringExact( 0, (LPCTSTR)lpelfe->elfFullName ) == 
					CB_ERR )
	{
		// Add to list
		pFontPage->m_comboFont.AddString( (LPCTSTR)lpelfe->elfFullName );
	}

 	return 1;
}

void CFontPage::OnSelChange() 
{
	// The selection hasn't changed yet, so change it
	if( IsChild( GetFocus() ) && 
			GetFocus()->GetParent()->IsKindOf( RUNTIME_CLASS( CComboBox ) ) )
	{
		CComboBox *cb = (CComboBox *)GetFocus()->GetParent();
		CString sText;
		if( cb->GetCurSel() != CB_ERR )
		{
			cb->GetLBText( cb->GetCurSel(), sText );
			cb->SetWindowText( sText );
		}
	}

	UpdateData(TRUE);
	
	LOGFONT logfont;
	m_fontSample.GetLogFont( &logfont );
	logfont.lfStrikeOut = m_bStrikeOut;
	logfont.lfUnderline = m_bUnderline;
	memcpy( logfont.lfFaceName, m_sFont, LF_FACESIZE );

	logfont.lfHeight = MulDiv(atoi(m_sSize), m_cyPixelsPerInch, 72);
	 
	logfont.lfWeight = 400;			//Regular
	logfont.lfItalic = FALSE;
	if( m_sStyle.Find( _T("Italic") ) != -1 )
		logfont.lfItalic = TRUE;
	if( m_sStyle.Find( _T("Bold") ) != -1 )
		logfont.lfWeight = 700;

	m_fontSample.DeleteObject();
	m_fontSample.CreateFontIndirect( &logfont );
 	m_staticSample.SetFont(&m_fontSample);	
}

void CFontPage::GetCurrentFont(LPLOGFONT lplf)
{
	m_fontSample.GetLogFont( lplf );
}



Comments

  • XP style tab controls/ property pages

    Posted by Legacy on 09/26/2003 12:00am

    Originally posted by: Junaid

    sir i m using tab controls in my application....what shd i do to make them look like a windowsXP style tab controls.
    (means as the tabs appear in window XP, not like window98)
    if u can help me in this regard, it will be gr8....
    thanx

    Reply
  • changing caption of a dialog box thru code

    Posted by Legacy on 12/30/2002 12:00am

    Originally posted by: Rekha

    someone please help me to change the caption of a dialog box through vc code. or to uncheck the title bar property of the same.

    Reply
  • Thank You

    Posted by Legacy on 06/21/2002 12:00am

    Originally posted by: John R. Shaw

    Thank you very much, this is just what I was looking for.

    Reply
  • How to add the user-defined PropertyPage ....

    Posted by Legacy on 04/17/2001 12:00am

    Originally posted by: jaejin


    Hi!
    I maked a propertypage. This page uses to change
    the mouse's property.
    Mouse PropertySheet is in the Control panel,
    which named Main.cpl
    I want to add this page wthin Main.cpl.
    How to to it...?

    Reply
  • Its a beautiful thing!

    Posted by Legacy on 02/19/2001 12:00am

    Originally posted by: Craig MacDonald

    compiled and linked the first time. I'm impressed. it was just what I needed. I was almost to the point of writing this myself. CFontPage is something that MS should have done in the first place. Thanks -cem

    Reply
  • Excellent work

    Posted by Legacy on 11/23/2000 12:00am

    Originally posted by: Sally Mendoza

    Just what I needed. Together with the Enhancement this is a wonderful piece of code.
    
    

    Thanks.

    Reply
  • Enhancement

    Posted by Legacy on 11/16/2000 12:00am

    Originally posted by: Jon Roch-Berry

    It would be nice if the selections of all the controls were initialised to the values of the passed-in LOGFONT.
    
    

    Here's my code that does it:

    I've altered OnInitDialog to this:

    BOOL CFontPage::OnInitDialog()
    {
    CPropertyPage::OnInitDialog();

    // Populate combo box controls and set their default values to be the
    // same as the current.

    CWindowDC dc(this);
    LOGFONT logfont;
    logfont.lfCharSet = DEFAULT_CHARSET;
    logfont.lfFaceName[0] = '\0';
    logfont.lfPitchAndFamily = 0;
    EnumFontFamiliesEx( dc.m_hDC, &logfont, (FONTENUMPROC)FontEnumProc,
    (LPARAM) this, 0 );

    LOGFONT lf;
    GetCurrentFont(&lf);
    const int currentPointSize = MulDiv(lf.lfHeight, 72, m_cyPixelsPerInch);

    // Fill Size combobox with "common" sizes
    TCHAR* Defaults[] = { _T("8"), _T("9"), _T("10"), _T("11"), _T("12"), _T("14"),
    _T("16"), _T("18"), _T("20"), _T("22"), _T("24"), _T("26"),
    _T("28"), _T("36"), _T("48") };

    for (int i = 0; i < (sizeof(Defaults)/sizeof(Defaults[0])); i++)
    {
    int pos = m_comboSize.AddString(Defaults[i]);

    // Set the current selection
    if (currentPointSize == atoi(Defaults[i]))
    m_comboSize.SetCurSel(pos);
    }

    // Fill Style combobox with "common" styles
    m_comboStyle.AddString( _T("Regular") );
    m_comboStyle.AddString( _T("Bold") );
    m_comboStyle.AddString( _T("Italic") );
    m_comboStyle.AddString( _T("Bold Italic") );


    // Make sure current selections are correct
    int indexCS = m_comboStyle.FindStringExact(-1, m_sStyle);
    if (CB_ERR != indexCS)
    m_comboStyle.SetCurSel(indexCS);

    int indexFS = m_comboFont.FindStringExact(-1, lf.lfFaceName);
    if (CB_ERR != indexFS)
    m_comboFont.SetCurSel(indexFS);

    OnSelChange(); // ensure sample text is updated

    return TRUE; // return TRUE unless you set the focus to a control
    // EXCEPTION: OCX Property Pages should return FALSE
    }

    Reply
  • What about a CFilePropPage object?

    Posted by Legacy on 07/22/1999 12:00am

    Originally posted by: ABBAS Mourad

    Hello, 
    
    I'm trying to create a kind of CFilePropPage object. In fact, it consists on puting a window derived from CFileDialog inside a PropertyPage. You could call the AddPage() methode of a PropertySheet to obtain esealy a nice browser inside your PropertySheet... Unfortunatly, I didn't succed.
    I'm wondering if it's possible or not do that? Do you know how can I do that. Is there an MFC class that offers you those fonctionalities for a PropertyPage?

    Thanks.
    Mourad ABBAS.
    22/07/99 Paris

    Reply
  • What about Print Setup Dialog?

    Posted by Legacy on 07/18/1999 12:00am

    Originally posted by: Cody Mack

    Can you help me with adding a Print Setup Dialog into a Property Sheet?

    Reply
  • Updating Sample Text

    Posted by Legacy on 07/09/1999 12:00am

    Originally posted by: TC

    How do I keep the sample text from being changed back to "AaBbYyZz" by MFC whenever I select a new font?

    I've managed to initialize the font dialog with the desired font and size selected, as well as a predefined string in the sample text CTEXT box.

    I've tried subclassing the control (correctly, I think),
    I've tried trapping the WM_SETTEXT and WM_GETTEXT messages sent to this control (no success).

    Any ideas?

    TC

    Reply
  • Loading, Please Wait ...

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: August 20, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT When you look at natural user interfaces as a developer, it isn't just fun and games. There are some very serious, real-world usage models of how things can help make the world a better place – things like Intel® RealSense™ technology. Check out this upcoming eSeminar and join the panel of experts, both from inside and outside of Intel, as they discuss how natural user interfaces will likely be getting adopted in a wide variety …

  • Event Date: April 15, 2014 The ability to effectively set sales goals, assign quotas and territories, bring new people on board and quickly make adjustments to the sales force is often crucial to success--and to the field experience! But for sales operations leaders, managing the administrative processes, systems, data and various departments to get it all right can often be difficult, inefficient and manually intensive. Register for this webinar and learn how you can: Align sales goals, quotas and …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds