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

  • Live Event Date: September 10, 2014 @ 11:00 a.m. ET / 8:00 a.m. PT Modern mobile applications connect systems-of-engagement (mobile apps) with systems-of-record (traditional IT) to deliver new and innovative business value. But the lifecycle for development of mobile apps is also new and different. Emerging trends in mobile development call for faster delivery of incremental features, coupled with feedback from the users of the app "in the wild". This loop of continuous delivery and continuous feedback is …

  • The explosion in mobile devices and applications has generated a great deal of interest in APIs. Today's businesses are under increased pressure to make it easy to build apps, supply tools to help developers work more quickly, and deploy operational analytics so they can track users, developers, application performance, and more. Apigee Edge provides comprehensive API delivery tools and both operational and business-level analytics in an integrated platform. It is available as on-premise software or through …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds