How to update JComboBox without Selected Item being changed

  • Thread starter =?iso-8859-1?q?J=FCrgen_Gerstacker?=
  • Start date
?

=?iso-8859-1?q?J=FCrgen_Gerstacker?=

Hello,
I want to periodically update a Combobox due to changes in the OS
(available RS232 serial ports).
I started to fill the box every time the combobox becomes visible

setup_cbport.removeAllItems();
while () {
...
setup_cbport.addItem(portId.getName());
...
}
setup_cbport.setSelectedItem(port); // previously selected item

But when the box is being filled, an actionPerformed is fired


public void actionPerformed(ActionEvent e) {
if (e.getSource()==setup_cbport) {
Object obj=setup_cbport.getSelectedItem();
port=(String)obj; // new selection
}


and the previous selected item is overwritten.

I could compare the items in the box with the new items and
'add'/'remove' individually but this would complicated the code. I
would like a simple solution, e.g. to allow the new selection only when
the user changes the ComboBox, not when the 'actionPerformed' is issued
by the System.
Any ideas?

Juergen
 
W

wesley.hall

Jürgen Gerstacker said:
Hello,
I want to periodically update a Combobox due to changes in the OS
(available RS232 serial ports).
I started to fill the box every time the combobox becomes visible

setup_cbport.removeAllItems();
while () {
...
setup_cbport.addItem(portId.getName());
...
}
setup_cbport.setSelectedItem(port); // previously selected item

But when the box is being filled, an actionPerformed is fired


public void actionPerformed(ActionEvent e) {
if (e.getSource()==setup_cbport) {
Object obj=setup_cbport.getSelectedItem();
port=(String)obj; // new selection
}


and the previous selected item is overwritten.

I could compare the items in the box with the new items and
'add'/'remove' individually but this would complicated the code. I
would like a simple solution, e.g. to allow the new selection only when
the user changes the ComboBox, not when the 'actionPerformed' is issued
by the System.
Any ideas?

Jurgen,

The problem you have is that you want fairly complex functionality from
your combo box but you are using the simple, convinience methods to
manage it. Obviously this isn't going to work.

The reason you are seeing the actionPerformed event is most likley
because your combo box is informed of the model change after you call
'removeAllItems'. This is causing the selected item to change (because
you have removed it). The combo box default model cannot 'know' that
you intend to add the item back again a few lines later.

What you need to do to solve this issue is write your own
ComboBoxModel. This will give you complete control over the items in
the combo box as well as complete control over when the combo box will
attempt to re-evaluate its data based on the new model.

The interface you should implement is
http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/ComboBoxModel.html

Implementing this should not be hard (its only a few methods) but you
must remember to keep track of your 'ListDataListeners' (which are
provided to you when addListDataListener is called) and when you update
the contents of your model with the new information, fire an
appropriate event to all of your ListDataListeners. It is when this
event is fired, that the combo box will re-evaluate the model. It will
call 'getSelectedItem' and provided the item is equal (.equals) to the
previous item, it should not change the selected item and therefore not
fire actionPerformed. You will, of course, have to formulate a strategy
for what happens when the selected item IS removed, but you would have
had to do that anyway, and using a custom model will give you much more
control over the behaviour.

Good Luck!
 
N

Nigel Wade

Jürgen Gerstacker said:
Hello,
I want to periodically update a Combobox due to changes in the OS
(available RS232 serial ports).
I started to fill the box every time the combobox becomes visible

setup_cbport.removeAllItems();
while () {
...
setup_cbport.addItem(portId.getName());
...
}
setup_cbport.setSelectedItem(port); // previously selected item

But when the box is being filled, an actionPerformed is fired


public void actionPerformed(ActionEvent e) {
if (e.getSource()==setup_cbport) {
Object obj=setup_cbport.getSelectedItem();
port=(String)obj; // new selection
}


and the previous selected item is overwritten.

I could compare the items in the box with the new items and
'add'/'remove' individually but this would complicated the code. I
would like a simple solution, e.g. to allow the new selection only when
the user changes the ComboBox, not when the 'actionPerformed' is issued
by the System.
Any ideas?

Juergen

You could remove the listeners before updating the ComboBox, then restore them
after the update is complete:

ActionListener[] listeners = setup_cbport.getActionListeners();
for(ActionListener l:listeners) {
setup_cbport.removeActionListener(l);
}

// do whatever you need to here.

for(ActionListner l:listeners) {
setup_cbport.addActionListener(l);
}
 
W

wesley.hall

You could remove the listeners before updating the ComboBox, then restore them
after the update is complete:

YUCK! This is a terrible habbit. It should be the last of the last of
resorts.

In any case, I dont think it will solve the OPs problem. Calling
'removeAllItems' will result in the selected item being lost. The
JComboBox is a listener of it's model. It is taking control of the
events that fire between the model that is needed.

The correct solution is a custom model.
 
?

=?iso-8859-1?q?J=FCrgen_Gerstacker?=

In any case, I dont think it will solve the OPs problem. Calling
'removeAllItems' will result in the selected item being lost.

I know this issue, I restore the selected item after updating the
contents.

setup_cbport.setSelectedItem(port); // previously selected item

After all, the modification of the combobox is not a bad idea.


Vector<String> set=new Vector<String>();
while (...) {
// collect items from the OS
set.add(pId.getName());
}

// delete items from the box
Vector<String> set2=new Vector<String>();
int i=0; while (i<setup_cbport.getItemCount()) {
String itm=(String)setup_cbport.getItemAt(i);
if (!set.contains(itm)) setup_cbport.removeItemAt(i);
else {set2.add(itm); i++;}
}

// add new items
Iterator it = set.iterator();
while (it.hasNext()) {
Object itm=it.next();
if (!set2.contains(itm)) setup_cbport.addItem(itm);
}
 
W

wesley.hall

Jürgen Gerstacker said:
I know this issue, I restore the selected item after updating the
contents.

There is more to it than this, removing the selected item sets into
motion a whole series of events that is what ultimately ends up in your
actionPerformed event being fired. This happens before your remove
method invocation even returns. Restoring the selected item later will
not prevent it.

The slightly lazy approach, is what Nigel suggested. Remove the
listeners, then add them back after you are done with your
manipulation. I stand by my point that this is a bad bad habbit, but I
would be lying if I said I haven't done it myself and it (now that I
understand a little more) should solve your problem for now.

My point, is that the better way is to take control over which events
are fired between your model and your view. This can be accomplished by
creating your own model rather than using the default one. As you get
into more complex swing code, you will eventually have no choice but to
create your own models. In this example, you do have a choice, but it
is a choice between lots of ugly code that is doing far more work than
required, or the elegant solution where you use the libraries how Sun
originally intended.

The more elegant solution may require for you to learn a little more
about MVC and how it works within Swing, and if you dont have the time
and/or inclination to learn this then you would be better of going down
your own path or using the listener removal technique. You may find
that you will reach an impasse with this approach though, and be forced
to learn the 'proper' way.
 
L

Lew

Jürgen Gerstacker said:
Vector<String> set=new Vector<String>();

Are you relying on the synchronization of Vector methods? If not, a better
declaration might be:

List <String> set = new ArrayList <String>();

Also, since there is a Set type, naming a List variable "set" might be a tad
misleading.

- Lew
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,482
Members
44,901
Latest member
Noble71S45

Latest Threads

Top