Creating Wizards in Microsoft Office 97

Microsoft Corporation

March 7, 1997

Click to copy the files in the WIZARD sample application for this technical article.

Table of Contents

Introduction

The Wizard Interface

Using the Office Assistant with a Wizard

Wizard Templates and Add-ins

Where Do You Go from Here?

Introduction

A wizard is a macro (or set of macros) that presents a series of steps for a user to complete. A wizard includes one or more custom dialog boxes (referred to as forms in this article), that contain a variety of controls for getting information from the user. After the user has completed the steps in the wizard, the wizard creates a document or carries out a task based on the information provided by the user. If you aren’t familiar with wizards, take a moment to run the wizards included with Microsoft® Word and Microsoft Excel. To locate Wizards in Word, click New on the File menu. Wizards can be found, along with templates, on each of the tabs in the New dialog box. A list of Wizards available with Microsoft Excel can be found by clicking on the Tools menu and pointing to Wizard. A wizard has essentially two parts: the user interface that requests information from the user, and the behind-the-scenes part that carries out the actions necessary to accomplish the task. The behind-the-scenes part varies according to what the wizard is designed to accomplish; every wizard is different in that respect. However, most of the wizards included with the Microsoft Office applications share a basic user interface. This article describes how to create this user interface for your custom wizards.

WizForm.frm, which accompanies this article, includes the controls and the corresponding subroutines used to create the basic wizard user interface. WizForm.frm provides a useful starting point for creating a wizard. Wizard.bas, which also accompanies this article, contains all the code that you need to run the wizard that is not contained in the form.

Note   Microsoft Excel, Word, and Microsoft PowerPoint® share the same tools for creating custom dialog boxes. For information about designing forms in Microsoft Access, see "Building Applications with Microsoft Access 97" (MSDN Library, Microsoft Office Development).

In this article, the first letter of variable names indicates the variable type. For example, “o” indicates the variable type is an object, “l” indicates a long, “b” indicates a Boolean, “i” indicates an integer, and “str” indicates a string.

The Wizard Interface

The basic interface shared by most Office wizards is shown in Figure 1. The empty area to the right of the flow chart is a MultiPage control with no tabs. The controls that are unique to each wizard are placed on the pages of this MultiPage control.

Figure 1. The basic Office Wizard interface

A wizard is comprised of a series of steps. The user navigates through the steps using the command buttons at the bottom of the form or the flow chart navigation image. When the user clicks the Finish button, the wizard uses the information from the wizard steps to perform a task. For example, the Memo Wizard included with Word 97 prompts the user for information and then creates a memo based on the user’s responses.

Step 1. Designing the Form

The first step in creating a wizard is to design your wizard on paper. Decide what information you need to gather and what controls are most appropriate. Sketch out the contents of each step in the wizard. After the design is complete, you’re ready to create the user form. This article discusses how to create the user form shown in Figure 1.

Step 2. Creating the Form

The next step in creating a wizard is to create the user form. The user form includes all of the controls that are used to gather information from the user. In addition to defining the basic interface of the wizard, the form includes the majority of the wizard code. This is because much of the wizard code executes in response to a user clicking on a control on the user form. The user form shown in the preceding illustration (WizForm.frm) includes the controls in Table 1

Table 1. Controls Included with Wizform.frm.

Name Control type Description
imgFlowChartBackground Image The black box behind the flow chart
tglAssistant ToggleButton Help button
cmdBack CommandButton Back button
cmdCancel CommandButton Cancel button
cmdFinish CommandButton Finish button
cmdNext CommandButton Next button
lblMap0 Label Start label
lblMap1 Label Step 2 label
lblMap2 Label Step 3 label
lblMap3 Label Step 4 label
lblMap4 Label Finish label
shpChartPathCV Label Line that connects the squares in the flow chart (an empty label with a border style)
shpMap0 Label Green box in flow chart
shpMap1 Label Gray box in flow chart (this box is green when active)
shpMap2 Label Gray box in flow chart (this box is green when active)
shpMap3 Label Gray box in flow chart (this box is green when active)
shpMap4 Label Red box in flow chart (this box is green when active)
MpgWizardPage MultiPage Multiple page control with five pages that correspond to the steps in the wizard

Instead of adding these controls one by one to a new user form, you can simply import WizForm.frm.

To import WizForm.frm

  1. Create a new Microsoft Excel workbook, Microsoft PowerPoint presentation, or Word template.

  2. Switch to the Microsoft Visual Basic® Editor.

  3. Select the project associated with the new file.

  4. On the File menu, click Import File. Select WizForm.frm and click the Open button.

These steps add the WizForm form to your new project. All of the controls and the corresponding subroutines are copied to your project. To learn how the wizard works, you can remove the subroutines behind the controls and add the subroutines back one by one as described in the remaining steps of this article.

To remove the existing code in WizForm.frm

  1. Select WizForm in the Project Explorer.

  2. Click Code on the View menu.

  3. Click Select All on the Edit menu.

  4. Click Cut on the Edit menu.

Step 3. Displaying and Closing the Form

The code to display WizForm should be stored in a standard code module. If you want to import a standard module that contains all the code not contained in the form that you need to run the wizard, click Import File on the File menu. Select Wizard.bas and click the Open button.

If you want to go through the steps of creating your own standard code module and inserting code from this document into it, select your project name in the Project Explorer and click Module from the Insert menu. Create the following subroutine, which uses the Show method to display WizForm:

Public iCurrentPage As Integer

Public Sub InitWizard()
    iCurrentPage = 0
    With WizForm
        .MpgWizardPage.Value = 0
        .Show
    End With
End Sub

The additional instructions set the MpgWizardPage control to 0 (zero), which is the first page in the group of pages. For more information on the MultiPage control named MpgWizardPage, see “Step 4. Making the Next and Back Buttons Work.”

Now add the code to close the form. Double-click the Cancel button in the form. Complete the cmdCancel_Click procedure, as shown in the following example. The Unload method is used to close WizForm:

Private Sub cmdCancel_Click()
    Unload WizForm
End Sub

Switch back to the wizard form and double-click the Finish button. Complete the cmdFinish_Click procedure, as shown in the following example:

Private Sub cmdFinish_Click()
    Unload WizForm
End Sub

In addition to clicking the Finish or Cancel button, the user can click the Close button in the upper-right corner of the form or choose the Close command from the form’s Control menu. However, if the user closes the form in either of these ways, the code in the Click event procedure for the Cancel button does not run. You can use the QueryClose event, which occurs just before a user form closes, to call the Click event procedure for the Cancel button (cmdCancel) if the form is closed using Close (indicated if the value of the CloseMode argument of the QueryClose event is vbFormControlMenu). In the WizForm code module, add the following procedure:

Private Sub WizForm_QueryClose(Cancel As Integer, CloseMode As Integer)
    If (CloseMode = vbFormControlMenu) Then
        Call cmdCancel_Click
    End If
End Sub

Note   In the preceding code examples, you can use the keyword Me instead of the code name of the form (WizForm). That is, you can replace the statement "WizForm.Show" with the statement "Me.Show". The Me keyword used in code for a UserForm or a control on the UserForm represents the UserForm itself.

Now it’s time to run the InitWizard macro. Switch to the code module that contains the InitWizard subroutine, position the insertion point in the subroutine, and click Run Sub/UserForm on the Run menu. The Sample Wizard form is displayed and can be closed by using the Cancel or Finish button, or by using the Close button or the Close command on the Control menu.

Step 4. Making the Next and Back Buttons Work

The majority of the WizForm layout is taken up by a MultiPage control named MpgWizardPage. A MultiPage control contains a collection of one or more pages. Each page of a MultiPage control contains its own controls, and therefore can have a unique layout. Each of the pages is a Page object and together they represent the Pages collection of the MultiPage control. The MpgWizardPage control on WizForm includes five different Page objects with the controls noted in Table 2.

Table 2. Controls for Each Page in Wizform

Page Control Description
Page1 lblEmail (Label control) “What is your e-mail name?”
lblEmail1 (Label control) “E-mail”
txtEmail (TextBox control) Text box for e-mail
Page2 lblColor (Label control) “Choose a color”
frmColors (Frame control) Color frame around the option buttons
optBlue (OptionButton control) Blue option button
optGreen (OptionButton control) Green option button
optRed (OptionButton control) Red option button
Page3 lblNameAddress (Label control) “What is your name and mailing address?”
txtName (TextBox control) Name text box
txtAddress (TextBox control) Address text box
lblName (Label control) “Name”
lblAddress (Label control) “Address”
Page4 lblPersonal (Label control) “Which personal items would you like to include?”
chkMarital (CheckBox control) Marital status check box
chkNationality (CheckBox control) Nationality check box
chkAge (CheckBox control) Age check box
chkBirthPlace (CheckBox control) Place of birth check box
Page5 lblDone (Label control) “The sample wizard is done!”

When a user clicks the Next or Back button to move to the next or previous step in the wizard, the appropriate page in the MultiPage control is displayed. This is done by incrementing the Value property of the MultiPage control. The following example displays the next page in the MultiPage control, named MpgWizardPage:

iCurrentPage = WizForm.MpgWizardPage.Value
WizForm.MpgWizardPage.Value = iCurrentPage + 1

Double-click the Back button on the form. Complete the cmdBack_Click procedure, as follows:

Private Sub cmdBack_Click()
    If iCurrentPage <> 0 Then WizForm.MpgWizardPage.Value = _
        iCurrentPage - 1
End Sub

The value of the first page in a MultiPage control is 0 (zero). The cmdBack_Click event procedure decreases the current page in the MultiPage control by 1 if the current page isn’t zero (0). Double-click the Next button in the form. Complete the cmdNext_Click procedure, as follows:

Private Sub cmdNext_Click()
    If iCurrentPage < 4 Then WizForm.MpgWizardPage.Value = _
        iCurrentPage + 1
End Sub

The MpgWizardPage control on WizForm has five pages: 0 (zero) through 4. The cmdNext_Click event procedure increases the current page in the MpgWizardPage control by 1 if the current page is less than four. The Change event occurs when the Value property of the MpgWizardPage control changes. Create an event procedure named MpgWizardPage_Change, as follows (this procedure runs when the user moves to a new page).

Private Sub MpgWizardPage_Change()

End Sub

If you ran the InitWizard subroutine at this point, the Next button would work only once. The iCurrentPage value is initially set to zero in the InitWizard subroutine. After the Value property of the MpgWizardPage control is changed in the cmdNext_Click event procedure, the iCurrentPage value remains 0 (zero). The iCurrentPage value needs to reflect the new page number. This can be done by modifying the MpgWizardPage_Change event procedure as follows:

Private Sub MpgWizardPage_Change()
    With WizForm
        iCurrentPage = .MpgWizardPage.Value
    End With
End Sub

Now, if you run the InitWizard subroutine, the Next button moves through the pages in the MultiPage control. However, the Back button remains disabled. Depending upon the current page, the Next and Back buttons are enabled or disabled. This is done using a Select Case structure that enables and disables buttons depending upon the current page (the value of iCurrentPage). Complete the MpgWizardPage_Change procedure, as follows:

Private Sub MpgWizardPage_Change()
With WizForm
    iCurrentPage = .MpgWizardPage.Value
    Select Case iCurrentPage
        Case 0
            .cmdBack.Enabled = False
            .cmdNext.Enabled = True
        Case 1, 2, 3
            .cmdBack.Enabled = True
            .cmdNext.Enabled = True
        Case 4
            .cmdBack.Enabled = True
            .cmdNext.Enabled = False
    End Select
End With
End Sub

Now save and run the InitWizard subroutine. At this point, you can move forward and backwards through the pages in the MultiPage control.

Handling forced steps (part 1)

The Sample Wizard included with this article doesn’t include any mandatory, or “forced,” steps. A user can freely move between pages in the MultiPage control regardless of the values on the current page. A wizard can, however, force a user to provide a response before moving on to the next page. In the case of the Sample Wizard discussed in this article, you can disable the Next button until the user types text into the e-mail text box on the first wizard page. To do this, modify Case 0 (zero) in the Select Case structure in the MpgWizardPage_Change event procedure to appear as follows:

    Select Case iCurrentPage
        Case 0
            .cmdBack.Enabled = False
            If txtEmail.Value = "" Then .cmdNext.Enabled = False

The If...Then statement disables the txtEmail TextBox control when the current value is an empty string (""). This instruction executes once when the current page changes to the first page in the MultiPage control. Once the user adds text to the txtEmail control, the Next button should be enabled. This can be done using the Change event of the txtEmail control. Create an event procedure named txtEmail_Change as follows (this procedure runs whenever the user changes the value of the txtEmail control):

Private Sub txtEmail_Change()
    If WizForm.txtEmail.Value = "" Then
        WizForm.cmdNext.Enabled = False
    Else
        WizForm.cmdNext.Enabled = True
    End If
End Sub

The If...Then statement disables the Next button if the value of the txtEmail control is an empty string (""). Once text is typed into the txtEmail control, the Value property doesn’t equal an empty string and the Next button is enabled.

Step 5. Changing the Form Caption

You may have noticed that when you move from page to page, the form caption remains the same (“Sample Wizard—Step 1 of 4”). Ideally, the caption should change to reflect the active page number. This can be done by setting the Caption property of the form in the MpgWizardPage_Change procedure as follows:

Private Sub MpgWizardPage_Change()
With WizForm
    iCurrentPage = .MpgWizardPage.Value
    .Caption = strCaption(iCurrentPage)    'add this instruction

The Caption property changes the caption text of the form (WizForm). The strCaption() variable refers to an array of strings defined in the InitWizard module. This allows the caption to vary depending upon the current page. Add the following instruction to the declarations section of the InitWizard subroutine:

Public strCaption(4) As String

This declaration creates an array of strings named strCaption(). Now define the individual strings in the InitWizard subroutine before WizForm is displayed using the Show method:

Private Sub InitWizard()
    iCurrentPage = 0

    'caption strings for WizForm
    strCaption(0) = "Sample Wizard - Step 1 of 4"
    strCaption(1) = "Sample Wizard - Step 2 of 4"
    strCaption(2) = "Sample Wizard - Step 3 of 4"
    strCaption(3) = "Sample Wizard - Step 4 of 4"
    strCaption(4) = "Sample Wizard - Steps 1-4 are complete"

At this point, you can run the InitWizard subroutine and see the caption text change when you use the Next and Previous buttons to navigate through the wizard steps.

Step 6. Making the Flow Chart Work

The flow chart controls that appear on WizForm show users the individual wizard steps and allow users to navigate through the wizard. For example, by clicking the Step 3 label or the corresponding flow chart box, a user can jump ahead to Step 3.

The names of the flow chart labels are lblMap0 through lblMap4, and the names of the box shapes are shpMap0 through shpMap4. Using the Click event, you can change the current page when the user clicks either a label or a flow chart box. To do this, you need to add the Click event procedure for each of the labels and flow chart boxes.

The lplMap#_Click procedures are called when a label (for example, “Step 1”) beside a flow chart box is clicked. Set the Value property to change the current page in the MultiPage control. For example, the first event procedure (lblMap0_Click) changes the page to the first page (page zero) when lblMap0 is clicked:

Private Sub lblMap0_Click()
    WizForm.MpgWizardPage.Value = 0
End Sub

Private Sub lblMap1_Click()
    WizForm.MpgWizardPage.Value = 1
End Sub

Private Sub lblMap2_Click()
    WizForm.MpgWizardPage.Value = 2
End Sub

Private Sub lblMap3_Click()
    WizForm.MpgWizardPage.Value = 3
End Sub

Private Sub lblMap4_Click()
    WizForm.MpgWizardPage.Value = 4
End Sub

The shpMap#_Click procedures are called when a flow chart box is clicked. Set the Value property to change the current page in the MultiPage control. For example, the second procedure (shpMap1_Click) changes the page to the second page (page one) when the box beside Step 2 is clicked:

Private Sub shpMap0_Click()
    WizForm.MpgWizardPage.Value = 0
End Sub

Private Sub shpMap1_Click()
    WizForm.MpgWizardPage.Value = 1
End Sub

Private Sub shpMap2_Click()
    WizForm.MpgWizardPage.Value = 2
End Sub

Private Sub shpMap3_Click()
    WizForm.MpgWizardPage.Value = 3
End Sub

Private Sub shpMap4_Click()
    WizForm.MpgWizardPage.Value = 4
End Sub

If you run the InitWizard subroutine at this point, the flow chart controls can be used to navigate through the wizard pages. However, the controls don’t change in appearance. This can be confusing to the user. In order to make the flow chart more useful, you can add instructions to visually differentiate the current step and the previously visited steps. The wizards included with the Office applications do this by using color and bold text formatting. Specifically, when a user jumps to a step, that step's text appears in bold and the box beside the step is changed to green. The other steps are marked with a dark gray box and the label text is not bold.

These visual changes need to occur when the user changes the current page (by using either the Next or Previous command buttons or the flow chart image). Adding instructions to the MpgWizardPage_Change event procedure can do this. Add the following instructions to the MpgWizardPage_Change procedure:

Private Sub MpgWizardPage_Change()
    With WizForm
        iCurrentPage = .MpgWizardPage.Value
        .Caption = strCaption(iCurrentPage)

        'deselect current flow chart marker
        .Controls(strSHP_MAP & iPreviousPage).BackColor = COLOR_DARKGREY
        .Controls(strLBL_MAP & iPreviousPage).FontBold = False
         
        'select new flow chart marker
        .Controls(strSHP_MAP & iCurrentPage).BackColor = COLOR_GREEN
        .Controls(strLBL_MAP & iCurrentPage).FontBold = True

The new instructions concatenate a string, either shpMap or lblMap, with the current or previous page number. This is done in order to refer to a label or shape control name such as lblMap2 or shpMap3. Bold formatting is turned off and the box of the previous page is colored dark gray. The current page label text is made bold and the box is colored green.

The instructions added to the MpgWizardPage_Change event procedure require the following declarations. Add the following statements to the declarations section of your InitWizard subroutine:

Public iPreviousPage As Integer

'used for visited page's shape
Public Const COLOR_DARKGREY As Long = &H808080
'used for active page
Public Const COLOR_GREEN As Long = vbGreen

'prefix to the box names in the flow chart
Public Const strSHP_MAP As String = "shpMap"
'prefix to the label names in the flow chart
Public Const strLBL_MAP As String = "lblMap"

The iPreviousPage variable stores the number of the previous page. For example, if the user jumps from page 2 to page 4, iPreviousPage would be 2 and iCurrentPage would be 4. The iCurrentPage value is set near the beginning of the MpgWizardPage_Change event procedure (this was done in Step 4). The iPreviousPage value needs to be set before the MpgWizardPage value is changed. Look for places in your existing code where the Value property of the MpgWizardPage control is set. This is done in the cmdBack_Click and cmdNext Click event procedures as well as the eight Click event procedures added for the flow chart navigation. In each of the shpMap#_Click and lblMap#_Click event procedures, add the following instruction before the Value property is set:

iPreviousPage = iCurrentPage

For example, the complete lblMap0_Click event procedure should appear as follows:

Private Sub lblMap0_Click()
    iPreviousPage = iCurrentPage
    WizForm.MpgWizardPage.Value = 0
End Sub

Now add the same instruction to the cmdBack_Click and cmdNext _Click event procedures so that they appear as follows:

Private Sub cmdBack_Click()
    If iCurrentPage <> 0 Then
        iPreviousPage = iCurrentPage
        WizForm.MpgWizardPage.Value = iCurrentPage - 1
    End If
End Sub

Private Sub cmdNext_Click()
    If iCurrentPage < 4 Then
        iPreviousPage = iCurrentPage
        WizForm.MpgWizardPage.Value = iCurrentPage + 1
    End If
End Sub

At this point, you can run the InitWizard subroutine and move forward and backward using the flow chart controls. When you change pages, the flow chart updates to reflect the current page as well as the pages you’ve visited.

Handling forced steps (part 2)

If you create a wizard with mandatory or forced steps, you may want to control how the user moves between steps. “Handling Forced Steps (Part 1)” in Step 4 addressed this issue for the Next and Back buttons, but now the flow chart controls need to be addressed. This can be done in a couple of different ways. The easiest way is to remove the Click event procedures associated with the flow chart controls (that is, the various shpMap#_Click and lblMap#_Click procedures). This way the flow chart controls can’t be used for navigation, but you can still see the current and visited steps.

If you want to keep the navigation functionality of the flow chart controls, you need to add instructions that check the values of the controls on the current page before allowing the user to change pages. This can be done by calling a function that determines whether the user can move to another step prior to changing the mpgWizardPage value. For example, the current lblMap1_Click event procedure changes the page to the second page (page 1) when the label named lblMap1 is clicked:

Private Sub lblMap1_Click()
    iPreviousPage = iCurrentPage
    WizForm.MpgWizardPage.Value = 1
End Sub

Instead of moving directly to page 1, call a function that approves the move prior to changing the page. For example, the following lblMap1_Click event procedure calls the OKToMoveOn function prior to changing the page. The instructions within the If...End If structure run only if the OKToMoveOn function returns True:

Private Sub lblMap1_Click()
    If OKToMoveOn() = True Then
        iPreviousPage = iCurrentPage
        WizForm.mpgWizardPage.Value = 1
    End If
End Sub

Now create the following OKTOMoveOn function:

Private Function OKToMoveOn()
    Select Case iCurrentPage
        Case 0
            If WizForm.txtEmail.Value = "" Then fOKToMove = False _
                Else fOKToMove = True
        Case Else
            fOKToMove = True
    End Select
    OKToMoveOn = fOKToMove
End Function

The OKToMoveOn function includes a special case for the first page (Case 0). If the txtEmail textbox is empty, the fOKToMove flag is set to False. In the lblMap1_Click event procedure, the mpgWizardPage value is only changed if the OKToMoveOn function returns True; the user can’t move to another step until the txtEmail control includes some text.

Note   If your wizard includes one or more forced steps, the OKToMoveOn function needs to be called in each of the various shpMap#_Click and lblMap#_Click event procedures. Modify each routine to include the If...End If structure shown in the lblMap1_Click procedure.

Using the Office Assistant with a Wizard

If you run the wizards included with Word and Microsoft Excel, you may notice that each one has a button in the lower-left corner for displaying help about using the wizard. If you click this button, the Office Assistant is displayed, and the balloon shown in Figure 2 allows you to indicate whether you want help with the wizard or help about another feature of the product. In this article, this built-in balloon is referred to as the branch balloon.

Figure 2. The "branch balloon"

If you click Help with this feature, another balloon is displayed with the appropriate information about the current page of the wizard. If you click Help with something else, the standard Assistant balloon with a text box for typing queries is displayed.

Note   The branch balloon is also displayed when you click the Assistant window while a wizard is running.

Also, if the Help with wizards check box on the Options tab of the Office Assistant dialog box is selected, the Assistant is displayed automatically when the wizard starts, and the balloon shown in Figure 3 allows you to indicate whether you want help with the wizard right away. In this article, this built-in balloon is referred to as the decision balloon.

Figure 3. The "decision balloon"

If you click Yes, please provide help, the appropriate balloon is displayed. If you click No, don’t provide help now, the balloon is dismissed and the Assistant remains visible.

Both of these balloons, as well as the way the Assistant displays them, and the way the Assistant responds to the user’s actions are built into Office. To control when the built-in balloons are displayed while your wizard is running, use the StartWizard, ActivateWizard, and EndWizard methods of the Assistant object in the Office object library, as outlined in Table 3.

Table 3. Assistant-Object Methods to Use with Wizards

Method Purpose Where it is used
StartWizard Initiates a wizard session with the Assistant and returns a unique session ID. (If the Help with wizards check box is selected, displays the built-in decision balloon automatically.) Until the session ends, the Assistant displays the built-in branch balloon whenever it is clicked. InitWizard()
ActivateWizard Displays the Assistant (if it isn’t visible already) and the built-in branch balloon when the user clicks the Office Assistant button on WizForm. If the Assistant is already displaying Help, this method can be used to close the visible Help balloon. TglAssistant_Click()
EndWizard Ends the wizard session indicated by a unique session ID. When there are no active wizard sessions, the Assistant displays the built-in query balloon whenever it is clicked. CmdCancel_Click(), cmdFinish_Click()

To control how the Assistant responds when the user clicks a button in the decision balloon or branch balloon, you must create a callback procedure in your wizard code. A callback procedure is a subroutine that takes a predefined set of arguments and uses decision structures to direct subsequent actions based on the values of one or more of those arguments. The callback procedure required by the Assistant while a wizard is running must be a private subroutine that takes two Long arguments. The name of the callback procedure, as well as the names of the two arguments required by the procedure, can be anything you want. For example, the callback procedure may appear as follows:

Private Sub wizCallback(lButton As Long, lpriv As Long)
'
' decision structures for directing action
'
End Sub

When your wizard starts running, you must identify the name of the callback procedure you’ve created to control the Assistant’s behavior. Do this with the second argument of the StartWizard method.

The five steps in this section describe how to bring these elements together to incorporate the Assistant into your wizard just as it was incorporated into the wizards that ship with Microsoft Excel and Word. Follow each of the steps in this section. Each step adds an essential element, so no step should be omitted.

Note   This article describes one possible approach for incorporating Office Assistant Help with your custom wizard. The Assistant’s behavior is not restricted to the behavior described in this article, however. You may choose to program the Assistant differently in your custom wizards. For more information about programming the Office Assistant, see Chapter 9 of the Microsoft Office 97/Visual Basic Programmer’s Guide (MSDN Library, Microsoft Office Development).

Step 1. Beginning the Session

Before you start programming the Assistant object, add the following global variables to the declaration section of the module that contains the InitWizard procedure:

Public lwizID As Long, lprefState As Long
Public oTheBalloon As Balloon
Public bExitTglEvent As Boolean, bVis As Boolean
Public strArray(4) As String

Insert the following statements into the InitWizard procedure, directly before the iCurrentPage variable is set to 0 (zero). These statements initialize the global variables related to the Assistant:

lwizID = 0
lprefState = 0
bExitTglEvent = False
bVis = False

Now insert the following block of code after bVis is set to False. The If statement identifies the value of the Help with wizards check box on the Options tab of the Office Assistant dialog box and assigns the correct value to the lprefState variable. This variable is passed to the StartWizard method and determines whether the Assistant and the decision balloon are displayed when your wizard starts. If the Help with wizards check box is selected, lprefState is assigned msoWizardActActive. If Help with wizards is cleared or in a mixed state, lprefState is assigned msoWizardActInactive:

'record user preference in the lprefState variable
If Assistant.AssistWithWizards = True Then
    lprefState = msoWizardActActive
Else
    lprefState = msoWizardActInactive
End If

Insert the following block of code directly after the preceding If...End If statement. This block of code creates a modeless balloon that is used to display Help throughout the session. Later, this balloon is customized with Help text for each wizard page:

'create standard balloon
Set oTheBalloon = Assistant.NewBalloon
With oTheBalloon
    .Heading = "Here's some help..."
    .Mode = msoModeModeless
    .Button = msoButtonSetNone
End With

You may notice that a callback procedure isn’t specified for this modeless balloon. Normally, modeless balloons require a callback procedure. But because it’s being used during a wizard session, statements in the callback procedure for the built-in decision and branch balloons control this balloon instead.

Note   If you decide to add controls or buttons to this balloon, you must specify a callback procedure to handle each control that the user may click.

After oTheBalloon is defined in the InitWizard procedure, insert this call to a procedure named "fillArray:"

'fill the message array
Call fillArray

Now insert the private fillArray procedure (shown in the following code) in the module. The fillArray procedure fills strArray() with text messages used to provide Help for each page of the wizard’s MultiPage control. The Help message for each page is placed in the array in a location that corresponds to the number of the page. For example, Help text for the first page, page number 0 (zero), is placed in strArray(0). This way, text is added to the oTheBalloon easily by assigning the appropriate text in the array to the Text property (see “Step 4. Using Navigation Buttons and Maps” later in this article):

Private Sub fillArray()
strArray(0) = "Type your e-mail alias in the space provided. " & _
    "If you don't know your e-mail alias, contact " & _
    "your system administrator."

strArray(1) = "Select a color from the list provided by " & _
    "clicking the button next to the color choice."

strArray(2) = "Type your full name and and mailing address " & _
    "in the space provided."

strArray(3) = "All of the following personal items will be " & _
    "included. Please clear the box next to the " & _
    "items you don't want included."

strArray(4) = "To change any settings, click Back."
End Sub

Finally, insert the following code in the InitWizard procedure after the call to the fillArray procedure. This code begins the wizard session. The StartWizard method returns an integer that uniquely identifies the session and associates the session with a specific wizard. This integer is used later by the ActivateWizard and EndWizard methods. The first argument, which is assigned lprefState, indicates whether the Assistant and the built-in decision balloon are displayed automatically. The second argument indicates the name of the callback procedure that controls the built-in decision and branch balloons. The third argument is an integer that uniquely identifies the balloon that runs the callback procedure:

'start the session and assign the returned value
lwizID = Assistant.StartWizard(On:=lprefState, _
    Callback:="wizCallback", PrivateX:=1)

Step 2. Using Callback Procedures

When the user clicks Yes, please provide help in the built-in decision balloon, or Help with this feature in the built-in branch balloon, a custom balloon offering help with the current page of the MultiPage control is displayed. When the user clicks Help with something else in the branch balloon, the built-in query balloon is displayed and the user can get help with any Office feature. What makes this behavior possible is the callback procedure specified in the StartWizard method.

The callback procedure required by these built-in balloons is structured differently from callback procedures required by other modeless balloons. The callback procedure for the built-in balloons receives only two arguments: the index of the button the user clicked and the integer that identifies the balloon. The Balloon object, included in standard callback procedures for modeless balloons, is omitted because the same callback procedure runs for both the branch balloon and the decision balloon.

Your callback procedure for these balloons must use a Select Case structure to test lButton to determine the user’s response to the built-in balloons, as shown in Table .4.

Table 4. IButton Values

Branch balloon button Decision balloon button
lButton value
Help with this feature Yes, please provide help msoWizardMsgShowHelp
Help with something else No, don’t provide help now msoWizardMsgLocalStateOff

In each case, the following things must happen:

Copy the following procedure into the same module that contains the InitWizard procedure:

Private Sub wizCallback(lButton, lpriv)
    Select Case lButton
        Case msoWizardMsgLocalStateOff
            lprefState = msoWizardActInactive
            If bVis Then
                oTheBalloon.Close
                bVis = False
            End If
            If WizForm.tglAssistant.Value Then
                bExitTglEvent = True
                WizForm.tglAssistant.Value = False
            End If
            
        Case msoWizardMsgShowHelp
            lprefState = msoWizardActActive
            If bVis Then
                Exit Sub
            Else
                oTheBalloon.Text = strArray(iCurrentPage)
                oTheBalloon.Show
                bVis = True
            End If
            If Not (WizForm.tglAssistant.Value) Then
                bExitTglEvent = True
                WizForm.tglAssistant.Value = True
            End If
    End Select
End Sub

Step 3. Using the Office Assistant Button

The Office Assistant button on the wizard allows the user to display or dismiss Help at any point in the wizard. While the wizard is running, the Office Assistant button must reflect the state of the Assistant. If the Assistant is visible and displaying oTheBalloon, the button should be pushed in, whereas if the Assistant is not visible or is visible but is not displaying oTheBalloon, the button should be raised.

You may assume that the code that sets the state of the Office Assistant button belongs in the Click event procedure for the control (tglAssistant_Click). However, that’s not the case. The user’s response to the built-in branch or decision balloon determines whether the Assistant displays oTheBalloon, so the code that sets the state of the Office Assistant button must appear in the callback procedure as discussed in “Step 2. Using Callback Procedures.”

The tglAssistant_Click procedure must do the following:

Add the following Click event procedure to WizForm:

Private Sub tglAssistant_Click()
    If lwizID = 0 Then Exit Sub

    If bExitTglEvent Then
        bExitTglEvent = False
        Exit Sub
    End If

    If lprefState = msoWizardActInactive Then
        If tglAssistant.Value Then
            bExitTglEvent = True
            tglAssistant.Value = False
        End If
        Assistant.ActivateWizard WizardID:=lwizID, _
            Act:=msoWizardActActive
    ElseIf prefState = msoWizardActActive Then
        Assistant.ActivateWizard WizardID:=lwizID, _
            Act:=msoWizardActInactive
    End If
End Sub

Step 4. Using Navigation Buttons and Maps

Each time the user clicks a navigation button or clicks a section of the navigation map, the text in the Assistant balloon must change to display Help for the new wizard page. You accomplish this by assigning the appropriate string from the message array to the Text property of oTheBalloon.

Each time the user advances to the next wizard page, the value of the mpgWizardPage control is increased by one. Similarly, each time the user moves to a previous page the value of the control is decreased. The code needed to handle page changes for the wizard is in one procedure and the corresponding Assistant code should be in the same place.

If the balloon is visible (bVis is True), the following code closes the Assistant’s balloon, assigns text from strArray to the Text property of the Balloon object, and displays the balloon again.

Insert the following code after the iCurrentPage variable is set near the beginning of the MpgWizardPage_Change procedure:

If bVis Then
    oTheBalloon.Close
    oTheBalloon.Text = strArray(iCurrentPage)
    oTheBalloon.Show
End If

Step 5. Ending the Session

As discussed previously, the StartWizard method returns a session number. Assign this value to a variable, and reference the variable in the ActivateWizard and EndWizard methods. The EndWizard method releases the variable returned by the StartWizard method; therefore, it is important to include the EndWizard method to each exit point of your wizard. The exit points in this wizard are the Cancel button, the Finish button, and the built-in Close button of WizForm.

In the following subroutines, the WizardID argument of the EndWizard method is set to the lwizID variable. The varfSuccess argument is a Boolean value that indicates whether the user has successfully completed the wizard. This argument should be set to False if the Cancel button is clicked and True if the Finish button is clicked. The msoAnimationCharacterSuccessMajor animation type is applied to the Assistant if the varfSuccess argument is True.

Edit the existing code in the specified procedures to appear as follows:

Private Sub cmdCancel_Click()
    If bVis Then oTheBalloon.Close
    Assistant.EndWizard WizardID:=lwizID, varfSuccess:=False
    Unload WizForm
End Sub

Private Sub cmdFinish_Click()
    If bVis Then oTheBalloon.Close
    ' end the session and unload the wizard
    Assistant.EndWizard WizardID:=lwizID, varfSuccess:=True
    Unload WizForm
End Sub

Wizard Templates and Add-ins

Once your wizard is complete, you need to think about your distribution strategy. Where should the code module and the user form be stored? A wizard can be stored in a template or add-in depending on the Office application. How does the user run the wizard? For example, is the wizard run from the New dialog box, a menu, or a toolbar button? The following information describes wizard distribution techniques for Word, Microsoft Excel, and Microsoft PowerPoint.

Word Wizards

Word wizards, such as the wizards included with Word 97, are typically stored in a template (.dot file). If you want the user to access your wizard from the New dialog box, change the extension to .wiz  and place it in the Template folder or a subfolder of the Templates folder.

If you want the wizard to run when the user creates a new document based on the wizard, use the New event with the Document object. Use the following steps to create the event procedure.

  1. Under your project in the Project Explorer window, double-click ThisDocument. (In folder view, ThisDocument is located in the Microsoft Word Objects folder.)

  2. Select Document from the Object box. An empty Document_New subroutine is added to the class module.

  3. Add a statement that calls the subroutine that initializes and displays the wizard form (in this case InitWizard).
    Private Sub Document_New()
        Call InitWizard
    End Sub
    

Word wizards can also be designed to modify existing documents. In this case, the wizard macros are again stored in a template, but the template extension is not renamed. Instead, the template is loaded as a global template either manually (from Templates and Add-Ins on the Tools menu) or automatically when Word starts. Templates in the \Office\Startup folder are automatically loaded as global templates when Word starts.

To give users easy access to your wizard, consider customizing the menu commands or toolbar buttons in the template that contains your wizard. Open or switch to the open wizard template in Word. On the Tools menu, click Customize and then click the Commands tab. In the Categories box, click Macros. Make sure your template is selected in the Save In box. Now, drag the InitWizard macro from the Commands box over to a menu or toolbar button. After the button or menu item is added, you may want to customize the toolbar image or menu text using the options available when you right-click on the new item (while the Customize dialog box is displayed).

Now when the global template is loaded, the menu item or toolbar button becomes available so that your wizard can be accessed. Later when the template is unloaded the menu item or toolbar button disappears.

PowerPoint Wizards

PowerPoint wizards can be initially created in a presentation file (.ppt). After the wizard is ready, save the presentation file as a PowerPoint add-in file (.ppa). If you want to run the wizard from New on the File menu, change the extension to .pwz. After changing the extension, PowerPoint identifies the template as a wizard in the New dialog box (the .pwz file needs to reside in the Template folder or a subfolder of the Templates folder).

PowerPoint supports Auto_Open and Auto_Close subroutines, which are automatically executed when add-ins are loaded and unloaded. In the case of a wizard run from the New dialog box, you can add wizard initialization code to the Auto_Open subroutine or you can call the subroutine that includes the initialization code. The following Auto_Open subroutine calls a subroutine named InitWizard, which includes instructions to initialize and display the wizard form created in this article:

Sub Auto_Open()
    Call InitWizard
End Sub

In this case, the PowerPoint wizard (an add-in with a .pwz extension) includes a module with two subroutines: InitWizard and Auto_Open.

In addition to running a wizard from the New dialog box, a wizard can be designed to modify an existing presentation. In this case, the wizard macros are again stored in an add-in, but the add-in file isn’t renamed so that it appears in the New dialog box. Instead, the add-in is loaded from Add-Ins on the Tools menu.

Using Auto_Open and Auto_Close subroutines in your add-in code module, you can control when the wizard is accessible. Menu items or toolbar buttons can be added when the wizard add-in is loaded and later removed when the add-in is unloaded. The instructions to add and remove menu items automatically runs if they are stored in the Auto_Open and Auto_Close subroutines.

Note   An add-in that is loaded during the current session is automatically loaded the next time PowerPoint starts.

Microsoft Excel Wizards

Microsoft Excel wizards, such as those included with Microsoft Excel 97, can be stored in add-in files (.xla) or templates (.xlt). If you want the wizard to run from the New dialog box, create a new workbook that includes a reference to your wizard project (use the References dialog box in the Visual Basic Editor). Save the new workbook as a template (.xlt) in the Templates or Spreadsheet Solutions folder. Use the Call statement in the Workbook_Open subroutine to call the wizard initialization routine (InitWizard) in the referenced wizard project.

The following sample Workbook_Open subroutine is stored in the ThisDocument class module. The routine calls the subroutine that initializes and displays the wizard form (in this case InitWizard) in the referenced wizard project. The If...Then structure calls the InitWizard routine if the active workbook hasn’t been saved:

Private Sub Workbook_Open()
    If ActiveWorkbook.Path = "" Then
        Call InitWizard
    End If
End Sub

Now when a new workbook is based on the template (in the New dialog box), the wizard runs. For more information on referencing another project, refer to "Distributing Microsoft Excel 97, Word 97, and PowerPoint 97 Solutions".

In addition to running a wizard from the New dialog box, a wizard can be designed to modify an existing workbook. The wizards included with Microsoft Excel, such as the Lookup and Conditional Sum wizards, use this technique. To use this method, save your Microsoft Excel workbook or template as an add-in file (.xla). Microsoft Excel add-ins can be loaded manually from Add-Ins on the Tools menu or automatically when Microsoft Excel starts. Add-ins in the \Office\XLStart folder are automatically loaded when Microsoft Excel starts.

Microsoft Excel supports Workbook_Open and Workbook_Close events, which are automatically executed when an add-in is opened or closed. Additionally, the Workbook_AddinInstall and Workbook_AddinUnInstall events execute when an add-in is loaded or unloaded from Add-Ins on the Tools menu.

You can add instructions that make the wizard accessible in the WorkBook_Open subroutine. For example, the WorkBook_Open subroutine can add a menu item or toolbar button that in turn runs the wizard. When the add-in is unloaded, the WorkBook_Close subroutine is automatically called. This subroutine can include instructions that remove the menu items or buttons added in the WorkBook_Open subroutine. Using a combination of WorkBook_Open and WorkBook_Close subroutines, your wizard can be made accessible when the add-in is loaded from Add-Ins on the Tools menu.

Note   An add-in that is loaded during the current session is automatically loaded the next time Microsoft Excel starts. To increase the speed with which Microsoft Excel loads, you can demand load an add-in. This means the add-in is loaded when the user chooses to use the add-in, instead of when Microsoft Excel starts. For more information about creating demand loaded add-ins, refer to "Distributing Microsoft Excel 97, Word 97, and PowerPoint 97 Solutions." This article includes information about how to use registry keys to create menu items that provide entry points to an add-in without loading the add-in automatically when Microsoft Excel starts.

If your wizard requires multiple menu items or toolbar buttons, you can distribute a workbook-level menu bar or toolbar with your wizard. For information on this technique, see Chapter 8 in Microsoft Office 97/Visual Basic Programmer’s Guide (MSDN Library, Microsoft Office Development).

Where Do You Go from Here?

Now that the wizard user interface is complete, you need to add additional code to perform operations based on the user’s responses to the various wizard steps. For example, after the user completes the Memo Wizard included with Word, the wizard creates a memo based on the user’s preferences. The subroutines that perform the actions can be stored in an existing or new code module included with the project.

In the existing cmdFinish_Click() subroutine (in the WizForm UserForm), add an instruction that calls a routine in a code module. For example, the following cmdFinish_Click() subroutine calls the PerformTask subroutine after WizForm is unloaded:

Private Sub cmdFinish_Click()
    If bVis Then oTheBalloon.Close
    Assistant.EndWizard WizardID:=lwizID, varfSuccess:=True
    Unload WizForm
    Call Module1.PerformTask
End Sub

Note   Any data that a user enters in WizForm is lost when WizForm is unloaded. If you return the values of controls in WizForm after the form has been unloaded, you get the initial values for the controls rather than any values the user may have entered. If you want to save the data entered by a user in WizForm, you can do so by saving the information to variables prior to unloading WizForm. For example, if you want to save the e-mail name from the txtEmail TextBox control, add the following instruction before the Unload statement in the previous example.

    strEmail = WizForm.txtEmail