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
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);