0

I am having an issue where calling JComboBox.setSelectedIndex(0) causes my program to crash. Any help would be greatly appreciated!!

On itemStateChanged() starts a new Thread to handle UpdateAllForms.

UpdateAllForms calls updateComboModel() which Queries an SQL Database to update the ComboBoxModel and adds an additional option 'Select...'

This all works fine, however if i add JComboBox.setSelectedIndex(0) the program crashes with no exception etc. I assume the issue is with threading?

itemStateChanged() Method:

public void itemStateChanged(ItemEvent e) {

    if (e.getStateChange() == ItemEvent.DESELECTED) {

        Runnable updateRunnable = new UpdateAllForms(e.getSource());
        new Thread(updateRunnable).start();
    }

}

UpdateAllForms Class:

// <<=== UpdateAllForms Class ===>>
// Only Updates Forms below the Current Form
// Must be ran as a Separate Thread due to swing concurrency
// ==============================================================================
public class UpdateAllForms implements Runnable {

    Object source = null;

    public UpdateAllForms(Object source) {
        this.source = source;
    }

    @Override
    public void run() {
        // TODO Auto-generated method stub

        boolean shouldUpdate = false;

        Logger.write("PropConfDialog.updateAllForms");

        // Loop through Forms
        for (int formCount = 0; formCount < dataInputForms.get(1).size(); formCount++) {

            Component curForm = dataInputForms.get(1).get(formCount);
            // Update Forms after current form
            if (shouldUpdate) {

                if (curForm instanceof JSQLComboPanel) {
                    JSQLComboPanel panel = (JSQLComboPanel) curForm;
                    // Resets the where String
                    panel.setWhereString(getInputString(panel.getInputID()));
                    panel.updateComboModel();
                    shouldUpdate = true;
                    continue;
                } else if (curForm instanceof JSQLLabelPanel) {
                    JSQLLabelPanel panel = (JSQLLabelPanel) curForm;
                    panel.setWhereString(getInputString(panel.getInputID()));
                    panel.updateLabel();
                    shouldUpdate = true;
                    Logger.write("LABEL CAN CARRY OUT");
                    continue;

                }// End if/else

            } // End should update

            if (source == ((JSQLComboPanel) dataInputForms.get(1).get(formCount)).getComboBox()) {
                shouldUpdate = true;
            }// End if

        }// End Loop

    }// End updateAllCombos()

}// End UpdateAllForms Class

JSQLComboPanel Class - updateComboModel Method !!THIS IS THE ISSUE!!! if I call combo.setSelectedIndex(0) in this method the program crashes.

public void updateComboModel(){

    if(comboType == TYPE_DRIVEN_COMBO){

        ArrayList values = SQLTools.getColValues(lkTable, lkNameCol);
        combo.setModel(new DefaultComboBoxModel(values.toArray(new String[values.size()])));


    }else if(comboType == TYPE_WHERE_COMBO){

        ArrayList values = SQLTools.executeJoin(fkTable, fkIDCol, fkNameCol, lkTable, lkIDCol, lkNameCol, whereString);
        combo.setModel(new DefaultComboBoxModel(values.toArray(new String[values.size()])));

    }else if(comboType == TYPE_WHERE_LINKED_COMBO){

        ArrayList values = SQLTools.executeLinkTableJoin(fkTable, fkIDCol, fkNameCol, linkTable, fkIDCol, lkIDCol, lkTable, lkIDCol, lkNameCol,whereString);
        combo.setModel(new DefaultComboBoxModel(values.toArray(new String[values.size()])));

    }//End if/else

    combo.insertItemAt("Select...", 0);
    //combo.setSelectedIndex(0);
    combo.repaint();

}//End updateComboModel()

If anybody can shed any light, that would be fantastic! I am fairly new to Java especially Threading!

Thanks again

Tim

Cam1989
  • 1,241
  • 2
  • 9
  • 9
  • Just a general hint: All code which accesses or even depends on the state of the GUI is to be executed from within the event dispatch thread. For your task, a [`SwingWorker`](https://stackoverflow.com/questions/782265/how-do-i-use-swingworker-in-java) should be used, I think. – Steffen Feb 23 '15 at 11:24

1 Answers1

0

The problem is (almost certainly) related to the fact that you are modifying the state of Swing Components on the wrong thread.

The general rule is:

Code that depends on or modifies the state of a Swing Component should be executed on the Event Dispatch Thread.

A violation of this rule may sometimes be hard to detect - particularly, when only a model is modified, which does not necessarily have a connection to a GUI component!

However, in your case, the main problem is more obvious, because there is (at least) the problematic call

combo.setModel(new DefaultComboBoxModel(values.toArray(new String[values.size()])));

which happens on an own thread, and modifies the Swing component directly.

As suggested in the comments, you should definitiely consider using a SwingWorker. More details about the SwingWorker (and threading in Swing in general) can be found in the article about Concurrency In Swing


A quick workaround for your problem could be the following:

...
// Remove this line
//combo.setModel(new DefaultComboBoxModel(values.toArray(new String[values.size()])));

// Replace it with this line
setModelOnEDT(combo, new DefaultComboBoxModel(values.toArray(new String[values.size()]));

and create a method like this:

private static void setModelOnEDT(
    final JComboBox comboBox, final ComboBoxModel model)
{
    SwingUtilities.invokeLater(new Runnable() 
    {
        @Override
        public void run()
        {
            comboBox.setModel(model);
        }
    });
} 

This is certainly not the prettiest solution, but the simplest, until you modify the code to use a SwingWorker.

Marco13
  • 53,703
  • 9
  • 80
  • 159
  • Hello All, Thanks for your great suggestions. I have a much better understanding of the SwingWorker/EDT now. However the issue doesn't seem to be with the setModel(); method. It is with the setSelectedIndex() or setSelectedItem() method. I have tried creating a setSelectedOnEDT method similar to the above and it still didn't work! As a work-around, rather than blanking the ComboBoxes (Which was the goal) I have put a tick/question mark next to the Box, to represent its status, which I can update easily. No idea why this works and the setSelected doesn't! – Cam1989 Feb 24 '15 at 13:22
  • @Cam1989 Although the exception comes from the `setSelectedIndex` method, the actual **reason** for the exception is (most likely) the invalid thread access. Imagine that the Swing EDT is just about to set the index. Then, a different thread "destroys" the contents of the combo box by executing `setModel`. Then, the EDT tries to continue with setting the index, but the index has become invalid in the meantime. (That's just one form how such a "race condition" due to invalid/unsynchronized thread accesses may show up) – Marco13 Feb 24 '15 at 15:37