4

I set the model of my combobox in my controller class

cboCategory.setModel(new ModernDefaultComboBoxModel(model.getProductCategories()));

productCategories is a List of String. ModernDefaultComboBoxModel is just model that extends DefaultComboBoxModel.

public class ModernDefaultComboBoxModel extends DefaultComboBoxModel{
    public ModernDefaultComboBoxModel(List<String> elements){
        super(elements.toArray());
    }
}

Now in my model, productCategories is populated from the DB, in a SwingWorker

SwingWorker<Void, String> worker = new SwingWorker<Void, String>() {
    @Override
    protected Void doInBackground() throws Exception {
        //query and resultset stuff
        while (rs.next()) {
            publish(rs.getString(1));
        }
        //cleanup stuff
    }
    @Override protected void process(List<String> chunks){
        List<String> oldCategories = new ArrayList<String>(productCategories);
        for(String cat : chunks){
            productCategories.add(cat);
        }
        fireModelPropertyChange(PRODUCT_CATEGORIES, oldCategories, productCategories);
    }
    @Override
    protected void done(){
        //some stuff
    }
};
worker.execute();

You see every publish, it fires a property change event to its listener (fireModelPropertyChange is just a wrapper for firePropertyChange).

Now in my model listener,

@Override
    public void propertyChange(PropertyChangeEvent evt) {
        String propName = evt.getPropertyName();

        //some branching for the other models

        else if(ProductModel.PRODUCT_CATEGORIES.equals(propName)){
            List<String> newVal = (List<String>)evt.getNewValue();

            //notify the model of the combobox that the data is changed, so refresh urself
        }

        //some stuff
    }

I'm stuck in the part where my ModelListener needs to notify the combobox in the view that data in its model are changed. I have the same situation with JTable but with JTable I can just call fireTableRowsInserted of from its model which is implemented from AbstractTableModel.

Actually, in the AbstractListModel there is a method fireContentsChanged but unlike in the JTable, this method is protected so I can't access it.

I know I can just create an instance of ModernDefaultComboBoxModel then call the setModel method of the combobox to refresh the combobox, but I'm just wondering if there is a "cleaner" way as clean as that of JTable

mKorbel
  • 109,525
  • 20
  • 134
  • 319
Bnrdo
  • 5,325
  • 3
  • 35
  • 63
  • never-ever change the underlying data-structure of a model under its feet (and _never_ call any of its fireXX methods form outside code, that's the inherent responsibility of the model itself) - instead, use model api to add new items – kleopatra Apr 14 '13 at 11:05
  • Ah ok. So is the case also the same for JTable? No right because we can't add data to JTable (like the `addItem` of JComboBox) – Bnrdo Apr 14 '13 at 11:54
  • @kleopatra is correct; it looks like you are planning to register your `ModernDefaultComboBoxModel` as listener to your application's data model and update the combo box model correctly; the combo view will follow. – trashgod Apr 14 '13 at 12:26
  • Do you mean I don't need to do this? Then instead just call the combobox's `addItem` in my application model's listener to add the processed data? – Bnrdo Apr 14 '13 at 12:31

1 Answers1

2

JComboBox implements ListDataListener in order to listen to its own ComboBoxModel. Any change to your DefaultComboBoxModel should invoke the relevant fireXxxx() method in AbstractListModel, and the JComboBox should see the change. Just update the combo's model in process().

Addendum: Here's a minimal example that updates the model. Set a breakpoint on model.addElement(), debug, click on Add, and step into the method to see the call to fireIntervalAdded(), which subsequently updates the view.

JFrame f = new JFrame("ComboWorkerTest");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setLayout(new GridLayout(0, 1));
final JComboBox jcb = new JComboBox(new Integer[]{value});
f.add(new JButton(new AbstractAction("Add") {
    @Override
    public void actionPerformed(ActionEvent e) {
        DefaultComboBoxModel model = (DefaultComboBoxModel) jcb.getModel();
        model.addElement(++value);
    }
}));
f.add(jcb);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
trashgod
  • 203,806
  • 29
  • 246
  • 1,045
  • But my model has no access to the view there for I can't do `combobox.getModel`. It can only update the data of my combobox's model. So how can I do this? – Bnrdo Apr 14 '13 at 11:53
  • You can pass a reference to the combo's model into your worker, or your worker can fire a `PropertyChangeEvent` to which your `ModernDefaultComboBoxModel` listens. – trashgod Apr 14 '13 at 11:58
  • 1
    Ah ok so I can also register `ModernDefaultComboBoxModel` as a listener to my main model. Thank u. – Bnrdo Apr 14 '13 at 12:10