Creating a Wizard

A wizard is a macro (or a set of macros) that creates a document or carries out
a task as directed by the user.

Like most computer programs, a wizard has 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 of
a wizard varies according to what the wizard is designed to accomplish; every wizard is different in that respect. But the wizards that come with Word share
a common user interface, which is described in this section.

The Microsoft Word Developer's Kit disk includes two wizards, the Starter Wizard (STARTER.WIZ on the Windows disk, STARTER WIZARD on the Macintosh disk) and the Wizard Maker Wizard (MKWIZARD.WIZ on the Windows disk, WIZARD MAKER WIZARD on the Macintosh disk), to help you get started creating your own wizards. The Starter Wizard is a "blank" wizard that contains routines (common to all wizards) that manage the user interface. The Wizard Maker Wizard constructs a wizard using the same routines as those contained in the Starter Wizard. You can then complete the new wizard, adding more controls to the wizard dialog box and including routines to carry out the task you want the wizard to accomplish.

Note

This section assumes a familiarity with dynamic custom dialog boxes. If you're not familiar with custom dialog boxes and dynamic dialog boxes, see Chapter 5, "Working with Custom Dialog Boxes."

Wizard Templates

A wizard is stored in a template. In Windows, a wizard is stored in a template saved with the filename extension .WIZ rather than .DOT. When you save a template with the extension .WIZ in your template folder, Word identifies the template as a wizard in the list of templates and wizards displayed in the New dialog box (File menu). On the Macintosh, a wizard is a Word template with the file type WIZ! (you can use the SetFileCreatorAndType statement to change a template's file type). The wizard's filename is the name that appears in the list of templates and wizards displayed in the New dialog box (File menu). To be displayed in the New dialog box, a wizard must be saved in the template folder.

Every Word wizard contains an AutoNew macro that runs when the user creates a new document based on the wizard. The AutoNew macro calls the StartWizard macro, which does all the wizard's work. In effect, the StartWizard macro is the wizard; the template is just the container in which it's stored.

The StartWizard Macro

The StartWizard macro provided in the Starter Wizard template has the same design as the StartWizard macros in the wizard templates provided with Word. Its structure separates those parts of the macro that may need to be modified for a particular wizard from those parts that can be used by every wizard without modification. The following diagram shows how the macro code is organized.


'Global variables used by all wizards
        'Variable declarations
'Global wizard-specific variables
        'Variable declarations

Sub MAIN
        'Dialog box definition
End Sub

'** Wizard-specific subroutines and user-defined functions
        'Subroutines and functions here

'** Subroutines and user-defined functions used by all wizards 'Subroutines and functions here Function DialogCtrl(id$, action, sval) 'Dialog function goes here End Function

Many routines categorized as "wizard-specific" are shared by all wizards and are included in the Starter Wizard. These routines often do not need to be customized, but they can be. Some of these routines are described in detail later in the chapter, but it isn't necessary to understand how most of the routines work to take advantage of them in wizards you develop.

The following table lists all the wizard-specific routines included in the Starter Wizard and other Word wizards.

Name

Description

DoButtonClick

Responds to the user choosing a button or selecting an option on a dialog box panel.

GetHintName$()

Returns the AutoText name for the current panel's hint. Can be customized to return different AutoText names for different controls on the same panel.

NextPanel

Determines which panel is displayed next.

PrevPanel

Determines which panel was displayed previously.

RstDialog

Retrieves the wizard dialog box settings. Can call RstDlgPref, RstDlgValPref, and RstDlgMultiLinePref.

SaveDialog

Saves wizard settings. Can call SaveDlgPref, SaveDlgValPref, and SaveDlgMultiLinePref.


The following table lists the routines in the Starter Wizard and other Word wizards that are exactly the same in every wizard; they are not designed to be customized.

Name

Description

ChangePanel

Changes the wizard dialog box panel. Calls ShowHideControls.

DisplayHint

Displays a hint when the user chooses the Hint button. Calls the GetHintName$() function.

EnableControls

Enables and disables standard wizard controls. For example, if the last panel is displayed, the Next button is disabled.

ItemsInPanel

Stores the number of controls in a panel, plus the total number of previous controls in the PanelControls array.

RstDlgPref

Retrieves a string setting. Calls xFetchPref$().

RstDlgMultiLinePref

Retrieves a multiple-line text box setting. Calls xFetchPref$().

RstDlgValPref

Retrieves a numeric setting (for example, a check box setting). Calls xFetchPref$().


Name

Description

SaveDlgPref

Saves a string setting. Calls xStorePref.

SaveDlgValPref

Saves a numeric setting (for example, a check box setting). Calls xStorePref.

SaveDlgMultiLinePref

Saves a multiple-line text box setting. Calls xStorePref.

ShowHideControls

Shows or hides the controls in a panel. (Not used on the Macintosh or in Windows NT.)

xFetchPref$()

Retrieves settings from a settings file.

xStorePref

Stores settings in a settings file.


Note

The names of some routines in the Starter Wizard are slightly different from the names of those in the wizards that ship with Word. For example, the ShowHideControls subroutine is called SHControls in the Word wizards. The Starter Wizard uses the term "panel" to refer to a set of dialog box controls displayed at the same time, because that term is used in this book; the wizards that ship with Word use the term "state." Thus, subroutines in the Starter Wizard such as NextPanel and PrevPanel are called NextState and PrevState in the Word wizards.

The Wizard Interface

The interface shared by all the wizards included with Word consists of a dialog box with several panels. Each panel in the dialog box has its own set of controls, and each panel also shares a set of controls used to move between panels and to dismiss the dialog box. These standard controls are shown in the following illustration.

The Wizard Dialog Box Definition

A dialog box definition defines all the controls in a dialog box. Definitions for dialog boxes with more than one panel, such as wizard dialog boxes, are quite long because the definition must include the controls for all the dialog box panels.

In the wizard dialog box definition, the instructions defining the standard controls shared by every panel are defined first, followed by the controls for the first panel, the controls for the second panel, and so on.

Here is the dialog box definition in in the Starter Wizard's StartWizard macro:


Begin Dialog UserDialog 628, 276, wizname$, .DlgControl
'Controls shown in every panel: 0-8
    Picture 0, 238, 500, 11, "LinePic", 1, .LinePicA                    '0
    Picture 128, 238, 500, 11, "LinePic", 1, .LinePicB                '1
    OKButton 11, 215, 73, 21                                            '2
    PushButton 229, 250, 73, 19, "&Hint", .Hint                        '3    
    CancelButton 313, 250, 80, 19                                    '4
    PushButton 401, 250, 71, 19, "<&Back", .Back                        '5
    PushButton 471, 250, 71, 19, "&Next>", .Next                        '6
    PushButton 550, 250, 75, 19, "&Finish", .FastForward                '7
    FilePreview 16, 12, 250, 214, .Preview                            '8
    StateItems(0) = 9
'Panel 1
    'Controls for the first panel go here
    ItemsInPanel 0    'The number of controls in 1st panel
'Panel 2
    'Controls for the second panel go here
    ItemsInPanel 0    'The number of controls in 2nd panel
'Panel 3
    'Controls for the third panel go here
    ItemsInPanel 0    'The number of controls in 3rd panel
End Dialog

The dialog box definition in the StartWizard macro is set up with nine standard controls. All these controls — except the file preview box and the OK button —
are displayed in every panel of the wizard dialog box. The file preview box is included in the standard controls because there can be only one per dialog box, and when a Word wizard uses this control in a panel, the control is always in the same location in the dialog box. Note that in Windows the line graphic displayed by the two picture controls is stored as two AutoText entries in the wizard template, rather than in a separate file. This ensures that the graphic is always available. (Two picture controls are necessary to create a line that extends the full width of the dialog box. On the Macintosh, only one picture control is needed, and the graphic is stored in a Macintosh resource fork.) The rest of the dialog box definition has placeholders for three panels, but you can add more panels as you need them.

Two other instructions within the dialog box definition require explanation: PanelItems(0) = 9 and ItemsInPanel 0. PanelItems() is an array defined (before the dialog box definition) with as many elements as the dialog box has panels. Each element of the array will store the number of controls in its corresponding panel, plus the total number of controls defined for previous panels. The standard controls that are defined first are stored in the element numbered 0 (zero), the controls for the first panel are stored in the element numbered 1, and so on. The instruction PanelItems(0) = 9 indicates that there are nine standard controls (or "items").

For the other panels, the subroutine ItemsInPanel is used to assign the number
of items to the PanelItems() array. The ItemsInPanel subroutine increments the array index number and adds the number of controls stored in the current panel to the total number of controls defined to that point in the dialog box definition. For example, if there are two controls in the first panel, the number 2 is added to the number of standard controls, 9, for a total of 11 controls. Other subroutines in the macro will be able to manipulate this array to hide or show the items in a panel, which is the most important task in managing the different panels. Here is the ItemsInPanel subroutine:


Sub ItemsInPanel(howMany)
LastPanel = LastPanel + 1
PanelItems(LastPanel) = howMany + PanelItems(LastPanel - 1)
End Sub

The global variable LastPanel stores the index number of the PanelItems() array. The variable howMany is the number of items in the current panel, which is passed to the subroutine when it is called.

Numeric Identifiers

The order of the controls in the StartWizard dialog box definition is important because the instructions in the dialog function that display and hide these controls use numeric identifiers. The numeric identifier for a control is determined by its position within the dialog box definition. Numeric identifiers are used because they are much faster than string identifiers in dialog boxes with many controls. Numeric identifiers are also efficient because you can use loops to operate on a group of controls. In wizards, loops are used to hide and display all the controls
in a dialog box panel.

As you're developing a wizard, you should use string identifiers rather than numeric ones, so that you can add and delete controls from the dialog box definition without having to change all the instructions that act on those controls. Then, when you have finished the dialog box, you can convert to numeric identifiers for better performance.

Managing the Dialog Box Panels

Each time the user chooses a button to move to a different panel, the controls
in the current panel must be hidden and the controls in the new panel must be displayed. In addition, if the user moves to the first panel, the Back button must be disabled; if the user moves to the last panel, the Next button must be disabled. The task of managing panels is shared among the various subroutines described here.

Before turning to those subroutines, though, consider a portion of the dialog function that determines which action to take if a user chooses one of the standard wizard buttons. Each Case instruction in the following Select Case control structure corresponds to the numeric identifier of one of the wizard's standard buttons. Note that Case 5 and Case 6 correspond to the user choosing the Back and Next buttons. In both cases, the ChangePanel subroutine is called.


Select Case idnum
    Case 3                'Hint
        DisplayHint
    Case 4                'Cancel, so exit dialog box
        fRet = 0
    Case 5                '<Back
        ChangePanel(Panel, PrevPanel(Panel))
    Case 6                'Next>
        ChangePanel(Panel, NextPanel(Panel))
    Case 7                'Finish, so exit dialog box
        fRet = 0
    Case Else
        'Shouldn't happen...
End Select

The ChangePanel subroutine takes two arguments: Panel (the number of
the current panel) and PrevPanel(Panel) if the Back button is chosen or NextPanel(Panel) if the Next button is chosen. PrevPanel() and NextPanel()
are user-defined functions that determine which panel is the next panel. Usually,
this is simply Panel + 1 or Panel - 1. Here is the NextPanel() function:


Function NextPanel(oldPanel)        'Determine the panel following oldPanel
If oldPanel = LastPanel Then
    NextPanel = oldPanel            'Safeguard -- do not modify
Else
    NextPanel = oldPanel + 1        'The normal, default case
End If
End Function

ChangePanel

The ChangePanel subroutine calls the ShowHideControls subroutine to hide the current panel and display the new one. Also, ChangePanel either hides or shows the FilePreview control and calls the EnableControls subroutine. The old argument corresponds to the current panel that will be hidden; the new argument indicates the new panel that will be displayed.


Sub ChangePanel(old, new)
ShowHideControls(PanelItems(old - 1), PanelItems(old), 0)
DlgVisible "preview", 1 - HideFilePreview(new)
ShowHideControls(PanelItems(new - 1), PanelItems(new), 1)
Panel = new
EnableControls
End Sub

ShowHideControls

This subroutine is called to hide the current panel and display the new one. (This subroutine is only necessary in Word version 6.0 for Windows. In Word version 6.0 for the Macintosh and Windows NT, and in Word version 7.0, a single DlgVisible instruction can show or hide a range of dialog box controls.) If ShowHideVal is equal to 1, the controls are shown; if it's equal to 0 (zero), they're hidden. The variables FirstControl and LastControl are the key values. They control the For¼Next loop, which hides or shows all the controls in a panel: FirstControl corresponds to the numeric identifier of the first control in a panel and (LastControl - 1) corresponds to the numeric identifier of the last control in a panel.

When the ChangePanel subroutine calls ShowHideControls to hide the current panel, it passes PanelItems(old - 1) as FirstControl. Each element in the array PanelItems(), you recall, corresponds to a panel in the dialog box. The number assigned to each element is the total number of dialog box controls defined up through the last control on the corresponding panel. For example, if 25 controls are defined for the first two panels (including the standard controls), PanelItems(2) is assigned the value of 25. If the third panel has 10 items, PanelItems(3) is assigned a value of 35. One final piece of the puzzle: WordBasic assigns numeric identifiers to dialog box controls starting with 0 (zero). So if a dialog box definition contains 35 controls, the last control is numbered 34. To hide the 10 controls in the third panel, you could write a loop that iterates from 25 to 35 – 1:


For i = 25 To (35 - 1)
    DlgVisible i, 0
Next

That is just what the ShowHideControls subroutine does:


Sub ShowHideControls (FirstControl, LastControl, ShowHideVal)
For i = FirstControl To LastControl - 1
    DlgVisible i, ShowHideVal
Next
End Sub

EnableControls

This subroutine controls which wizard button has the focus and which buttons are enabled. When the first panel is displayed, the Next button gets the focus and the Back button is disabled. When the last panel is displayed, the Next button is disabled and the Finish button gets the focus. Otherwise, both the Back button and the Next button are enabled, and the focus remains on the button chosen last.


Sub EnableControls
    If Panel = 1 Then
        DlgFocus 6                    'Give the Next> button the focus
        DlgEnable 5, 0                'Disable the <Back button
    ElseIf Panel = 2 Then
        DlgEnable 5, 1                'Enable the <Back button
    ElseIf Panel = LastPanel - 1 Then
        DlgEnable 6, 1                'Enable the Next> button
    ElseIf Panel = LastPanel Then
        DlgFocus 7                    'Give the Finish button the focus
        DlgEnable 6, 0                'Disable the Next> button
    EndIf
End Sub

Storing Wizard Settings

Every wizard contains a set of subroutines that save and retrieve settings, so
the wizard can "remember" the user's choices and present them as defaults the
next time the user runs the wizard. Some of the subroutines are designed to be customized for different wizards and some remain the same in every wizard.

On the Macintosh, DlgStoreValues and DlgLoadValues() are used to save and retrieve multiple settings. These instructions are not available in Windows, so the subroutines to save and retrieve settings are more complex. This section describes the Windows subroutines.

The SaveDialog subroutine saves the wizard dialog box settings. You can call three subroutines from the SaveDialog subroutine: SaveDlgValPref to save numeric values such as check box settings; SaveDlgPref to save the contents of a text box; and SaveDlgMultiLinePref to save the contents of a multiple-line text box. Each of these subroutines takes as an argument the string identifier of the control whose setting is to be saved. For example, the following instruction saves the setting of the control with the identifier Panel4CheckBox:


SaveDlgValPref "Panel4CheckBox"

Here is the framework of the SaveDialog subroutine:


Sub SaveDialog
'Panel 1
'Statements to save settings in panel 1 go here
'Panel 2
'Statements to save settings in panel 2 go here
'Panel 3
'Statements to save settings in panel 3 go here
'Panel 4
'Statements to save settings in panel 4 go here
End Sub

Likewise, there are three corresponding subroutines you can call from the RstDialog subroutine to restore settings to a newly opened wizard dialog
box: RstDlgValPref, RstDlgPref, and RstDlgMultiLinePref. Each subroutine takes two arguments: the string identifier of the control whose setting is to be restored and a default setting. The default setting is used only when there is no value to restore (the first time the wizard is used). For example, the following instruction restores the setting of the control with the identifier Panel4CheckBox; the default value is 0 (zero), which means the check box
is cleared:


RstDlgValPref "Panel4CheckBox", 0

The following example restores the setting of a text box with the identifier "Panel2TextBox"; the default text is an empty string (leaving an empty text box):


RstDlgPref "Panel2TextBox", ""

Here is the framework of the RstDialog subroutine:


Sub RstDialog
'Panel 1
'Statements to restore settings in panel 1 go here
'Panel 2
'Statements to restore settings in panel 2 go here
'Panel 3
'Statements to restore settings in panel 3 go here
'Panel 4
'Statements to restore settings in panel 4 go here
End Sub