JComboBox mit ItemListener zeigt JOptionPane --> Zwei Klicks benötigt

mas666

Mitglied
Hallo,

Ich habe das Problem, dass ich auf die Änderung einer JComboBox reagieren möchte, indem ich einen JOptionPane anzeige und abfrage ob die Änderung auch wirklich durchgeführt werden soll. Wenn ich das tue bleibt die JComboBox im Hintergrund ausgeklappt und ich muss zuerst auf den JOptionPane klicken (erst jetzt klappt im Hintergrund die JComboBox zu), bevor ich eine Option auswählen kann, obwohl der JOptionPane im Vordergrund ist und aktiv ist.

Da der Benutzer die Änderung abbrechen kann, arbeite ich mit einem VetoableSelectionListener.

Kann mir jemand einen Typ geben, wie ich das Problem lösen kann? Also, dass ich beim ersten Klick auf den erscheinenden Dialog bereits eine Auswahl (YES/NO(CANCEL) treffen kann?

NACHTRAG: Es ändert nichts, wenn ich statt des JOptionPane einen eigens gebastelten Dialog verwende. Auch wenn ich explizit den Fokus auf einem JButton anfordere, bleibt der Effekt der selbe.

So wird die ComboBox erstellt:
Java:
Integer[] items = Utils.getSystemComboItems(parent.getProject(), false);
final VetoableComboBoxModel model = new VetoableComboBoxModel( items );
model.setSelectedItem(area.getNumber1());
model.addVetoableSelectionListener( new VetoableComboBoxSelectionListener() {
  public boolean selectionChanged( VetoableChangeEvent vce ) {
    return changeAreaSystem((Integer)model.getSelectedItem());
   }
 });
systemCombo = new JComboBox(model);
systemCombo.setRenderer(new SystemListRenderer(parent.getProject()));

Diese Methode wird bei einer Änderung aufgerufen:
Java:
private boolean changeAreaSystem(int selectedSystem) {
n = JOptionPane.showConfirmDialog(this, 
						  						  "Änderung zulassen?", 
						  						  "Änderung zulassen", 
						  						  JOptionPane.YES_NO_CANCEL_OPTION, 
						  						  JOptionPane.WARNING_MESSAGE);
if(n==JOptionPane.YES_OPTION) {
			return true;
		}
		return false;
}


Java:
interface VetoableComboBoxSelectionListener extends java.util.EventListener {
	boolean selectionChanged( VetoableChangeEvent e );
}

Folgende Hilfsklassen werden benötigt.
Java:
class VetoableChangeEvent extends javax.swing.event.ChangeEvent {
    private Object newValue;

    public VetoableChangeEvent( Object source, Object newValue ) {
        super( source );
        this.newValue = newValue;
    }

    public Object getNewValue() {
        return newValue;
    }
}

Java:
class VetoableComboBoxModel extends DefaultComboBoxModel {
    private ArrayList selectionListeners = new ArrayList();

    public VetoableComboBoxModel() {
    }

    public VetoableComboBoxModel( Object[] items ) {
        super( items );
    }

    public VetoableComboBoxModel( Vector v ) {
        super( v );
    }

    public void addVetoableSelectionListener(
            VetoableComboBoxSelectionListener l ) {
        selectionListeners.add( l );
    }

    public void removeVetoableSelectionListener(VetoableComboBoxSelectionListener l ) {
        selectionListeners.remove( l );
    }

    protected boolean fireVetoableSelectionChange( Object newValue ) {
        boolean result = true;
        VetoableChangeEvent event = new VetoableChangeEvent(this,
                                                            newValue);
        Iterator it = selectionListeners.iterator();
        while ( it.hasNext() ) {
            VetoableComboBoxSelectionListener l =
                    (VetoableComboBoxSelectionListener)it.next();
            result &= l.selectionChanged( event );
        }
        return result;
    }

    public void setSelectedItem( Object anItem ) {
        Object oldItem = getSelectedItem();

        super.setSelectedItem( anItem );
        if ( !fireVetoableSelectionChange(anItem) )
            super.setSelectedItem( oldItem );
    }
}

Danke für jeden Tipp!!
 
Zuletzt bearbeitet:
Hallo,

mein initialer Versuch wäre es erstmal in VetoableComboBoxModel.setSelectedItem() eine Atempause für den GUI-Thread einzubauen und mittels SwingUtilities.invokeLater() den Rest asynchron direkt danach auszuführen.

Ich würde es so versuchen:
Java:
public void setSelectedItem( final Object anItem ) {
    final Object oldItem = getSelectedItem();
    super.setSelectedItem( anItem );
 
    SwingUtilities.invokeLater(new Thread() {
        @Override
        public void run() {
            if ( !fireVetoableSelectionChange( anItem ) ) {
                setSelectedItemInSuper( oldItem );
            }
        }
    });
}
 
void setSelectedItemInSuper( Object anItem ) {
    super.setSelectedItem( anItem );
}

Leider sehe ich keine Möglichkeit direkt aus dem Thread (ohne diese hässliche Zusatzmethode) auf das vererbende ComboBoxModel zuzugreifen.

Vielleicht wäre das ja schon mal ein Ansatz um der ComboBox Zeit zum Schließen zu geben.
Mehr fällt mir auf die Schnelle nicht ein.


Viele Grüße
Carron
 
Hi Carron,

Ich habs so ausprobiert und hat genau den Effekt den ich mir wünschte. Das einzige, was jetzt noch zu beheben ist, ist, dass die Methode beim initialisieren des Modells (in meinem Fall also, wenn ich den entsprechenden Dialog aufrufe) ausgeführt wird. Habs mir aber auch noch nicht intensiver angeschaut, da werd ich schon dahinter kommen.

Auf jeden Fall Danke für die kompetente und gut verständliche Hilfe.

Gruss
mas
 
Hi mas,

wenn du es verhindern willst, dass dein fireVetoableSelectionChange() beim Initialisieren geworfen wird (schließlich setzt er da ja die default-Auswahl...), kann ich mir einen Schalter vorstellen, den du immer nach dem Initialisieren mit setVetoActive(true) aktivierst.

Sowas in der Richtung wie:
Java:
private boolean vetoActive = false;

public void setSelectedItem( final Object anItem ) {
    final Object oldItem = getSelectedItem();
    super.setSelectedItem( anItem );

    if (this.vetoActive) {
        SwingUtilities.invokeLater(new Thread() {
            @Override
            public void run() {
                if ( !fireVetoableSelectionChange( anItem ) ) {
                    setSelectedItemInSuper( oldItem );
                }
            }
        });
    }
}
 
void setSelectedItemInSuper( Object anItem ) {
    super.setSelectedItem( anItem );
}

public void setVetoActive(final boolean flag) {
    this.vetoActive = flag;
}

Schön helfen zu können :)

Viele Grüße & Schönes Wochenende
Carron
 
Zuletzt bearbeitet:
Hi Carron,

Für meine Zwecke reicht es sogar, das Flag nach der ersten Ausführung innerhalb der Klasse zu setzen.

Java:
public void setSelectedItem( final Object anItem ) {
        final Object oldItem = getSelectedItem();
        super.setSelectedItem( anItem );
     
        if (this.vetoActive) {
        	SwingUtilities.invokeLater(new Thread() {
        		@Override
        		public void run() {
        			if ( !fireVetoableSelectionChange( anItem ) ) {
        				setSelectedItemInSuper( oldItem );
        			}
        		}
        	});
        }
        this.vetoActive = true;
    }

Nochmals danke. Ich markiere das Thema als erledigt.

Gruss
mas
 

Neue Beiträge

Zurück