Microsoft KB Archive/150349

From BetaArchive Wiki
Knowledge Base


Article ID: 150349

Article Last Modified on 11/21/2006



APPLIES TO

  • Microsoft Foundation Class Library 4.2, when used with:
    • Microsoft Visual C++ 4.0 Standard Edition
    • Microsoft Visual C++ 4.1 Subscription
    • Microsoft Visual C++ 4.2 Enterprise Edition
    • Microsoft Visual C++ 5.0 Enterprise Edition
    • Microsoft Visual C++ 6.0 Enterprise Edition
    • Microsoft Visual C++ 4.2 Professional Edition
    • Microsoft Visual C++ 5.0 Professional Edition
    • Microsoft Visual C++ 6.0 Professional Edition
    • Microsoft Visual C++ 6.0 Standard Edition



This article was previously published under Q150349

SYMPTOMS

When you use a wizard style property sheet with Dialog Data Exchange (DDX) routines, data is not transferred from the current page to the corresponding variables when you click Finish.

CAUSE

MFC relies on the PSN_KILLACTIVE notification handler, OnKillActive(), to call UpdateData(TRUE). This notification is not sent when you click Finish, thus UpdateData(TRUE) is not called to allow data transfer by way of DDX.

RESOLUTION

When you click Finish, a PSN_WIZFINISH notification is sent. To allow the DDX routines to transfer data from the page to the corresponding variables, derive a class from CPropertyPage, and override the virtual function OnWizardFinish(). In OnWizardFinish(), place a call to UpdateData(TRUE).

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 Microsoft Visual C++ .NET.


MORE INFORMATION

When a user clicks Back or Next on a wizard page, the page loses the focus and consequently receives a PSN_KILLACTIVE notification message. When it receives the PSN_KILLACTIVE notification, the virtual function of the wizard page, OnKillActive(), calls UpdateData(TRUE). This call allows the DDX routines to transfer data from the page to the corresponding variables. However, when you click Finish a PSN_KILLACTIVE notification is not sent, and thus UpdateData(TRUE) is not called. Following is an example of this in code:

   /* Compile options needed: default
   */ 
   BOOL CMyPropertyPage::OnWizardFinish()
   {
       if (!UpdateData())  //note: parameter is TRUE by default
       {
           TRACE0("UpdateData failed during wizard finish\n");
           return FALSE;
       }
       return CPropertyPage::OnWizardFinish();
   }
                


An additional problem surfaces when validation fails for one of the entries of this last page. Normally, focus should be set to the first control which contains an invalid entry. This does not occur for the wizard style property sheets. To work around this problem, send a WM_ACTIVATE message to the control.

The MFC CDataExchange class manages the validation process, and stores the HWND in its m_hWndLastControl variable. When a control fails validation, the value is stored, and a CUserException is thrown, promptly escorting the pDX instance of CDataExchange out of scope from within the property page's DoDataExchange() override. The value can be retrieved, however, by catching the exception in DoDataExchange(). To test the following example code, add an HWND member to a derived property page class and call it m_hWndLastControl. The edit box IDC_EDIT1 accepts digits from 0 to 10, thus an entry greater than 10 fails validation.

void CMyPropertyPage::DoDataExchange(CDataExchange* pDX)
{
    try
    {
        CPropertyPage::DoDataExchange(pDX);
        //{{AFX_DATA_MAP(CMyPropertyPage)
        DDX_Text(pDX, IDC_EDIT1, m_nDigit);
        DDV_MinMaxInt(pDX, m_nDigit, 0, 10);
        //}}AFX_DATA_MAP
    }
    catch(CUserException* e)
    {
        if (pDX->m_bSaveAndValidate &&
                    (pDX->m_hWndLastControl != NULL))
        {
            m_hWndLastControl = pDX->m_hWndLastControl;
        }
        else
        {
            m_hWndLastControl = NULL;
        }
        throw(e); // Pass the exception on
    }
}
                


Once HWND has been saved, a WM_ACTIVATE message can be posted in OnWizardFinish() as follows:

BOOL CMyPropertyPage::OnWizardFinish()
{
    if (!UpdateData())  //note: parameter is TRUE by default
        {
        TRACE0("UpdateData failed during wizard finish\n");
        if (m_hWndLastControl != NULL)
        {
            ::PostMessage(m_hWndLastControl, WM_ACTIVATE,
                                     MAKEWPARAM(WA_ACTIVE, 0), 0L);
        }
        return FALSE;
    }
    return CPropertyPage::OnWizardFinish();
}
                


Additional query words: CPropertySheet UpdateData DoDataExchange OnWizardFinish Wizard CPropertyPage

Keywords: kbbug kbdlg kbfix kbnoupdate kbprogramming kbpropsheet KB150349