Custom Font in Property Sheets

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.



About the Author

Ovidiu Cucu

Graduated at "Gh. Asachi" Technical University - Iasi, Romania. Programming in C++ using Microsoft technologies since 1995. Microsoft MVP awardee since 2006. Moderator and article reviewer at Codeguru.com, the number one developer site. Co-founder of Codexpert.ro, a website dedicated to Romanian C++ developers.

Downloads

Comments

  • Font is not set properly in CMFCPropertySheet in Window 8.1 Japanese OS

    Posted by Mysoon N S on 01/06/2014 03:55am

    In Windows 8.1 Japanese OS,font of pages is not correct. We have overridden BuildPropPageArray() and set the font as you described. But it is not working. In this application, we used CMFCPropertySheet and CMFCPropertyPage instance. It seems that even after setting the font, font of pages was Meiryo UI itself. We also tried call back function too. It seems that it is also not working. Can you please help me to sort out the problem?

    Reply
  • coments

    Posted by James on 09/12/2012 04:12pm

    THis procedure is wonderful. It works well with my Visual Studio 2005 (C++). Now i can easy modify the font size. Thanks a lot.

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

Top White Papers and Webcasts

  • Today's agile organizations pose operations teams with a tremendous challenge: to deploy new releases to production immediately after development and testing is completed. To ensure that applications are deployed successfully, an automatic and transparent process is required. We refer to this process as Zero Touch Deployment™. This white paper reviews two approaches to Zero Touch Deployment--a script-based solution and a release automation platform. The article discusses how each can solve the key …

  • Learn How A Global Entertainment Company Saw a 448% ROI Every business today uses software to manage systems, deliver products, and empower employees to do their jobs. But software inevitably breaks, and when it does, businesses lose money -- in the form of dissatisfied customers, missed SLAs or lost productivity. PagerDuty, an operations performance platform, solves this problem by helping operations engineers and developers more effectively manage and resolve incidents across a company's global operations. …

Most Popular Programming Stories

More for Developers

RSS Feeds