Download NetBeans!

20080406 Sunday April 06, 2008

Disabling/Enabling a Wizard Panel's Finish Button

Let's take the wizard panel from a few days ago and disable the "Finish" button until both the new fields are filled in:

  1. Change the visual panel's signature so that it implements DocumentListener. Then, in the constructor, add a DocumentListener to both of your new fields:

    extField.getDocument().addDocumentListener(this);
    impField.getDocument().addDocumentListener(this);

    Next, implement the DocumentListener's methods like this:

    @Override
    public void changedUpdate(DocumentEvent e) {
        panel.fireChangeEvent();
        updateUI(e);
    }
    
    @Override
    public void insertUpdate(DocumentEvent e) {
        panel.fireChangeEvent();
        updateUI(e);
    }
    
    @Override
    public void removeUpdate(DocumentEvent e) {
        panel.fireChangeEvent();
        updateUI(e);
    }
    
    public void updateUI(DocumentEvent e) {
        if (this.extField.getDocument() == e.getDocument()) {
            firePropertyChange(EXTENSION, null, this.extField.getText());
        }
        if (this.impField.getDocument() == e.getDocument()) {
            firePropertyChange(IMPLEMENTATION, null, this.impField.getText());
        }
    }

  2. In the non-visual wizard panel, add the code that receives the change event and enables/disables the "Finish" button:

    private final Set<ChangeListener> listeners = new HashSet<ChangeListener>(1);
    
    @Override
    public final void addChangeListener(ChangeListener l) {
        synchronized (listeners) {
            listeners.add(l);
        }
    }
    
    @Override
    public final void removeChangeListener(ChangeListener l) {
        synchronized (listeners) {
            listeners.remove(l);
        }
    }
    
    protected final void fireChangeEvent() {
        Set ls;
        synchronized (listeners) {
            ls = new HashSet(listeners);
        }
        ChangeEvent ev = new ChangeEvent(this);
        for (ChangeListener l : ls) {
            l.stateChanged(ev);
        }
    }
    
    @Override
    public boolean isValid() {
        if (getExtensionFromVisualPanel().length() == 0 || 
                getImplementationFromVisualPanel().length() == 0) {
            return false;
        }
        return true;
    }

  3. Finally, make sure the two classes are correctly hooked up together. In the wizard panel, pass the current class to the visual panel:

    @Override
    public Component getComponent() {
        if (component == null) {
            component = new FancyVisualPanel1(this);
        }
        return component;
    }

    In the visual panel, receive the wizard panel in the constructor:

    private FancyWizardPanel1 panel;
    
    public FancyVisualPanel1(FancyWizardPanel1 panel) {
        initComponents();
        this.panel = panel;
        extField.getDocument().addDocumentListener(this);
        impField.getDocument().addDocumentListener(this);
    }

That's it. Now, if both fields aren't filled in, the "Finish" button is disabled. You can also display some kind of informative message so that the user knows what they should do to enable the "Finish" button. In real life, you probably don't want to force the user to fill in an extension class and an implementation class, because possibly they want to have a signature that doesn't include superclasses and/or interfaces. So you'd probably add a checkbox that enables/disables the two fields, depending on whether the user wants to fill them in or not.

Apr 06 2008, 03:56:15 PM PDT Permalink

Trackback URL: http://blogs.sun.com/geertjan/entry/disabling_enabling_a_wizard_panel
Comments:

Simpler than doing your own listener management is to use ChangeSupport in the Utilities API.

Not entirely clear who listens on the property change events fired by

firePropertyChange(EXTENSION, null, this.extField.getText());

Posted by Andrei Badea on April 07, 2008 at 05:55 AM PDT #

Hi,

I just want to know how to copy the information that users puts in the first screen of a wizard to the second screen to ask for confirmation.

I have something like this in the performAction method.

public void performAction() {
//Some default declarations for the wizard
//created by NetBeans templates.

boolean cancelled = wizardDescriptor.getValue() != WizardDescriptor.CANCEL_OPTION;
boolean finished = wizardDescriptor.getValue() == WizardDescriptor.FINISH_OPTION;
boolean next = wizardDescriptor.getValue().equals(WizardDescriptor.NEXT_OPTION);
if (!cancelled) {
// do something
}
if(next){
//copy the values to ask for confirmation

NuevaInstalacionWizardPanel1 o = (NuevaInstalacionWizardPanel1) panels[0];
NuevaInstalacionWizardPanel2 o2 = (NuevaInstalacionWizardPanel2) panels[1];

String t1 = o.getComponent().getTextField(1).getText();
String t2 = o.getComponent().getTextField(2).getText();
String t3 = o.getComponent().getTextField(3).getText();
String t4 = o.getComponent().getTextField(4).getText();
String t5 = o.getComponent().getTextField(5).getText();

o2.getComponent().getLabel(1).setText(t1);
o2.getComponent().getLabel(2).setText(t2);
o2.getComponent().getLabel(3).setText(t3);
o2.getComponent().getLabel(4).setText(t4);
o2.getComponent().getLabel(5).setText(t5);
}
if (finished) {
//stores in DB. This works fine.
}
}

The "finish" part of the code works fine, and I've already tried the same code for the "Next" button but nothing happens!!

When I click the "Next" button in the wizard it appears not to enter to the performAction method.

What can I do? I'm using NetBeans 6.1 Beta

I created the methods getTextField and getLabel in the dataVisualPanel1 and dataVisualPanel2, and made the proper changes for the other classes so I can have enabled and disabled by events the "next" and "finish" buttons and that works fine too.

In advance, thanks!!

Posted by Luis on April 08, 2008 at 12:31 PM PDT #

I've already solved the problem, I made it trough the properties, actually the properties refreshes themselves, so you can pass data from one window to another of the wizard.

Posted by Luis on April 09, 2008 at 10:35 AM PDT #

Luis, could you please describe how you did it through properties? I have the same problem...

Posted by Vlad on May 31, 2008 at 12:16 PM PDT #

There are two methods in each WizardPanel:

public void readSettings(Object settings) {
}

public void storeSettings(Object settings) {
}

In these two methods you can pass any object.

here is an example:

In the first WizardPanel,

public void storeSettings(Object settings) {
wizardDescriptor = (WizardDescriptor) settings;
String t1 = component.getTextField(1).getText();
wizardDescriptor.putProperty("text1", t1);

}

In the second WizardPanel,

public void readSettings(Object settings) {
wizardDescriptor = (WizardDescriptor) settings;
String t1 = (String)wizardDescriptor.getProperty("text1");
component.getLabel(1).setText(t1);
}

I hope this was helpful.

Posted by Luis on June 02, 2008 at 08:00 AM PDT #

Post a Comment:

Name:
E-Mail:
URL:

Your Comment:

HTML Syntax: NOT allowed