X

Geertjan's Blog

  • September 28, 2005

How Wizards Work (Part 2): Different Types

Geertjan Wielenga
Product Manager
When I was about 12, I used to be a big fan of the 'Choose Your Own Adventure' books. They had titles like: "Prisoner of the Ant People" and "The Magic of the Unicorn". On page 1, the scene would be set—for example, you'd wake up smothered by vampires drinking your blood. What do you do? And then the book would helpfully give you some options: "Run around screaming? Turn to page 27." "Cut them off with the Sword of Destiny? Turn to page 8." "Curse them under your breath? Turn to page 92." And then you'd obediently turn to the appointed page and walk into a snake pit or find yourself parachuting behind enemy lines in wartime France or find (yet another) Sword of Destiny.

NetBeans IDE 5.0 allows you to do the very same thing. (No, not parachute behind enemy lines in wartime France.) But, it's almost just as exciting—'Write Your Own Wizard' means, essentially, 'Create Your Own Adventure'. You could do it the simple way—the user must work through a page, click Next, work through the next page, click Next, work through the next page, click Next, work through the final page, and then click Finish. But life often isn't that simple. Martin Krauskopf (the creater of the Wizard wizard in NetBeans IDE 5.0) sent me a few wizards that illustrate the point really well. Here's an example of a simple wizard (click to enlarge):


Here you can see that you can choose either 'Tea', 'Coffee', or 'Water'. But only if you choose 'Tea' or 'Coffee' does it make sense to go to the next page, which lets you say 'Yes' or 'No' to sugar. For this reason, the first page implements the WizardDescriptor.FinishablePanel interface. By implementing this interface, you allow the Finish button to be enabled if 'Water' is selected:

public boolean isFinishPanel() {
return getDrinkType() == MenuVisualPanel1.WATER;
}

This is the only method required by the WizardDescriptor.FinishablePanel interface (in addition to those required by its superinterface, WizardDescriptor.Panel). However, the isValid() method must also be set to true, which is set to true by default. So, WizardDescriptor.FinishablePanel enables you to end your adventure prematurely. There are many more pages that you could work through, potentially, but why should you? You chose water and it doesn't make sense to specify whether or not you want sugar.

However, a cool thing about NetBeans is that each step in the wizard consists of two components (kind of like how each web page in Wicket consists of two components): a Java class that implements WizardDescriptor.Panel (for example, WizardDescriptor.FinishablePanel, described above) and a Java class that implements JPanel. According to this document, "The WizardDescriptor.Panel subclass should reference the JPanel subclass, instantiate and return an instance of that class the first time its java.awt.Component getComponent() method is called. This is the safest, performant and also the preferred way to construct wizard steps." So, when you use the Wizard wizard, this method is automatically generated for you in the WizardDescriptor.Panel subclass (which, in this case, was called MenuWizardPanel1, which is a name that correlates with the name of the visual panel instantiated below, although the names don't have to be similar at all):

public Component getComponent() {
if (component == null) {
component = new MenuVisualPanel1();
}
return component;
}

Similarly, the getDrinkType() method that is called in the isFinishPanel() method interacts with the visual component:

private int getDrinkType() {
return ((MenuVisualPanel1) component).getDrinkType();
}

However, note that irrespective of your choice, you have the same number of pages. If there were a third page, you wouldn't be able to skip the second. (And even if you choose 'Water', the Next button is always enabled. Hmmm. Must be some way to disable the Next button.) Why can't you skip pages? What happened to 'Choose Your Own Adventure?' How adventurous does NetBeans let you be if you can't let the user skip around from page to page? Good questions. And the answer is... the WizardDescriptor.Iterator interface. Here, for example, is what you could implement; thanks again to Martin for this wizard (click to enlarge):


Here you can see that only if you choose 'Drink' will Next take you to the Drink page (and only if you choose Food will 'Next' bring you to the Food page). In terms of the Wizard wizard, and the NetBeans APIs, the above scenario is completely different to the previous one. In fact, when you use the Wizard wizard, the first panel forces you to specify which wizard you want (click to enlarge):


Above, if you choose 'Simple', you cannot let the user diverge from the given number of steps (i.e, although it is possible to let the user prematurely finish the wizard, using WizardDescriptor.FinishablePanel, you cannot let the user skip a page). If you choose 'Variable', you can let the user branch off as much as you allow. This means that in the second scenario there's a lot more work for you than in the first. In 'Number of Steps' above, you specify the number of steps—i.e., for each step you'll get two files, a Java class that implements WizardDescriptor.Panel and a Java class that implements JPanel. The interaction between these two was briefly outlined above (i.e., instantiation via the getComponent method and calling methods on the visual component from the WizardDescriptor.Panel class).

There's actually a lot more to say about wizards—here's just a few off the top of my head:

  • The readSettings() and storeSettings() methods take care of the state.
  • Iteration for 'Variable' wizards is via the generated WizardDescriptor.Iterator subclass; for 'Simple' wizards, you just need to invoke the wizard and the iteration is linear, so you don't need to provide an iterator.
  • There's a lot going on with change listeners.

The above are some of the things I need to look into before writing a tutorial on all of this...

Update: There are more parts to this series... Part 1, Part 3, Part 4, and Part 5.

Join the discussion

Comments ( 2 )
  • guest Sunday, March 21, 2010

    You did not specify how to make skip one or more wizard panels using an Iterator. It would be a great help if you demonstrate such a wizard


  • Christian Monday, September 1, 2014

    Hello Geertjan,

    I got problems with using the valid-function during runtime.

    My return-value is:

    @Override

    public boolean isValid() {

    return valid;

    I am using a JTree and if a node is selected the "Next"-Button should be enabled and if a node is not selected the "Next"-Button should be disabled again.

    So valid has to be true or false.

    So far so good....

    Via .setValid(true) or .setValid(false) in my VisualPanel-Class (using TreeSelectionListener and valueChanged-Event) I can pass the correct value but nothing happens...

    What do I have to consider using the valid-Function during runtime? (I cannot use readSettings - or storeSettings because they catch the event to the wrong time - not runtime).

    Thanks a lot

    Christian


Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.