sinujohn
sinujohn

Reputation: 2604

ComboBox not showing bound values

I have a comboBox cb and an ObservableList<StringProperty> data I have bound the cb's Items to data as follows:

Bindings.bindContent(cb.getItems(), data);

Suppose data has the following items: str1, str2, str3, str4

When I change data, the combobox gets the new list without any problem. But if str3 is selected in cb and I change the value of str3 to NewStr3 in data, that change is not getting displayed in cb. And sometimes the list displayed is also wrong (it shows str3 instead of NewStr3) eventhough underlying data it refers is correct.

How can I force combobox to display new values when the underlying model is changed?

Upvotes: 1

Views: 698

Answers (2)

kleopatra
kleopatra

Reputation: 51524

Oops ... mis-read the comboBox for a choiceBox - while the basics of this answer apply to both combo- and choiceBox, I don't have a custom ComboBoxX - yet :-)

Basically, it's the responsibility of the SelectionModel to update itself on changes to the items. The intended behaviour implemented in core is to completely clear the selection - that is, null the selectedItem and set selectedIndex to -1 - if the old item was the selectedItem and is replaced or removed. The typical solution for custom behaviour is to implement a custom selection model and set it:

/**
 * A SelectionModel that updates the selectedItem if it is contained in
 * the data list and was replaced/updated.
 * 
 * @author Jeanette Winzenburg, Berlin
 */
public static class MySelectionModel<T> extends ChoiceBoxSelectionModel<T> {

    public MySelectionModel(ChoiceBoxX<T> cb) {
        super(cb);
    }

    @Override
    protected void itemsChanged(Change<? extends T> c) {
        // selection is in list
        if (getSelectedIndex() != -1) {
            while (c.next()) {
                if (c.wasReplaced() || c.wasUpdated()) {
                    if (getSelectedIndex() >= c.getFrom()
                            && getSelectedIndex() < c.getTo()) {
                        setSelectedItem(getModelItem(getSelectedIndex()));
                        return;
                    }
                }
            }
        }
        // super expects a clean change
        c.reset();
        super.itemsChanged(c);
    }
 }

 // usage
 myChoiceBox.setSelectionModel(new MySelectionModel(myChoiceBox));

Unfortunately, core choiceBox doesn't play by the rule - it severely interferes with model's responsibilities (probably because the model implementation doesn't stand up to its duties) which requires a complete re-write of the whole collaborator-stack (choiceBox, -skin, copied -behaviour) such as ChoiceBoxX - which I did just to learn a bit, try remove some of its smells and fix some bugs.

Upvotes: -1

James_D
James_D

Reputation: 209225

The selected item in a combo box is not required to be an element of the combo box's items list. (For example, in an editable combo box, you can type in an item which is not in the list.) If you think about your example from this perspective, it's no surprise that it behaves as you describe.

If you want to force the selected value to be an element of the underlying list when that list may change, you need to define how the selected item should change if the list changes in a way in which it no longer contains the selected item (it is not obvious how you will do this, and probably depends on your application logic). Once you know what you want to do, you can implement it with a ListChangeListener:

cb.getItems().addListener((ListChangeListener.Change change) -> {
    String newSelectedItem = ... ; // figure item that should be selected instead
    cb.setValue(newSelectedItem);
});

The simplest implementation would be just cb.setValue(null);, which would mean no item was selected if the list changed so that it no longer contained the currently selected item.

Upvotes: 2

Related Questions