Custom Font in Property Sheets

CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

Introduction

Usually, it’s not needed and not a very good idea to change the default font in property sheets.
However, sometimes that may be a requirement. Let’s say, one client wants some particular corporate font
or simply we must use very large fonts in an application for kids or for grandpa.
That’s it, we have to solve the problem and change the font.

First attempts

Some people could say: “That’s piece of cake! All we have to do is to use the resource editor
and set the font in each dialog template used to create the property pages”.

It doesn’t work. The Windows API which creates the property sheet uses a font from a pre-defined resource located in
a library that we cannot control. Further, the MFC framework sets the same font for each property page, ignoring the font
we have set in the resource editor.

Other people may propose another solution: “Let Windows and MFC to create the property sheet
as they want, then change the font at runtime.”


Indeed, that may be a solution but not a very handy one. Besides changing the font for each control in
property sheet and in each property page, there is a lot to do for resizing these controls, for resizing the property pages, as
well as property sheet itself.

A better solution

For creating a property sheet a PROPSHEETHEADER structure is used.
We can set in pfnCallback member of this structure the address of an application-defined callback function.
Additionally, we have to add PSH_USECALLBACK flag to dwFlags.
The callback function is called before property sheet is being created and we can change here the
font face name and size in a DLGTEMPLATE structure.

   // psh is a PROPSHEETHEADER structure used to create the property sheet
   psh.pfnCallback = PropSheetProc;
   psh.dwFlags |= PSH_USECALLBACK;
int CALLBACK PropSheetProc(HWND hWndDlg, UINT uMsg, LPARAM lParam)
{
   switch(uMsg)
   {
   case PSCB_PRECREATE: // property sheet is being created
      {
         DLGTEMPLATE* pResource = (DLGTEMPLATE*)lParam;
         // modyfy the font face name and size in pResource.
      }
      break;
   }
   return 0;
}

Modifying the font in pResource is not a quite easy task because it points to a DLGTEMPLATE followed by
variable-length fields and an array of DLGITEMTEMPLATE structures, aligned on a DWORD boundary,
each one also followed by variable-length fields.
Besides, it’s possible to have the “EX” versions of these structures.
Fortunately, the MFC framework internally uses a class named CDialogTemplate (defined in AFXPRIV.H)
which takes care of all of these. By using this class, changing the font face name and size becomes like a walking in the park.
You’ll see it a little bit later in the implementation of CCBPropertySheet.

Now we have solved the font problem of property sheet buttons (“OK”, “Cancel”, “Apply”, etc)
as well as tab controls text. Next, we have to do something with property pages.
My first attempt was to follow the same pattern: “Modify the PROPSHEETPAGE structure to
set an application-defined callback function for changing the property page font”
.
That may work but not always. Depending on the target OS and the MFC framework version
the callback routine may be called too late and the property sheet is not
correctly resized. After few cigarettes and cups of coffee, I found a place to make it
properly by overriding CPropertySheet::BuildPropPageArray. You can see it also in the CCBPropertySheet
class implementation.

CCBPropertySheet class

CCBPropertySheet is derived from MFC class CPropertySheet and adds custom font functionality.
As promised above, here is the implementation of the callback function:

int CALLBACK CCBPropertySheet::PropSheetProc(HWND hWndDlg, UINT uMsg, LPARAM lParam)
{
   switch(uMsg)
   {
   case PSCB_PRECREATE:
      {
         if((m_wFontSize > 0) && (NULL != m_pszFontFaceName))
         {
            LPDLGTEMPLATE pResource = (LPDLGTEMPLATE)lParam;
            CDialogTemplate dlgTemplate(pResource);
            dlgTemplate.SetFont(m_pszFontFaceName, m_wFontSize);
            memmove((void*)lParam, dlgTemplate.m_hTemplate, dlgTemplate.m_dwTemplateSize);
         }
      }
      break;
   }
   return 0;
}

And here is the overridden BuildPropPageArray:

void CCBPropertySheet::BuildPropPageArray()
{
   CPropertySheet::BuildPropPageArray();

   if((m_wFontSize > 0) && (NULL != m_pszFontFaceName))
   {
      LPCPROPSHEETPAGE ppsp = m_psh.ppsp;
      const int nSize = static_cast(m_pages.GetSize());

      for(int nPage = 0; nPage < nSize; nPage++)
      {
         const DLGTEMPLATE* pResource = ppsp->pResource;
         CDialogTemplate dlgTemplate(pResource);
         dlgTemplate.SetFont(m_pszFontFaceName, m_wFontSize);
         memmove((void*)pResource, dlgTemplate.m_hTemplate, dlgTemplate.m_dwTemplateSize);

         (BYTE*&)ppsp += ppsp->dwSize;
      }
   }
}

Finally, two examples of using CCBPropertySheet. First creates a modal property sheet with default font, while the second one creates a modal property sheet with a custom font (Arial Bold, size 20).

   // CMyPropertySheet is derived from CCBPropertySheet
   CMyPropertySheet pps(_T("Property Sheet with default font"), this);
   // creates a modal property sheet with default font
   pps.DoModal();

   // CMyPropertySheet is derived from CCBPropertySheet
   CMyPropertySheet pps(_T("Property Sheet with Arial Bold 20"), this);
   // creates a modal property sheet with custom font
   pps.DoModal(_T("Arial Bold"), 20);

You can find the whole code of CCBPropertySheet in the source files CBPropertySheet.h and CBPropertySheet.cpp, attached to this article.

Demo application

The demo application is a simple MFC dialog-based application which demonstrates how to use CCBPropertySheet
class for creating property sheets with custom fonts.

Select the font size and font face name from the list then push the “Show…” button to create a modal property sheet
that may look like this one:

Final notes

I have built the demo application with Visual C++ 6.0 and Visual Studio 2005 and have tested it under Windows XP, Vista, and Windows 7.
If anybody encounters problems, please do not hesitate to send a feedback.

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read