Click here to Skip to main content
13,862,664 members
Click here to Skip to main content
Add your own
alternative version


32 bookmarked
Posted 19 Sep 2003
Licenced CPOL

A Wizard-like property sheet for the Pocket PC

, 19 Sep 2003
Rate this:
Please Sign up or sign in to vote.
Implementing a wizard-like dialog on the Pocket PC using property sheets.

Sample Image - CeWizard.jpg


It is common knowledge that the Pocket PC does not support wizard dialogs. Although the documentation says that the PSH_WIZARD flag is supported for property sheets, it does not seem to work as it should.

This article describes an implementation of a wizard-like dialog supported by property sheets, using a very simple tweak. There are other resources on creating wizard dialogs, most notably Daniel S.'s implementation using a dialog: QA: How can I create a wizard style dialog?. This article is the update of an article I had previously published: QA: How can I use a property sheet to implement a Wizard?.

In this second version of the article, the "visual glitch" reported by John Simmons is solved.

Pocket PC property sheets

In an article published here - see Property sheet callbacks in the Pocket PC 2002 - the property sheet callback mechanism is introduced and made compatible with MFC, through the CCePropertySheet class. By using a customized callback function, one can add the header and footer found in most Settings property sheets. The header and footer are set up by handling the PSCB_GETTITLE and PSCB_GETLINKTEXT callback messages.

What this article does not tell you is how the property sheet is assembled by the system, and how you can use this information for your own purposes. The property sheet is a dialog that contains the following items:

  • The property page dialog
  • The header. This is an optional static child control whose ID is 0x3028.
  • The tab control. It is a child control whose ID is 0x3020. MFC identifies it as AFX_IDC_TAB_CONTROL
  • The footer. This is an optional rich ink control whose text - set by PSCB_GETLINKTEXT - is interpreted as the parameter of a EM_INSERTLINKS message (see richink.h for more information).

The main idea of this article is that the tab control can be hidden and that wizard-like navigation can be implemented using SetActivePage(), GetActiveIndex() and GetPageCount().


We are now prepared to implement a wizard-like dialog using a property sheet (CCeWizard). This class derives from CCePropertySheet, thereby allowing the user to insert headers and footers. The first thing we need to do is to set-up the dialog:

// CCeWizard::OnInitDialog
//        Initializes the wizard property sheet
BOOL CCeWizard::OnInitDialog() 
    BOOL            bResult = CCePropertySheet::OnInitDialog();
    HWND            hWndTab;
    // Hide the tab
    hWndTab = ::GetDlgItem(m_hWnd, AFX_IDC_TAB_CONTROL);
        ::ShowWindow(hWndTab, SW_HIDE);

    // Hide the OK button
    SHDoneButton(m_hWnd, SHDB_HIDE);

    // Populate the toolbar


    return bResult;

We will get to the PopulateToolBar() and UpdateControls() methods a bit later. Now, you may notice that after hiding the tab control, the line that separates the header from the dialog is gone too. Apparently, this line is a part of the tab control, and thus it is hidden. To circumvent this problem, we have to draw it ourselves, in the OnPaint method:

// CCeWizard::OnPaint
//        Paints the dialog
void CCeWizard::OnPaint() 
    CPaintDC    dc(this);
    CRect        rc;


        dc.MoveTo(0, 23);
        dc.LineTo(rc.right, 23);

Note that the line is drawn only if there is a title (m_strTitle belongs to the CCePropertySheet class).


Now, we have to worry about navigating through the wizard: after hiding the tab control, we must provide a means for the user to flip through the several pages (property pages). The best place to put the control buttons is on the command bar. The CCeWizard class provides two options for placing controls on the command bar (although you can certainly override this functionality): graphic buttons or text buttons. If you want to provide graphic buttons (see top image), create a toolbar on the resource editor with at least four buttons: ID_BAR_OK, ID_BAR_CANCEL, ID_BAR_BACK and ID_BAR_NEXT. When you create your CCeWizard object, pass the toolbar ID as the second parameter to the constructor. To show text buttons (see next image), use 0 as the second parameter on the class constructor, and define the following string resources: IDS_BAR_OK, IDS_BAR_CANCEL, IDS_BAR_BACK and IDS_BAR_NEXT.

Wizard with text buttons

Handling the navigation commands is a simple matter. Here is the ID_BAR_BACK handler:

// CCeWizard::OnBarBack
//        Moves to the previous page
void CCeWizard::OnBarBack() 
    SetActivePage(GetActiveIndex() - 1);


And now, the ID_BAR_NEXT handler:

// CCeWizard::OnBarNext
//        Moves to the next page
void CCeWizard::OnBarNext() 
    SetActivePage(GetActiveIndex() + 1);


Updating controls

Now, let's see how to update the wizard's controls. This task is necessary in order to let the application's user know where in the wizard he or she is. This is done in two ways: updating the navigation buttons and reporting the progress in the wizard's header. All of this is achieved in just one method:

// CCeWizard::UpdateControls
//        Updates the command bar buttons
void CCeWizard::UpdateControls()
    int                iIndex = GetActiveIndex(),
                    nPages = GetPageCount();
    CToolBarCtrl&    rToolBar = m_pWndEmptyCB->GetToolBarCtrl();
    CWnd*            pWndHdr;

    // Set the header text
    pWndHdr = GetDlgItem(AFX_IDC_HEADER_CONTROL);
        CString    strMsg,

        strMsg.Format(_T(" (%d/%d)"), iIndex + 1, nPages);

        strHeader = m_strTitle + strMsg;


    // Enable or disable the back and next buttons if needed
    rToolBar.EnableButton(ID_BAR_BACK, iIndex > 0);
    rToolBar.EnableButton(ID_BAR_NEXT, iIndex < nPages - 1);

Note that the navigation button's state is updated in the same way whether it is graphic or text.

Inserting the toolbar

Both the graphics and the text toolbars are inserted using just one method:

// CCeWizard::PopulateToolBar
//        Loads a graphics or button toolbar
void CCeWizard::PopulateToolBar()
    CCeCommandBar*    pCmdBar;

    pCmdBar = (CCeCommandBar*)m_pWndEmptyCB;

        TBBUTTON    tbButton;
        CString        strMenu;

        memset(&tbButton, 0, sizeof(TBBUTTON));
        tbButton.iBitmap = I_IMAGENONE;
        tbButton.fsState = TBSTATE_ENABLED;
        tbButton.fsStyle = TBSTYLE_BUTTON | TBSTYLE_AUTOSIZE;

        tbButton.iString    = (int)(LPCTSTR)strMenu;
        tbButton.idCommand    = ID_BAR_OK;
        pCmdBar->SendMessage(TB_INSERTBUTTON, 0, (LPARAM)&tbButton);

        tbButton.iString    = (int)(LPCTSTR)strMenu;
        tbButton.idCommand    = ID_BAR_CANCEL;
        pCmdBar->SendMessage(TB_INSERTBUTTON, 1, (LPARAM)&tbButton);

        tbButton.iString    = (int)(LPCTSTR)strMenu;
        tbButton.idCommand    = ID_BAR_BACK;
        pCmdBar->SendMessage(TB_INSERTBUTTON, 2, (LPARAM)&tbButton);

        tbButton.iString    = (int)(LPCTSTR)strMenu;
        tbButton.idCommand    = ID_BAR_NEXT;
        pCmdBar->SendMessage(TB_INSERTBUTTON, 3, (LPARAM)&tbButton);

Terminating the wizard

Terminating the wizard should not be done using a direct call to EndDialog(). My own experience showed me the hard way that this will not call the appropriate DDX and DDV routines. Instead, we send the IDOK and IDCANCEL commands directly to the property sheet.

The visual glitch

The first version of the code did not consider an inevitable side effect of hiding the tab control: the property sheet doesn't know it's hidden, so it will happily resize the child property page as though the tab were there. What happened was that some dialog real estate was being stolen (the area where the tab control was supposed to be). This was noted by John Simmons (and that is why his name is referenced in the image). Thank you, John!

Solving the glitch involved resizing the active page. This is done in the following method:

// CCeWizard::ResizePage
//        Resize the active property page
void CCeWizard::ResizePage()
    CPropertyPage*    pPage = GetActivePage();

        CRect    rc;

        rc.bottom += 22;            // MAGIC NUMBER!!!

This method is called from a number of places in the code, especially from inside UpdateControls().

After testing the code, I found that using the SIP would revert to the old behavior: the lower 22 pixel strip was being stolen again. To solve this CCeWizard must handle the WM_ACTIVATE and WM_SETTINGCHANGE messages. The handlers just call the appropriate shell methods:

// CCeWizard::OnActivate
//        Handle the SIP correctly
void CCeWizard::OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized)
    HWND    hWnd = NULL;

        hWnd = *pWndOther;

    SHHandleWMActivate(m_hWnd, MAKELPARAM(nState, 
            bMinimized), (LPARAM)hWnd, &m_sai, 0);

// CCeWizard::OnSettingChange
//        Handle the SIP correctly
void CCeWizard::OnSettingChange(UINT uFlags, LPCTSTR lpszSection)
       (WPARAM)uFlags, (LPARAM)lpszSection, &m_sai);

And that's it!


This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


About the Author

João Paulo Figueira
Software Developer (Senior) Frotcom International
Portugal Portugal
I work on R&D for Frotcom International, a company that develops web-based fleet management solutions.

You may also be interested in...

Comments and Discussions

QuestionHandle to richink window Pin
ajhuddy28-Jul-06 9:52
memberajhuddy28-Jul-06 9:52 
GeneralProblem with Cancel button Pin
jmr00713-Jun-05 19:48
memberjmr00713-Jun-05 19:48 
GeneralRe: Problem with Cancel button Pin
João Paulo Figueira13-Jun-05 22:55
professionalJoão Paulo Figueira13-Jun-05 22:55 
GeneralCompiling and running Pin
Member 18471515-Apr-05 10:41
memberMember 18471515-Apr-05 10:41 
GeneralRe: Compiling and running Pin
João Paulo Figueira5-Apr-05 11:31
professionalJoão Paulo Figueira5-Apr-05 11:31 
GeneralRe: Compiling and running Pin
Member 18471515-Apr-05 15:36
memberMember 18471515-Apr-05 15:36 
GeneralRe: Compiling and running Pin
João Paulo Figueira5-Apr-05 22:42
professionalJoão Paulo Figueira5-Apr-05 22:42 
QuestionHow to link to an URL Pin
Bui Huy Kien23-Mar-05 18:52
memberBui Huy Kien23-Mar-05 18:52 
GeneralExtremely helpful. Pin
islamomt30-Jan-05 6:56
memberislamomt30-Jan-05 6:56 
GeneralIf real-estate is not too critical.. Pin
Razle14-Nov-04 22:53
memberRazle14-Nov-04 22:53 
GeneralGreat Idea but... Pin
37NyyVp20-Mar-04 4:58
member37NyyVp20-Mar-04 4:58 
GeneralRe: Great Idea but... Pin
João Paulo Figueira21-Mar-04 12:17
professionalJoão Paulo Figueira21-Mar-04 12:17 
GeneralOne question ... Pin
Daniel Strigl23-Sep-03 23:12
memberDaniel Strigl23-Sep-03 23:12 
GeneralRe: One question ... Pin
João Paulo Figueira23-Sep-03 23:19
professionalJoão Paulo Figueira23-Sep-03 23:19 
GeneralNice Idea ! Pin
Senthil Kumar Umapathy21-Sep-03 21:39
sussSenthil Kumar Umapathy21-Sep-03 21:39 
GeneralRe: Nice Idea ! Pin
João Paulo Figueira21-Sep-03 23:14
professionalJoão Paulo Figueira21-Sep-03 23:14 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web06 | 2.8.190214.1 | Last Updated 20 Sep 2003
Article Copyright 2003 by João Paulo Figueira
Everything else Copyright © CodeProject, 1999-2019
Layout: fixed | fluid