Side of Software

 

 

Home

 

Contact Us

 

 

 Products

 

Dated Collections

 

Marker Bar

 

Persistence

 

Print Preview

 

Reports

 

Wizards

 

FAQs

Tutorial

 

API (javadocs)

Wizard Library Tutorial

Table of Contents

Introduction

The JWizard Component

The Wizard Model

Wizard Pages

Customizing a Wizard

Example

Introduction

Most user-friendly applications use wizards to step the user through difficult or critical tasks. Such a task may, for example, install software, create a user account, or export data to a file. Wizards make these tasks easier by requesting one piece of information at a time rather than overwhelming users with all information at once. In addition, wizards often give users the opportunity to verify their input, which leads to fewer errors and a more enjoyable user experience.

 

The core Java libraries do not provide high-level support for wizards. As a result, developers either implement their own wizards or resort to using an overcrammed traditional dialog. The Wizard Library provides a framework for which to create wizards. It leads to a common appearance and behavior not only within an application but also across applications. By default, the wizards in this library follow the guidelines set forth by the JavaTM Look and Feel Design Guidelines: Advanced Topics by Sun Microsystems, Inc.

                                                                                                                               

The Wizard Library does not provide content for the wizardís pages. It is not an application that generates wizards for you, such as InstallShield or InstallAnywhere. Rather, it is a general-purpose library that allows youóthrough programmingóto create wizards for any task. You can certainly use it to create an installation wizard, but it is your responsibility to provide the appropriate page content and wizard task.

 

Before you start using this library, you should download and include on your classpath the jar file of the Java Look and Feel Graphics Repository. It is available free-of-charge here. Without this resource, your wizards will not display any button icons.

The JWizard Component

The library includes JWizard, a Swing component that steps the user through a sequence of pages. It generally has the following layout:

 

 

 

 

The side panel, if one exists, is often a graphic, a list of steps, or a textual source of additional help. For more information on the side panel, see Side Panel.

 

The page panel is the editable area in which the user inputs information needed to carry out the wizardís task. The wizard model provides the content of this panel, which changes as the user steps through the pages. For more information on the page panel, see The Wizard Model and Wizard Pages.

 

The button panel contains navigation buttons (such as Next and Back) and action buttons (such as Finish and Cancel) that allow the user to control the wizard. For more information on the button panel, see Buttons.

 

Although an instance of JWizard can be placed in any container, it typically appears in a standalone dialog. The following code snippet demonstrates a typical use of a JWizard object.

 

  // create the model that determines the pages
  WizardModel wizardModel = ...
 
  // create the Swing component
  JWizard wizard = new JWizard( wizardModel );
 
  // show the modal dialog
  int result = wizard.showDialog( parentFrame, ďWizard TitleĒ );

 

The value returned from showDialog indicates if the wizard finished successfully.

Side Panel

You can place any component in the wizardís side panel. Simply create the component and invoke

 

  void setAccessory( JComponent accessory );
 

When sizing the dialog, the layout manager tries to respect the side panelís minimum and maximum sizes. If you do not explicitly set this accessory component or if you set it with null, then one of two constants determines what to use in the side panel.

 

Default Side Panel

Description

DEFAULT_ACCESSORY_NONE

Do not use a side panel.

DEFAULT_ACCESSORY_STEPS

Include a side panel that displays a list of the wizardís steps.

 

The default value is set using:

 

  void setDefaultAccessory( int defaultAccessory );
 

By default, the value is DEFAULT_ACCESSORY_NONE. The constant DEFAULT_ACCESSORY_STEPS produces a side panel similar to:

 

 

The actual steps displayed in the list are fetched from the wizard model. For more information, see The Wizard Model.

                                                                                                                                                               

To hide the side panel, you must ensure that the accessory component and default accessory are set to null and DEFAULT_ACCESSORY_NONE, respectively.

Buttons

The buttons in the JWizard component allow the user to step forward and backward through the pages, to cancel the wizard, to execute the wizardís task, and sometimes to provide additional assistance.  On a Windows machine in an English locale, the various button panel configurations appear as:

                                                         

 

 

 

 

When the last page is displayed, a Finish button appears on the button panel. This button replaces the Last button, if one exists; otherwise, it replaces the Next button. To change the Finish buttonís text, tool tip, or mnemonic, use the following JWizard methods:

 

† void setFinishButtonText( String finishButtonText );
† void setFinishButtonToolTipText( String finishButtonToolTipText );
† void setFinishButtonMnemonic( int finishButtonMnemonic );

 

All of the buttons except the Help button depend on the state of the underlying wizard model. The Help button is controlled through the wizard component itself via:

 

  void setHelpButtonIsShown( boolean shown );
 

In JWizard, you also have the option to hide all of the buttons:

 

  void setControlButtonsAreShown( boolean shown );

 

You may want to do this, for instance, to treat a wizard as a browser or as a slide show. Hiding the buttons, however, makes you responsible for advancing the pages. You should invoke the following methods, as needed:

 

  void doCancel();
  void doClose();
  void doHelp();
  void doBack();
  boolean doLast();
  boolean doNext();
  boolean doFinish();

 

If you wish to know when one of the buttons is pressed, you can register an action listener with the JWizard component:

 

  sampleWizard.addActionListener( new ActionListener() {
    public void actionPerformed( ActionEvent event )
    {
      String command = event.getActionCommand();
      
      // respond to the Help button
      if( JWizard.HELP_ACTION.equals( command ))
        JOptionPane.showMessageDialog( sampleWizard,
          "The programmer is responsible for responding to the " +
          "Help button." );
    }
  } );

 

The Wizard Model

The wizard model controls the sequence of pages in the wizard. It has several responsibilities.

 

First, it keeps track of the current page. You can retrieve the current page with a call to

 

  Page getCurrentPage();

 

For more information on wizard pages, see Wizard Pages. Second, it controls the stepping through the pages and the execution of the task. The methods that control these are:

 

  void stepNext();
  void stepBack();
  void stepLast();
  void finish();
  void cancel();

 

Not all actions are available at all times. The following methods state whether an action is available.

 

  boolean canStepNext();
  boolean canStepBack();
  boolean canStepLast();
  boolean canFinish();
  boolean canCancel();

 

Some models may not be able to support jumping to the last page in the sequence. In this case, they return false from

 

  boolean supportsLast();

 

Third, the wizard model maintains a state.  If the wizard model is gathering user input, then its state is WIZARD_IN_PROGRESS. If it is in the middle of executing its task, the state is WIZARD_FINISHING. When the task completes, the model moves into a state of WIZARD_FINISHED. If at any point the model is aborted, its state becomes WIZARD_CANCELED.

 

Last, the wizard model keeps track of the steps. The steps are listed in the componentís side panel, depending on the componentís settings. Steps and pages are entirely unconnected in the WizardModel interface, but, in practice, a step will correspond to a page or possibly a sequence of pages. The following methods deal with steps:

 

  public void addListDataListener(ListDataListener listener);
  public Object getElementAt(int index);
  public Object getSelectedItem();
  public int getSize();
  public void removeListDataListener(ListDataListener listener);

 

The Wizard Library provides one concrete implementation of this interface: DefaultWizardModel. The DefaultWizardModel class is a generic, domain-independent implementation that should prove sufficient for most needs. It can handle both static and dynamic page sequences, execute an arbitrary task, show a progress page, and show a final summary page.

 

DefaultWizardModel maintains a list of pages and allows the client to iterate through these pages until it reaches a page that you have denoted as the last page. At the last page, the finish action becomes enabled. If the client invokes finish, the model executes a task that you have supplied and advances to the progress and summary pages.

 

The use of DefaultWizardModel is described in detail in the following sections.

The Finish Task

When the user hits the wizardís Finish button, the wizardís task executes. DefaultWizardModel will execute your task if you supply it as an object that implements the Task interface. The Task interface extends java.lang.Runnable with the following behavior:

 

  • Inform listeners of its progress (optional, but strongly recommended)
  • Handle interruption (optional)
  • Pause and resume (optional)
  • Be comprised of sub-tasks (optional)

 

Since wizard tasks are application specific, the Wizard Library does not provide a complete implementation of Task; however, it provides AbstractTask to simplify the creation of your task. When you subclass AbstractTask, you need only provide an implementation of the run method, which executes the application-specific code. As your task executes it should invoke setProgress as often as possible to notify listeners that its progress has changed. A time-consuming task is user-friendlier if the user can interrupt it in mid-execution. If your task can handle interruption, you should override isInterruptible to return true and periodically check for interruption inside run by invoking shouldAbort. Pausing and resuming are handled by AbstractTask.

 

The following is a sample task that does nothing but sleep 100 times (your task will perform something a bit more useful):

 

  Task task = new AbstractTask( "Finishing wizard..." ) {
    public void run()
    {
      for( int i = 1; i <= 100; i++ )
      {
        if( shouldAbort() )
          break;
 
        try
        {
          Thread.currentThread().sleep( 30 );
        }
        catch( InterruptedException ie )
        {
          // ensure threadís interrupted flag remains set
          Thread.currentThread().interrupt();
        }
        setProgress( i );
      }
    }
 
    // override to indicate that this task is interruptible
    public boolean isInterruptible()
    {
      return true;
    }
  };

A Static Model

If the entire sequence of pages is known at the time the wizard appears, we call this a static, or unconditional, model. The next page to display never depends on the userís input in a previous page. The easiest way to create a static wizard model is to create the pages and task upfront and invoke the following constructor:

 

  DefaultWizardModel( Page[] pages, Task finishTask, Page summaryPage );

 

This constructor will automatically create a step for each page and designate pages[pages.length] as the last page. If you cannot provide all the information at the time of construction, you can build the model piecemeal with the following methods:

 

  void addLast( Page page );

  void setLastPage( Page page );

  void setFinishTask( Task finishTask );

  void setSummaryPage( Page summaryPage );

A Dynamic Model

If the page sequence depends on the userís input, we say that the model is dynamic, or conditional. In actuality, DefaultWizardModel associates a transition with each page. The transition updates the model when the user leaves a page or returns to a page. With a static model, the transition is simple: move to the next page in the internal list. With a dynamic model, the transitions may be complicated. You specify the transitions when you add pages:

 

  void addLast( Page page, Transition transition );
  void push( Page page, Transition transition );

 

push is the same as addLast except that it removes all subsequent pages before appending the new page.

 

The Transition interface requires the implementation of two methods:

 

  void stepNext();
  void stepBack();

 

In the first method, you will typically add pages, update the last page, and update the finish task as appropriate. In the second method, you will typically remove the pages that you added in the first method.

 

The following example illustrates the use of a transition. The transition uses the selection of a radio button to determine the subsequent pages.

 

  final DefaultWizardModel model = new DefaultWizardModel();
 
  final JRadioButton option1RadioButton = new JRadioButton( "Show pages 2-4", true );
 
  final Page page4 = ...
 
  DefaultWizardModel.Transition selectionPageTransition = 
                     new DefaultWizardModel.TransitionAdapter() {
    public void stepNext()
    {
      if( option1RadioButton.isSelected() )
      {
        Page page2 = ...
        Page page3 = ...
          
        model.push( page2 );
        model.addLast( page3 );
        model.addLast( page4 );
      }
      else
      {
        Page page2a = ...
          
        model.push( page2a );
        model.addLast( page4 );
      }
    }
      
    public void stepBack()
    {
      int numDynamicPages = option1RadioButton.isSelected()? 3 : 2;
      for( int i = 0; i < numDynamicPages; i++ )
        model.removeLast();
    }
  };
    
  model.addLast( selectionPage, selectionPageTransition );
  model.setLastPage( page4 );

Wizard Pages

We have discussed the wizard component and the wizard model, but we have not addressed the wizard page. Each wizard page must implement the Page interface. Recall that the page is retrieved via the modelís getCurrentPage method. A Page is, in short, an object that has the ability to specify if it is complete and valid, as defined by the following methods:

 

  boolean isComplete();

  void validate() throws InvalidPageException;

 

Although it is the developerís responsibility to populate the wizard model with pages relevant to the task, the library provides two concrete implementations of this interface: OptionPage and ProgressPage. The former is a generic page that can display any information, while the latter shows the progress of the wizardís task. We describe each page in detail in the next sections.

 

A page is part of the model (in the Model-View-Controller architecture), so it is not responsible for its own rendering and it does not have to implement javax.swing.JComponent. Rather, it is viewed and edited with a page editor. The PageEditor interface defines the editorís responsibilities:

 

  void cancelEditing();

  Component getEditorComponent( JComponent c, Page page );

  boolean stopEditing();

 

The method getEditorComponent takes a page and converts it into a graphical component to be rendered in the wizard. The page is in the state of being edited until an invocation of either cancelEditing or stopEditing occurs. The latter method commits any changes and validates the page. If the edited page is not valid, the method returns false.

The OptionPage Page

OptionPage is a generic page much as javax.swing.JOptionPane is a generic panel. An instance of OptionPage consists of a name and a message. The message may be of any type.

 

The appearance of an OptionPage in the wizard depends on the editor responsible for rendering it. By default, an OptionPageEditor is used to edit an OptionPage. The OptionPageEditor maps the message (based on its type) to a number of components and places them in a panel along the page axis. It follows rules similar to JOptionPane:

 

  • Treat an array recursively
  • Place an instance of java.awt.Component directly into the panel
  • Wrap an icon in a javax.swing.JLabel
  • Invoke String.valueOf on all other objects and place the result into a javax.swing.JTextPane

 

The following example demonstrates the creation of an OptionPage with HTML text and a text field

 

  final JTextField titleTextField = new JTextField( "My First Wizard" );
    
  Object[] titleMessage = new Object[] {
    "<html>Provide a title for your wizard.<p><html>",
    titleTextField,
    "<html><p><p>Each page can specify when it is incomplete " +
    "or invalid. This page is not complete if the above text field " +
    "is empty. Note that the <b>Next</b> and <b>Last</b> buttons " +
    "become disabled if the page is incomplete. This page is invalid " +
    "if the supplied text contains a non-alphanumeric character. Try " +
    "typing \"Wizard?\" and clicking <b>Next</b>.</html>"
  };
    
  final OptionPage titlePage = new OptionPage( "Wizard Title", titleMessage );

 

The editor renders it as:

 

 

By default, an OptionPage is always complete and valid. To control when your page is complete, either override isComplete or invoke setComplete when the pageís state changes. To control if your page is valid, you can override validate as is done here:

 

  final OptionPage titlePage = new OptionPage( "Wizard Title", titleMessage ) {
    public void validate() throws InvalidPageException
    {
      String title = titleTextField.getText();
      for( int i = 0; i < title.length(); i++ )
      {
        char ch = title.charAt( i );
        if( !Character.isLetterOrDigit( ch ) && !Character.isWhitespace( ch ))
        {
          titleTextField.requestFocus();
          throw new InvalidPageException( "The title may only contain letters and digits." );
        }
      }
    }
  };

The ProgressPage Page

DefaultWizardModel uses a progress page if you have supplied a finish task and have called setProgressPageIsUsed with true. The model constructs a page of type ProgressPage, which ultimately gets rendered with a ProgressPageEditor.

 

If the wizard task is interruptible, the page displays as follows (depending on the names of the page and task):

 

 

In most cases, you will not need to concern yourself with this page. You simply must decide if your task is long enough to warrant a progress bar and invoke setProgressPageIsUsed. By default, the progress page editor stops the task immediately when the user hits the Stop button. To bring up a confirmation dialog, however, you will need to subclass ProgressPageEditor and override confirmStop.

Custom Pages

You may find it worthwhile to create your own type of page. If you create an implementation of Page, you must also create an editor that is responsible for rendering this type of page.  Your new classes can extend AbstractPage and AbstractPageEditor to inherit some of the functionality. Last, you must also register your editor with the JWizard component, using the method

 

  void setDefaultPageEditor( Class pageClass, PageEditor pageEditor );

 

Customizing a Wizard

You can further customize your wizard by defining look-and-feel properties. At application start-up, define the properties you wish to change with the call

 

  UIManager.put( propertyKey, propertyValue );

 

where propertyKey specifies the BasicWizardUI property and propertyValue is the desired value. The type of propertyValue depends on the property key. The key should be one of the following keys:

 

Property Key

Value Type

Description

ACCESSORY_PERCENTAGE_KEY

java.lang.Double

The amount of space the side panel should occupy, as a percentage of the total width. The UI will honor this percentage unless the maximum and minimum widths of the side panel and button panel do not allow it.

ANCESTOR_INPUT_MAP_KEY

javax.swing.InputMap

A mapping of keyboard events to action names.

BACKGROUND_KEY

java.awt.Color

The wizardís background color.

DIALOG_PREFERRED_HEIGHT_KEY

java.lang.Integer

The preferred height of a wizard dialog.

DIALOG_PREFERRED_WIDTH_KEY

java.lang.Integer

The preferred width of a wizard dialog.

FOREGROUND_KEY

java.awt.Color

The wizardís foreground color. Page editors may use this value to initialize page components.

FONT_KEY

java.awt.Font

The wizardís font. Page editors may use this value to initialize page components. The UI uses this font in its default side panel.

STEPS_BACKGROUND_KEY

java.awt.Color

The background color of the side panel that contains a list of wizard steps.

TITLE_FONT_KEY

java.awt.Font

The font used for wizard titles. Page editors may use this value to initialize page conponents. The UI uses this font in its default side panel.

 

Example

We put most of the ideas discussed above into a complete example, called WizardDemo. The program uses a wizard to step you through the creation of your own wizard. The first wizard illustrates the use of a static wizard model without a finish task. Your custom wizard illustrates the use of a dynamic model with a finish task, a progress page, a summary page, and a Help button.

 

You can run this sample application on the libraryís homepage.

 

 

Home  |  Contact Us  |  Privacy Policy

 

Copyright © 2016 Side of Software, a branch of Natavision. All rights reserved.