FIX: CPropertySheet::AddPage Won't Display New Page
  
PSS ID Number: Q148134
Article last modified on 03-13-1996
 
4.00
 
WINDOWS NT
 

-----------------------------------------------------------------------
The information in this article applies to:
 
 - The Microsoft Foundation Classes (MFC) included with:
   Microsoft Visual C++, 32-bit Edition, version 4.0
-----------------------------------------------------------------------
 
SYMPTOMS
========
 
If CPropertySheet::AddPage() is called to add a property page containing an
OLE control after the property sheet has been displayed, then when the user
clicks the tab for the new property page, the tab disappears and the
previous page is activated.
 
CAUSE
=====
 
Usually, pages are added to a property sheet before DoModal() is called.
When DoModal() is called, a helper function is called by the framework
which parses the dialog templates of the existing pages. This processing
removes entries for OLE controls and instantiates the OLE controls ahead of
time. It then replaces the dialog templates associated with each page with
a "sanitized" version containing no OLE control entries. This processing
does not take place if a page is added after DoModal() is called.
Therefore, the dialog template for the new page contains OLE control
entries that the property sheet control does not understand and hence it
refuses to display the page.
 
RESOLUTION
==========
 
To work around this, the dialog template for the dynamically added page
needs to be processed to remove OLE control entries. There is no strait
forward way to do this without either writing the code from scratch or
borrowing code from the MFC 4.0 implementation-specific source. Although
not recommended, using the MFC 4.0 source code is probably the easiest way.
The "Sample Code" section of this article demonstrates how to add a method
to a CPropertySheet-derived class that will process a dialog template
correctly if the template contains OLE controls.
 
STATUS
======
 
Microsoft has confirmed this to be a bug in the Microsoft products listed
at the beginning of this article. This problem was corrected in Visual C++
4.1. The dialog template processing code has been moved to the AddPage
method so that the processing occurs regardless of when the page is added
to the propertysheet.
 
MORE INFORMATION
================
 
Sample Code to Demonstrate How to Resolve the Problem
------------------------------------------------------
 
The following sample code should be added to the implementation file
of the CPropertySheet derived class.
 
// The following header files are needed to provide the declarations
// of the MFC implementation-specific helpers and classes used below.
// The actual path to the Afximpl.h file may be different from that
// shown below and will depend on where Visual C++ was installed.
 
#include "c:\msdev\mfc\src\afximpl.h"
#include <afxpriv.h>
 
//////////////////////////////////////////////////////////////////////
//
// The dialog template processing code used below is taken directly
// from CPropertySheet::BuildPropPageArray, which provides this
// functionality normally.
//
// The _tChangePropPageFont() function is a utility function taken
// from the Sheetprop.cpp module. It is used by BuildPropPageArray.
//
 
static DLGTEMPLATE* _tChangePropPageFont(const DLGTEMPLATE* pTemplate,
        BOOL bWizard)
{
    CString strFaceDefault;
    WORD wSizeDefault;
 
    if (!AfxGetPropSheetFont(strFaceDefault, wSizeDefault, bWizard))
        return NULL;
 
    // set font of property page to same font used by property sheet
    CString strFace;
    WORD wSize;
 
    if ((!CDialogTemplate::GetFont(pTemplate, strFace, wSize)) ||
        (strFace != strFaceDefault) || (wSize != wSizeDefault))
    {
        CDialogTemplate dlgTemplate(pTemplate);
        dlgTemplate.SetFont(strFaceDefault, wSizeDefault);
        return (DLGTEMPLATE*)dlgTemplate.Detach();
    }
 
    return NULL;
}
 
//////////////////////////////////////////////////////////////////////
//
// In the following code, CMyPropertySheet is assumed to be a class
// derived from CPropertySheet. AddPageLater should only be used
// to handle the situation where a property page containing an OLE
// control is being added to a propertysheet after DoModal has been
// called and the sheet is being displayed. Otherwise, the stock
// CPropertySheet::AddPage() function should be used.
//
// In the default MFC implementation, the CPropertySheet class is
// declared as a friend of the CPropertyPage class to allow it access
// to the protected CPropertyPage members. Access to these protected
// members is necessary with the sample code shown below. To allow the
// derived CPropertySheet class to have access to the same protected
// members, it must be declared as a friend of the CPropertyPage derived
// class that implements the property page containing the OLE control(s).
//
// For example, if CMyOCXPropertyPage is a CPropertyPage derived
// class, it must declare CMyPropertySheet as a friend:
//
//     class CMyOCXPropertyPage : public CPropertyPage
//     {
//         // Other class declaration stuff goes here
//
//         friend class CMyPropertySheet;
//     };
//
 
void CMyPropertySheet::AddPageLater(CMyOCXPropertyPage* pPage)
{
    HRSRC hResource = ::FindResource(pPage->m_psp.hInstance,
        pPage->m_lpszTemplateName, RT_DIALOG);
    HGLOBAL hTemplate = LoadResource(pPage->m_psp.hInstance,
        hResource);
    LPCDLGTEMPLATE pTemplate = (LPCDLGTEMPLATE)LockResource(hTemplate);
 
#ifdef _MAC
    // Still need to unlock and free resources on the Mac
    // (using "borrowed" space in CDialog to store handle)
    if (pPage->m_lpDialogTemplate != NULL)
    {
        UnlockResource((HGLOBAL)pPage->m_lpDialogTemplate);
        FreeResource((HGLOBAL)pPage->m_lpDialogTemplate);
    }
 
    if (hTemplate != NULL)
        (HGLOBAL&)pPage->m_lpDialogTemplate = hTemplate;
#endif //_MAC
 
    ASSERT(pTemplate != NULL);
 
    // WINBUG: Windows currently does not support DIALOGEX resources!
    // Assert that the template is not a DIALOGEX template.
    // DIALOGEX templates are not supported by the PropertySheet API.
 
    // To change a DIALOGEX template back to a DIALOG template,
    // remove the following:
    //
    //   - Extended styles on the dialog
    //   - Help IDs on any control in the dialog
    //   - Control IDs that are DWORDs
    //   -  Weight, italic, or charset attributes on the dialog's font
    //
    ASSERT(((DLGTEMPLATEEX*)pTemplate)->signature != 0xFFFF);
 
#ifndef _AFX_NO_OCC_SUPPORT
    // if the dialog could contain OLE controls, deal with them now
    if (afxOccManager != NULL)
        pTemplate = pPage->InitDialogInfo(pTemplate);
#endif
 
#ifndef _MAC
    // set font of property page to same font used by property sheet
    hTemplate = _tChangePropPageFont(pTemplate,
        (m_psh.dwFlags & PSH_WIZARD));
 
    if (pPage->m_hDialogTemplate != NULL)
    {
        GlobalFree(pPage->m_hDialogTemplate);
        pPage->m_hDialogTemplate = NULL;
    }
 
    if (hTemplate != NULL)
    {
        pTemplate = (LPCDLGTEMPLATE)hTemplate;
        pPage->m_hDialogTemplate = hTemplate;
    }
#endif //!_MAC
 
    pPage->m_psp.pResource = pTemplate;
    pPage->m_psp.dwFlags |= PSP_DLGINDIRECT;
 
    // add page to internal list
    m_pages.Add(pPage);
 
    // add page externally
    if (m_hWnd != NULL)
    {
        HPROPSHEETPAGE hPSP = CreatePropertySheetPage(&pPage->m_psp);
        if (hPSP == NULL)
            AfxThrowMemoryException();
 
        if (!SendMessage(PSM_ADDPAGE, 0, (LPARAM)hPSP))
        {
            DestroyPropertySheetPage(hPSP);
            AfxThrowMemoryException();
        }
    }
}
 
Additional reference words: 4.00 ocx
KBCategory: kbprg kbbuglist kbfixlist
KBSubcategory: MfcMisc
=============================================================================
Copyright Microsoft Corporation 1996.
