Thursday, July 21, 2011

JFace Wizard Tips and Tricks

The JFace wizard classes offer a decent, basic framework for creating user interface wizards.  However, I found it hard to figure out how to accomplish a few basic things, such as getting notifications on page changes, disabling the Back button, and changing the text on a button.  These things are not that difficult to do, as it turns out, but the API documentation is very limited.  And while there are some good tutorials out there, they do not cover these tasks -- I had to dig in to the source code to find the solutions.

First, changing the text on a button.  You need to subclass WizardDialog and override createButtonsForButtonBar():

@Override 
protected void createButtonsForButtonBar(Composite parent) {
    super.createButtonsForButtonBar(parent);
    Button finishButton = getButton(IDialogConstants.FINISH_ID);
    finishButton.setText("Submit");
}

What about getting notified when the page changes (i.e. the user clicked the Next or Back buttons)?  In your Wizard's addPages() method, you can add the necessary listeners:

public class MyWizard extends Wizard implements IPageChangingListener, IPageChangedListener {
    @Override
    public void addPages() {
        // add pages here ...
        this.addPage(new MyPage());
        
        WizardDialog dialog = (WizardDialog)getContainer();
        dialog.addPageChangingListener(this);
        dialog.addPageChangedListener(this);
    }

    @Override
    public void pageChanged(PageChangedEvent event) {
        // ...
    }

    @Override
    public void handlePageChanging(PageChangingEvent event) {
        // ...
    }
}

Disabling the back button is harder than it should be, due to non-intuitive behavior in the default implementation of WizardPage.getPreviousPage().  You can call setPreviousPage( null ), and getPreviousPage() still returns the previous page.  You need to override the implementation of getPreviousPage() in order to disable the back button:

public abstract class MyWizardPage extends WizardPage {
    private boolean backButtonEnabled = true;

    public void setBackButtonEnabled(boolean enabled) {
        backButtonEnabled = enabled;
        getContainer().updateButtons();
    }
    
    @Override
    public IWizardPage getPreviousPage() {
        if (!backButtonEnabled) {
            return null;
        }
        return super.getPreviousPage();
    }
}

Finally, if you want to choose when to enable the Finish button, you will probably be overriding the canFinish() method on your Wizard subclass.  I did this, but made the mistake of creating my own canFinish boolean field.  This shadowed a field of the same name in the parent class -- as a result, I could never get my Finish button to become enabled!  Changing the field name to myCanFinish fixed the issue.

No comments: