2

My code is quite simple actually. I saw a simple and similar code was from this article.

At first, I have 1 combobox. I have a listener on it called itemStateChanged(). My purpose to add into this listener is that; "to execute some code when user click (select) an item from its dropbox".

Cmb_ItemCategory = new javax.swing.JComboBox();

Cmb_ItemCategory.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Loading..." }));

Cmb_ItemCategory.addItemListener(new java.awt.event.ItemListener() {
    public void itemStateChanged(java.awt.event.ItemEvent evt) {
        Cmb_ItemCategoryItemStateChanged(evt);
    }
});

private void Cmb_ItemCategoryItemStateChanged(java.awt.event.ItemEvent evt) {

        if(evt.getStateChange() == java.awt.event.ItemEvent.SELECTED){
        System.err.println("Sombody click or change my model content");
        }

    }

Behind the code, I grab some data, and then calling a method of removeAllItems() . And then I set a new Model (from new data) into it.

-- at another line of code ---
Cmb_ItemCategory.removeAllItems();
Cmb_ItemCategory.setModel(newModel);

I juz realized that my itemStateChanged() is called when i do the removeAllItem() method. called once.

So, How to make it only called once user Click (select) only AND not when removeAllItems() called?

it similar to this article. But it's not removingItems case. CMIIW.

Community
  • 1
  • 1
gumuruh
  • 2,544
  • 4
  • 33
  • 56
  • 1
    Simply write your code inside `if (ie.getStateChange() == ItemEvent.SELECTED){}` , so when you be calling `removeAllItem()` this block will not run. – nIcE cOw Apr 18 '12 at 05:22
  • @nIcEcOw: I did already, please look at the above code I just written over there.... But the interesting case is that it is running eventhough I already use that ItemEvent.SELECTED if-else. :( – gumuruh Apr 18 '12 at 05:32
  • I had tested that thingy too, on my side it's not doing that way, using JRE 1.7 update 3, don't put one else part, since what you described in the question, that you want only one situation to work, so just if part will do. – nIcE cOw Apr 18 '12 at 05:56
  • OMG... maybe because I'm using JDK 1.6 update 31 – gumuruh Apr 18 '12 at 07:28

5 Answers5

5

Here check this code out, this works flawlessly, might be you doing something wrong where you calling removeAllItems() :

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class ComboState 
{
    private void createAndDisplayGUI()
    {
        JFrame frame = new JFrame("Combo State Testing : ");
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

        final JComboBox cbox = new JComboBox();
        cbox.addItem("One");
        cbox.addItem("Two");
        cbox.addItem("Three");
        cbox.addItem("Four");
        cbox.addItemListener(new ItemListener()
        {
            public void itemStateChanged(ItemEvent ie)
            {
                if (ie.getStateChange() == ItemEvent.SELECTED)
                {
                    System.out.println("Item Selected is : " + ie.getItem());
                }
                /*else
                {
                    System.out.println("I am called for other reasons.");
                }*/
            }
        });

        /*
         * Click me to remove JComboBox's items.
         */
        JButton removeButton = new JButton("REMOVE");
        removeButton.addActionListener(new ActionListener()
        {
            public void actionPerformed(ActionEvent ae)
            {
                cbox.removeAllItems();
            }
        });

        frame.add(cbox, BorderLayout.CENTER);
        frame.add(removeButton, BorderLayout.PAGE_END);
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    }

    public static void main(String... args)
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            public void run()
            {
                new ComboState().createAndDisplayGUI();
            }
        });
    }
}
nIcE cOw
  • 24,468
  • 7
  • 50
  • 143
  • in my complete source code, actually I'm doing it from Thread, yes. A thread out side of the JFrame calling that Combobox. Is that make a difference, @nice cOw? – gumuruh Apr 18 '12 at 07:31
  • Some form of an [SSCCE](http://sscce.org/), would be much helpful in order to provide some solid answer, since predicting things might lead to more confusion :( – nIcE cOw Apr 18 '12 at 07:34
  • @gumuruh you should not modify Swing components on the non-EDT thread. See the [Concurrency in Swing](http://docs.oracle.com/javase/tutorial/uiswing/concurrency/) tutorial for more information – Robin Apr 18 '12 at 08:15
  • i shouldn't? but, until now I'm using all of my 5-10 thread modifying my SWING components and still runs okay. I mean, is it really making the Swing dead-lock or something? okay i'll read that link. :D @Robin – gumuruh Apr 18 '12 at 09:38
  • @gumuruh It might throw exception, result in weird visualisation, deadlocks, ... all sort of things you really do not want – Robin Apr 18 '12 at 10:02
3

As nIcE cOw already illustrated in his example, it should certainly work when you use a DefaultComboBoxModel (which is the case in his sample code, although it happens behind the screens).

I could explain the behavior you encounter for the non-DefaultComboBoxModel case, although your code snippet suggests you use one. Looking at the source code for JComboBox#removeAllItems there is a different code path since the removeAllElements method is not part of the MutableComboBoxModel interface

public void removeAllItems() {
    checkMutableComboBoxModel();
    MutableComboBoxModel<E> model = (MutableComboBoxModel<E>)dataModel;
    int size = model.getSize();

    if ( model instanceof DefaultComboBoxModel ) {
        ((DefaultComboBoxModel)model).removeAllElements();
    }
    else {
        for ( int i = 0; i < size; ++i ) {
            E element = model.getElementAt( 0 );
            model.removeElement( element );
        }
    }
    selectedItemReminder = null;
    if (isEditable()) {
        editor.setItem(null);
    }
}

So with a non-DefaultComboBoxModel you are going to remove the items one by one. This means that at a certain point in time, you will remove the selected element. A possible implementation of your model might change the selected element at that point. If you look for example at the implementation in DefaultComboBoxModel (although this code will not be triggered) you can clearly see it changes the selection.

public void removeElementAt(int index) {
    if ( getElementAt( index ) == selectedObject ) {
        if ( index == 0 ) {
            setSelectedItem( getSize() == 1 ? null : getElementAt( index + 1 ) );
        }
        else {
            setSelectedItem( getElementAt( index - 1 ) );
        }
    }

    objects.removeElementAt(index);

    fireIntervalRemoved(this, index, index);
}

Perhaps your model does something similar, which explains the event. Just for making this post complete, the code behind the DefaultComboBoxModel#removeAllElements where you can clearly see it sets the selection to null and does not select another object. Only weird thing in that code is that it does not fire a DESELECTED event first, although you know the selection has changed if you listen for the intervalRemoved event ... but that is not really relevant to your problem

public void removeAllElements() {
    if ( objects.size() > 0 ) {
        int firstIndex = 0;
        int lastIndex = objects.size() - 1;
        objects.removeAllElements();
        selectedObject = null;
        fireIntervalRemoved(this, firstIndex, lastIndex);
    } else {
        selectedObject = null;
    }
}

So to conclude: I say the solution for your problem is located in your model, and not in the code you posted

Robin
  • 36,233
  • 5
  • 47
  • 99
  • That is something I will call a complete answer :-) , cleared many things up and provided some valuable info, in every respect. – nIcE cOw Apr 18 '12 at 06:40
  • ic, ic, ic, @Robin... That sounds very complete answer! :D But, What if the user not using setModel()? Instead the user only do removeAllItems() and then, addItem(object), addItem(Object)... it also execute the itemStateChanged() don't u think ? :D – gumuruh Apr 18 '12 at 07:23
  • @gumuruh again, it depends on your model. The `DefaultComboBoxModel` fires an event in that case for the first element. Just take a look at the source of that method – Robin Apr 18 '12 at 08:14
2

One quick way to do is to have a bool set to true before you call removeAllItem() and back false after. Execute the code in your itemStateChanged() only if the bool variable is false.

Ideally you could override the removeAllItem() function.

Chris911
  • 4,131
  • 1
  • 23
  • 33
  • Sounds like a weird way to fix something that is probably caused by his used `ComboBoxModel`. See my answer for what I think is happening, and the post of nIcE cOw which shows that it should just work with the code posted in the question – Robin Apr 18 '12 at 06:12
  • yes, it's a bit weird but still can be used as an alternative. :D – gumuruh Apr 18 '12 at 09:37
2

not clear from whole discusion,

  • you have to remove all Listener(s) from JComboBox before removing all Items, after Items are removed you can add Listener(s) back,

  • still not sure if you want to add & remove Items dynamically, or you can set whatever value for another JComponent(s),

  • (against complicating simple things) did you see there remove,

.

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;

public class ComboBoxTwo extends JFrame implements ActionListener, ItemListener {

    private static final long serialVersionUID = 1L;
    private JComboBox mainComboBox;
    private JComboBox subComboBox;
    private Hashtable<Object, Object> subItems = new Hashtable<Object, Object>();

    public ComboBoxTwo() {
        String[] items = {"Select Item", "Color", "Shape", "Fruit"};
        mainComboBox = new JComboBox(items);
        mainComboBox.addActionListener(this);
        mainComboBox.addItemListener(this);
        //prevent action events from being fired when the up/down arrow keys are used
        //mainComboBox.putClientProperty("JComboBox.isTableCellEditor", Boolean.TRUE);
        getContentPane().add(mainComboBox, BorderLayout.WEST);
        subComboBox = new JComboBox();//  Create sub combo box with multiple models
        subComboBox.setPrototypeDisplayValue("XXXXXXXXXX"); // JDK1.4
        subComboBox.addItemListener(this);
        getContentPane().add(subComboBox, BorderLayout.EAST);
        String[] subItems1 = {"Select Color", "Red", "Blue", "Green"};
        subItems.put(items[1], subItems1);
        String[] subItems2 = {"Select Shape", "Circle", "Square", "Triangle"};
        subItems.put(items[2], subItems2);
        String[] subItems3 = {"Select Fruit", "Apple", "Orange", "Banana"};
        subItems.put(items[3], subItems3);
//      mainComboBox.setSelectedIndex(1);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        String item = (String) mainComboBox.getSelectedItem();
        Object o = subItems.get(item);
        if (o == null) {
            subComboBox.setModel(new DefaultComboBoxModel());
        } else {
            subComboBox.setModel(new DefaultComboBoxModel((String[]) o));
        }
    }

    @Override
    public void itemStateChanged(ItemEvent e) {
        if (e.getStateChange() == ItemEvent.SELECTED) {
            if (e.getSource() == mainComboBox) {
                if (mainComboBox.getSelectedIndex() != 0) {
                    FirstDialog firstDialog = new FirstDialog(ComboBoxTwo.this,
                            mainComboBox.getSelectedItem().toString(), "Please wait,  Searching for ..... ");
                }
            } 
        }
    }

    private class FirstDialog extends JDialog {

        private static final long serialVersionUID = 1L;

        FirstDialog(final Frame parent, String winTitle, String msgString) {
            super(parent, winTitle);
            setModalityType(Dialog.ModalityType.APPLICATION_MODAL);
            JLabel myLabel = new JLabel(msgString);
            JButton bNext = new JButton("Stop Processes");
            add(myLabel, BorderLayout.CENTER);
            add(bNext, BorderLayout.SOUTH);
            bNext.addActionListener(new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent evt) {
                    setVisible(false);
                }
            });
            javax.swing.Timer t = new javax.swing.Timer(1000, new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent e) {
                    setVisible(false);
                }
            });
            t.setRepeats(false);
            t.start();
            setLocationRelativeTo(parent);
            setSize(new Dimension(400, 100));
            setVisible(true);
        }
    }

    public static void main(String[] args) {
        JFrame frame = new ComboBoxTwo();
        frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}
mKorbel
  • 109,525
  • 20
  • 134
  • 319
  • Good solution to remove all `Listeners`, before removing items from the `JComboBox` :-) – nIcE cOw Apr 18 '12 at 08:19
  • remove all listeners?? i've never tried before. Was it going to cause the gauge of the memory getting fully loaded if i dont @mKorbel? But i didn't see any of the code written "removeListener".... you just said remove Listener don't u? ?:( – gumuruh Apr 18 '12 at 09:44
  • @gumuruh hehehe, be sure that we can't see rest(whole) of your code :-), then there could be there some endless loop betweens attached Listeners, from above discusions, nobody knows :-) – mKorbel Apr 18 '12 at 10:53
0

The method removeAllItems does not call ItemStateChanged, but it call actionPerformed, you can check it by running this simple code:

public class Tuto {

    public static void main(String[] args) {
        //create the main frame
        JFrame frame = new JFrame();
        frame.setResizable(false);
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.setLayout(null);
        frame.setLocation(new Point(10, 10));
        frame.setPreferredSize(new Dimension(400, 300));

        JComboBox<String> combo = new JComboBox();
        combo.addItem("item 1");
        combo.addItem("item 2");
        combo.addItem("item 3");
        combo.setBounds(50, 30, 300, 20);
        combo.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent ae) {
                System.out.println(" action Performed ");
            }
        });
        frame.add(combo);

        JButton button = new JButton("Remove");
        button.setBounds(50, 100, 100, 30);
        button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent ae) {
                combo.removeAllItems();
            }
        });
        frame.add(button);

        frame.pack();
        frame.setVisible(true);
    }

}