Creating JFace Wizards
Summary
This article shows you how to implement a wizard using the JFace toolkit and how to contribute your wizard to the Eclipse workbench. A wizard whose page structure changes according to user input is implemented to demonstrate the flexibility of wizard support.
Introduction
Wizards are used extensively throughout Eclipse. You can use wizards to create a new Java class or new resources like Projects, Folders or Files. A well designed wizard can considerably simplify user tasks and increase productivity.
Wizards are meant to take the hassle out of standard, repetitive, or tedious user tasks. For example, the Java New Class wizard can collect enough information to generate a skeleton implementation of a user's class, including package statements, constructors, inherited methods, and other details. Of course, as the wizard developer, you must implement the code that makes the wizard useful for your domain.
Not only does the platform contain many wizards, but there is a
lot of support for writing your own. The JFace wizard framework lets you
concentrate on the specifics of your wizard implementation. You will
need to use the org.eclipse.jface.wizard
package of JFace.
It is very easy to get started while the support is flexible enough to
allow you to add more complex logic to your wizards.
Wizard sample
Our sample wizard will gather some holiday travel choices from the user and collect more information based on the user's initial choices. Information about the holiday is kept in a model data object which is manipulated by the wizard page. The user's holiday data will be displayed in an information dialog upon completion of the wizard.
Running the Wizard
To run the sample or view its source, unzip the com.xyz.article.wizards.zip (updated July 2007 for Eclipse 3.3) into your eclipse root directory and restart the workbench. You can start the sample wizard from the New button or from File>New menu of the workbench (Figure 5). Alternatively, you can select the context menu of a folder (in any perspective) and start the wizard from there (Figure 6).
Let's look at our sample wizard in detail before diving into details of implementing it. On the first page the users can select the dates of travel, the type of transport for their holiday and enter the departure and destination locations:

Figure 1. Starting page of the wizard
The next page to be shown depends on the selected mode of transport. If the user has selected travel by plane the following page is displayed which shows the available flights. To keep the example code simple this information will be hard coded, rather than obtained from some database. The user can select the type of seat they want and to ask for the ticket price by pushing the "Get price" button. The base price is hard-coded as well. A discount is offered in conditions explained below.

Figure 2. Page displayed when the user has selected the plane
When the user has selected a flight and a type of seat the wizard can be finished.
If the user has selected the car as mode of transport, a different page is shown. The user can select the name of a rental company. Based on the company name, the price of the rented car is displayed. Once again, the prices are hard-coded and depend only on the rental company selected but not on dates and destination. The user can select whether to buy insurance from the rental company.

Figure 3. Page displayed when the user has selected the car.
When the user clicks Finish a message dialog is displayed summarizing the holiday data collected from the user. The wizard responds to various events and reports user errors.
This article explains the following:
- how to create, add and initialize wizard pages
- how to listen for events and control errors
- how to change the page order
- what to do on completion of a wizard
- how to start a wizard
Wizard Pages
JFace provides the interfaces org.eclipse.jface.wizard.IWizard
and org.eclipse.jface.wizard.IWizardPage
to describe
wizards and corresponding implementation classes that handle many of the
details of implementing wizards. Our wizard class HolidayWizard extends
org.eclipse.jface.wizard.Wizard
, which is a useful abstract
class to extend. Its main responsibilities are to create the pages
inside the wizard and perform the work when the wizard is completed.
Adding Pages to a Wizard
Each page is instantiated and added to the wizard. The order in which we add the pages to the wizard is the default navigation order. The page which is added first will be the starting page when the wizard is opened. Later we will look at ways of changing these defaults. The corresponding method on the HolidayWizard class is shown below:
public void addPages() { holidayPage = new HolidayMainPage(workbench, selection); addPage(holidayPage); planePage = new PlanePage(""); addPage(planePage); carPage = new CarPage(""); addPage(carPage); }
Creating the Controls
First you need to decide which controls you want to use and then how they should appear on the wizard page. Here is a quick guideline on common widgets choices:
- text fields : use them when you cannot predict what the user will enter. In our example, we let the users type in any holiday destination they want.
- combo boxes : use them to indicate a single selection from several options. The user can select only one type of seat in the plane: window, aisle or center.
- lists: use them to display many options from which one or more can be selected. We show the available flights in a list widget.
- buttons: there are three styles of buttons in SWT.
- checkboxes: use them to show options with clear "yes" or "no" meaning. When you rent a car, you either take the insurance from the rental company or you don't.
- radio buttons: use them when you want the user to select one options from two or more options. In our simplified model, you can either travel by car or by plane.
- push buttons: use them to trigger an event. The button on the plane page retrieves the price for the flight.
Widgets of type org.eclipse.swt.widgets.Composite
are used to hold other widgets. To create a widget of one of the types
mentioned above, you call its constructor and pass the parent Composite
and a mask of bits indicating the style.
More information about various widgets can be found in the Javadoc for org.eclipse.swt.widgets, and SWT documentation.
You will need to use layouts to give your wizard page a specific
look. A layout controls the position and size of children in a
Composite. In our sample, we use org.eclipse.swt.layout.GridLayout
,
which is one of the most flexible standard layouts. With a GridLayout
,
the widget children of a Composite
are laid out in a grid,
left to right, top to bottom. The numColumns
specifies the
number of columns in the grid. GridData is the layout data object
associated with GridLayout. With a GridData object you can control
things like the widget's alignment, indent or span, horizontally and
vertically. Use setLayoutData
method to set the grid data
of a widget. For more details on layouts see the Understanding Layouts article.
We start by hand-drawing a rough sketch of each wizard page, to find out the number of columns of the grid and the general look of the page. For a better organization of the information on the page, we use horizontal rules to separate related groups of input fields.
The place to create the page controls and arrange them on a page
is the createControl
method or each wizard page. The method
is invoked once for each page when the wizard is first created with a
parameter of type Composite. A typical implementation of this method is
shown below. It does the following tasks::
- create a composite using the specified parent (
)
- construct the widgets and, if necessary, their layout data
objects (
)
- construct the widgets and, if neccesary, their layout data
objects(
)
- set the composite as the control associated with the wizard
page (
).
Here is a simplified implementation of the createControl method for the HolidayMainPage. Some details have been omitted for brevity.
public void createControl(Composite parent) { // create the composite to hold the widgetsComposite composite = new Composite(parent, SWT.NONE); // create the desired layout for this wizard page
GridLayout gl = new GridLayout(); int ncol = 4; gl.numColumns = ncol; composite.setLayout(gl); // create the widgets and their grid data objects // Date of travel
new Label (composite, SWT.NONE).setText("Travel on:"); travelDate = new Combo(composite, SWT.BORDER | SWT.READ_ONLY); GridData gd = new GridData(); gd.horizontalAlignment = GridData.BEGINNING; gd.widthHint = 25; travelDate.setLayoutData(gd); travelMonth = new Combo(composite, SWT.BORDER | SWT.READ_ONLY); travelMonth.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); travelYear = new Combo(composite, SWT.BORDER | SWT.READ_ONLY); travelYear.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); // Similar widgets are constructed for date of return ... createLine(composite, ncol); // Departure new Label (composite, SWT.NONE).setText("From:"); fromText = new Text(composite, SWT.BORDER); gd = new GridData(GridData.FILL_HORIZONTAL); gd.horizontalSpan = ncol - 1; fromText.setLayoutData(gd); // Similar for Destination ... createLine(composite, ncol); // Travel by plane planeButton = new Button(composite, SWT.RADIO); planeButton.setText("Take a plane"); gd = new GridData(GridData.FILL_HORIZONTAL); gd.horizontalSpan = ncol; planeButton.setLayoutData(gd); planeButton.setSelection(true); // Similar for carButton ... // set the composite as the control for this page
setControl(composite); }
Events
Our wizard is not very useful if it is not able to respond to
changes and user interaction. The simplest way to register events on
wizard controls is to use the addListener method to register the wizard
page itself as the handler of the events.The wizard page must implement
the org.eclipse.swt.widgets.Listener
interface with its
handleEvent method. Classes which implement this interface are described
within SWT as providing the untyped listener API. The
listeners implement a simple
handleEvent(...)
method that
is used internally by SWT to dispatch events.
In our Plane page we want to know when the user interacts with the "Get price" button, with the list of flights and with the combo box that holds the seats choices. We add listeners in the createControl method for these widgets.The untyped event mechanism uses a constant to identify the type of event. In our case we are interested in Selection type events for the widgets.
public void createControl(Composite parent) { // ... // price button priceButton = new Button(composite, SWT.PUSH); priceButton.addListener(SWT.Selection, this); // ... // flights flightsList = new List(composite, SWT.BORDER | SWT.READ_ONLY ); flightsList.addListener(SWT.Selection, this); // ... // seat choice seatCombo = new Combo(composite, SWT.BORDER | SWT.READ_ONLY); seatCombo.addListener(SWT.Selection, this); // ... }
When the specified event occurs, the handleEvent method is invoked for each registered listener. The listener, in our case the WizardPage, implements a "case style" listener in which we check for various fields of the event parameter (like its type or source) and respond accordingly. For the PlanePage, we do some special action if the priceButton has been selected, informing the user of the flight price.
public void handleEvent(Event e) { if (e.widget == priceButton) { if (flightsList.getSelectionCount() >0) { if (((HolidayWizard)getWizard()).model.discounted) price *= discountRate; MessageDialog.openInformation(this.getShell(),"", "Flight price "+ price); } } //... }
Processing Errors
The data entered by the user on a wizard page can have a number of errors caused by wrong choices or invalid values. Where appropriate, we should disable the options which are not valid in order to prevent such errors. Where this is not possible, we need to inform the user of the error. When the user corrects it the error message needs to be cleared.
In the sample we disallow destinations to be the same as the departures (not much of a holiday, is it?). No travel back in time is allowed either, so the date of return needs to be after the date of travel. We won't check that the dates are correct. Hopefully you will not find any flight on the 30th of February anyway.

Figure 4. Reporting an error to the user.
You can use the setMessage
and setErrorMessage
methods to display information or error messages. The user can interact
with the controls in any order and, consequently, produce or clear
various errors. A common way to handle errors is to use a status
variable for each possible type of event which can create an error, a
warning or an information message.
The error handling for the first page is shown below. If the
destination or departure fields have triggered the event , the the corresponding
org.eclipse.core.runtime.IStatus
variable, is either set with an error if the two are the same or
cleared. If any of the date fields was modified , we set the timeStatus
variable to the right value. At the end of each processing of an event
, we update the
page to display the most serious error message. This can be the first
error or the first warning if there is no error or null if the page is
correct. When the page is correct, we should see again the page
description. This is how the sample code looks:
public void handleEvent(Event event) { // Initialize a variable with the no error status Status status = new Status(IStatus.OK, "not_used", 0, "", null); // If the event is triggered by the destination or departure fields // set the corresponding status variable to the right valueif ((event.widget == fromText) || (event.widget == toText)) { if (fromText.getText().equals(toText.getText())) status = new Status(IStatus.ERROR, "not_used", 0, "Departure and destination cannot be the same", null); destinationStatus = status; } // If the event is triggered by any of the date fields set // corresponding status variable to the right value
if ((event.widget == returnDate) || (event.widget == returnMonth) || (event.widget == returnYear) || (event.widget == travelDate) || (event.widget == travelMonth) || (event.widget == travelYear)) { if (isReturnDateSet() && !validDates()) status = new Status(IStatus.ERROR, "not_used", 0, "Return date cannot be before the travel date", null); timeStatus = status; } // Show the most serious error
applyToStatusLine(findMostSevere()) // ... }
Navigation Buttons
Using the JFace wizard support we can easily manage the navigation buttons on the wizard pages. These buttons can be Finish and Cancel if the wizard has one page, otherwise each wizard page has Back, Next, Finish and Cancel. By default, Next is enabled for all but the last page and Back for all pages but the first. .
For correct navigation we need to:
- implement the
canFlipToNextPage
method on the page to return true when the user has selected/entered all the required information on the current page. - overwrite the
canFinish
method of of the wizard to return true when the wizard can be completed - ensure that the methods from above are called at the right moment to enable/disable the Next and Finish buttons
We look at each of these steps in a little more detail.
-
To implement the canFlipToNextPage method for the first page of our wizard, we first prevent the user from moving to the next page when the page has any errors. When there are no errors, the destination and departure fields are filled, the return date is set and a mode of transport is selected, the user can move to the next page.
public Boolean canFlipToNextPage(){ if (getErrorMessage() != null) return false; if (isTextNonEmpty(fromText)&& isTextNonEmpty(toText) && (planeButton.getSelection() || carButton.getSelection()) && isReturnDateSet()) return true; return false; }
- Overwriting the canFinish method on the wizard class is useful when some fields or entire pages are optional. When we have all the required information for the current path through the wizard, canFinish should true and the wizard can be completed at any moment after this.
- You can force the update of the navigation buttons. The right
moment for this depends on your problem and the implementation of
canFlipToNextPage and canFinish methods. If we have registered
listeners for all type of events that can affect the enabled/disabled
status of Next and Finish button, then at the end of the event
processing method we force the redraw of the buttons:
public void handleEvent(Event event) { //... getWizard().getContainer().updateButtons(); }
Changing the Page Order
We can change the order of the wizard pages by overwriting the getNextPage
method of any wizard page.Before leaving the page, we save in the model
the values chosen by the user. In our example, depending on the choice
of travel the user will next see either the page with flights or the
page for travelling by car.
public IWizardPage getNextPage(){ saveDataToModel(); if (planeButton.getSelection()) { PlanePage page = ((HolidayWizard)getWizard()).planePage;page.onEnterPage(); return page; } // Returns the next page depending on the selected button if (carButton.getSelection()) { return ((HolidayWizard)getWizard()).carPage; } return null; }
Initializing widgets on wizard pages
The widgets can be initialized based on constants, values available on the start of the wizard or other user choices. We look at each case more closely.
- constants: the widgets can be initialized immediately after their creation. In the sample, we initialize the travel date with today's date.
- values available at the start of the wizard.
In our example, if the user starts the wizard when a folder called Discounts is selected, he will get 10% off the price of flights (You would want to get discounts this way, wouldn't you?). To achieve this, we need to overwrite the
init
method on the wizard class. If the parameter representing the selection is the special folder, we know that we'll offer a discount so we cache this information.public void init(IWorkbench workbench, IStructuredSelection selection) { this.workbench = workbench; this.selection = selection; if (selection != null && !selection.isEmpty()) { Object obj = selection.getFirstElement(); if (obj instanceof IFolder) { IFolder folder = (IFolder) obj; if (folder.getName().equals("Discounts")) model.discounted = true; } } }
In our example, we initialize the model data based on the selection field and use it when displaying the flight price, see above . In other examples, we might need to initialize controls on the page with the selection values.The controls are not created when the init method is called, but we can initialize them as soon as they are created with the cached value.
For wizards which are started by defining a wizard contribution (see Starting a Wizard section), the init method is called by the platform, otherwise we need to call it explicitly.
- user choices
We can initialize the values of some controls based on values for other controls as defined by the user at runtime. For example, in the CarPage we assign the value of the price field based on the rental company that was selected
public void handleEvent(Event e) { if (e.widget == companyCombo) { if (companyCombo.getSelectionIndex() >=0) priceText.setText("£"+prices[companyCombo.getSelectionIndex()]); } // ... }
In another example, the source widgets are on one page and the widgets whose values are initialized belong to subsequent page. Such is the case in our example, where the departure and destination from the first page is used to show.
We define a method to do this initialization for the PlanePage,
onEnterPage and we invoke this method when moving to the PlanePage, that
is in the getNextPage () method for the first page.
Actions on Completion of the Wizard
To complete a wizard, the user can press either the Finish or the
Cancel buttons. If the Cancel button is pressed, the performCancel
method is called and you should overwrite this to cleanup any resources
allocated while running the wizard. The real work is done in performFinish
.
In our case, this method is quite simple:
public boolean performFinish() { String summary = model.toString(); MessageDialog.openInformation(workbench.getActiveWorkbenchWindow().getShell(), "Holiday info", summary); return true; }
If possible, it is always best to subclass from an existing
wizard or wizard page which performs a similar task. A good place to
look for such wizards for subclassing are org.eclipse.ui.newresource
package which provides standard wizards for creating files, folders, and
projects in the workspace and org.eclipse.ui.wizards.datatransfer
package for the standard Import and Export wizards for moving resources
into and out of the workspace.
For example, if we want to save the user choices in a file we
would have the first page inherit from the class org.eclipse.ui.dialogs.WizardNewFileCreationPage
,
which is the standard main page for a wizard that creates a file
resource. We would inherit the actual file creation from the parent
class and could overwrite one of its method getInitialContents() to
return the user choices to be saved in the file.
The task to be completed at the end of the wizard could be a complex operation that modifies many workspace resources, files, classes or projects. This sort of operation could take a relatively long time. To keep the workbench responsive to user input or to give the user the possibility to cancel the operation we might want to run it in a different thread. To achieve all these, we create a runnable which performs the task and runs it in the context of the container of the wizard.
getContainer().run(forkable, canceleable, runnable);
For more details on this subject see JFace operations documentation.
Starting a Wizard
You can start a wizard either by defining a wizard contribution to the workbench or explicitly in your code. We will look at each of these methods in turn.
Defining a wizard contribution
You can contribute to the extension points for wizards that create new resources, import or export resources. When you select the new, import, or export menu or when you press the new wizard button, the workbench uses a wizard selection dialog to display all the wizards that have been contributed for that particular extension point.
In our sample, we contribute to the new wizard extension point. The relevant fragment from plugin.xml is :
<extension id="com.xyz.article.wizards" name="Holiday" point="org.eclipse.ui.newWizards"> <category name="Article Wizards" id="com.xyz.article.wizards.category1"> </category> <wizard name="Holiday Document" icon="icons/create.gif" category="com.xyz.article.wizards.category1"class="com.xyz.article.wizards.HolidayDocumentWizard" id="com.xyz.article.wizards.wizard1"> <description> Creates a holiday document </description> </wizard> </extension>
We define the category to which we add our wizard, the name,
description and icon that will be used. The most important entry in the
extension point is the class field( ) where we give the name of
our wizard class. A class used in this way must implement the (empty)
org.eclipse.ui.INewWizard
interface. This is all we need to do in this case. Some details are
handled by the workbench as we will see below.
Starting the Wizard Explicitly
You may want to launch your wizard as a result of some action that you have defined. Typically you use extension points that contribute to various menus and toolbars in the workbench and want the wizard to be started when the user interacts with these, for example when pressing a button or selecting a menu option.
In our example, we use the popupMenu extension point for a folder to start the wizard.
Figure 6. Starting the wizard from the popup menu
In the plugin.xml we have:
<extension point="org.eclipse.ui.popupMenus"> <objectContributionobjectClass="org.eclipse.core.resources.IFolder" id="com.xyz.article.wizards.popup1"> <action label="Create holiday document" icon="icons/create.gif"
class="com.xyz.article.wizards.CreateWizardAction" id="com.xyz.article.wizards.action1"> </action> </objectContribution> </extension>
The objectClass entry () defines the type of
objects to which this popupMenu will be added to, in our case a Folder.
The real work is done by the action class defined on
. Its run method
is executed when the user selects this new item from the popup menu of a
folder. The other two methods on the action class, setActivePart
and selectionChanged
cache the workbench part and the
selection fields respectively for use when the wizard is started, see below. For more
details on the popup menu extension point see the documentation.
When you are launching your own wizard, you need to wrap the
wizard in a org.eclipse.jface.wizard.WizardDialog
.
A WizardDialog is a container that can host a wizard and display wizard
pages. It has a standard layout: an area at the top containing the
wizard's title, description, and image; the actual wizard page appears
in the middle; below it is a progress indicator; and at the bottom is an
area with a message line and a button bar containing Next, Back, Finish,
Cancel, and Help buttons.
The relevant code to start the wizard is:
// Instantiates and initializes the wizard HolidayWizard wizard = new HolidayWizard();wizard.init(part.getSite().getWorkbenchWindow().getWorkbench(), (IStructuredSelection)selection); // Instantiates the wizard container with the wizard and opens it WizardDialog dialog = new WizardDialog(shell, wizard); dialog.create(); dialog.open();
Resources
We have seen how to implement a wizard, initialize its contents, and perform actions on its completion. For further information about wizards and controls, see the following resources:
Eclipse Platform Plug-in Developer Guide: Standard
Widget Toolkit (SWT)
Eclipse Platform Plug-in Developer Guide: JFace UI
Framework
Article: Understanding Layouts in SWT (Revised for
2.0)