ADDITIONAL SYSTEM INFORMATION :
Linux 4.18.0-305.19.1.el8_4.x86_64 #1 SMP Wed Sep 15 19:12:32 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
openjdk version "11.0.13" 2021-10-19 LTS
OpenJDK Runtime Environment 18.9 (build 11.0.13+8-LTS)
OpenJDK 64-Bit Server VM 18.9 (build 11.0.13+8-LTS, mixed mode, sharing)
OpenJFX 13.0.1
OpenJFX 17.0.1
A DESCRIPTION OF THE PROBLEM :
ComboBoxes that have their items set to a backing list via setItems lose their selected value (= value becomes null) when the backing list's contents are __interactively__ (e.g. via a button click) changed via ObservableList#setAll for the __second__ (!) time. Programmatic #setAll calls to the backing list before the interactive changes do not change the selected value, and neither does the __first__ interactive #setAll call. Once the selected value was lost once, further attempts to set a selected value and trigger an interactive item list content change always cause the selected value to be lost/set to null.
It is fully unclear to me why the __second__ interactive change causes the misbehaviour, and not the programmatic changes or the __first__ interactive call.
Losing the selected value when the item list content is changed is very unexpected and seems like a bug, given that the ComboBox Javadoc states "If the value property is set to a non-null object, and subsequently the items list is cleared, the value property IS NOT nulled out": If the value remains unchanged when the item list is cleared, it should probably also remain unchanged if the item list contents change.
In addition, the behaviour should always be the same, no matter if the change is done interactively (e.g. from a button click handler) or not, and no matter how often changes are made.
The behaviour is always reproduceable with both OpenJFX 13.0.1 and OpenJFX 17.0.1 .
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
- Start the app
- Observe the initial state after the backing list's content have been programmatically changed 3 times:
--- expected + actual (13.0.1, 17.0.1): "B" is selected in the ComboBox <br />
- Use the button one time to interactively change the backing list: <br />
--- expected + actual (13.0.1, 17.0.1): "B" is selected in the ComboBox <br />
- Use the button a second time to interactively change the items: <br />
--- expected: "B" is selected in the ComboBox <br />
--- actual (13.0.1, 17.0.1): null is selected in the ComboBox !! <br />
- Reselect "B" in the ComboBox - Use the button a second time to interactively change the items: <br />
--- expected: "B" is selected in the ComboBox <br />
--- actual (13.0.1, 17.0.1): null is selected in the ComboBox !! <br />
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Selected value should remain unchanged (B) even when the contents of the item list are changed in any way.
ACTUAL -
Selected value is unchanged (B) after programmatic changes to the item list contents, and also after first interactive change. The second interactive change causes the selected value to become null.
---------- BEGIN SOURCE ----------
public static class ComboBoxSetItemsLosesSelectionOnSecondInteractiveSetAllApp extends Application {
/**
* What does this do? <br />
* - Creates a ComboBox backed by an ObservableList (ComboBox#setItems) <br />
* - The backing list is initialized with three strings, one of them is "B". "B" is programmatically selected in
* the ComboBox.<br />
* - We programmatically change the backing list's contents 3 times ("B" is always contained). <br />
* - There is a button to interactively change the backing list's contents ("B" is always contained).
* <p>
* What to try? <br />
* - Start the app <br />
* - Observe the initial state after the backing list's content are changed 3 times:<br />
* --- expected + actual (13.0.1, 17.0.1): "B" is selected in the ComboBox <br />
* - Use the button one time to interactively change the backing list: <br />
* --- expected + actual (13.0.1, 17.0.1): "B" is selected in the ComboBox <br />
* - Use the button a second time to interactively change the items: <br />
* --- expected: "B" is selected in the ComboBox <br />
* --- actual (13.0.1, 17.0.1): null is selected in the ComboBox !! <br />
* - Reselect "B" in the ComboBox - Use the button a second time to interactively change the items: <br />
* --- expected: "B" is selected in the ComboBox <br />
* --- actual (13.0.1, 17.0.1): null is selected in the ComboBox !! <br />
*/
@Override
public void start(final Stage stage) throws Exception {
final ObservableList<String> baseList = FXCollections.observableArrayList();
final List<String> strings1 = List.of("A", "B", "C");
final List<String> strings2 = List.of("D", "B", "E");
baseList.addAll(strings1);
final ComboBox<String> comboBoxWithSetItems = new ComboBox<>();
comboBoxWithSetItems.setItems(baseList);
initSelectionListeners(comboBoxWithSetItems, "comboBoxWithSetItems");
comboBoxWithSetItems.setValue("B");
System.out.println("Programmatic setAll calls");
setListContent(baseList, strings2);
setListContent(baseList, strings1);
setListContent(baseList, strings2);
System.out.println("------");
final Button setAllButton = new Button("Change contents");
setAllButton.setOnAction(evt -> {
System.out.println("Interactive setAll call");
if (baseList.equals(strings1)) {
setListContent(baseList, strings2);
} else {
setListContent(baseList, strings1);
}
});
final StackPane mainRoot = new StackPane(new VBox(comboBoxWithSetItems, setAllButton));
final Scene scene = new Scene(mainRoot);
stage.setTitle("ComboBoxSetAllApp");
stage.setScene(scene);
stage.sizeToScene();
stage.show();
}
private static void initSelectionListeners(final ComboBox<?> comboBox, String string) {
comboBox.valueProperty().addListener((obs, old, newV) -> {
System.out.println(string + " -- " + "new selected value: " + newV);
});
comboBox.getSelectionModel().selectedItemProperty().addListener((obs, old, newItem) -> {
System.out.println(string + " -- " + "new selected item: " + newItem);
});
comboBox.getSelectionModel().selectedIndexProperty().addListener((obs, old, newIndex) -> {
System.out.println(string + " -- " + "new selected index: " + newIndex);
});
}
private static void setListContent(final ObservableList<String> list, final List<String> newContent) {
System.out.println("Changing contents...");
list.setAll(newContent);
System.out.println("Changed contents");
}
public static void main(final String[] args) {
ComboBoxSetItemsLosesSelectionOnSecondInteractiveSetAllApp.launch(args);
}
}
---------- END SOURCE ----------
FREQUENCY : always
Linux 4.18.0-305.19.1.el8_4.x86_64 #1 SMP Wed Sep 15 19:12:32 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
openjdk version "11.0.13" 2021-10-19 LTS
OpenJDK Runtime Environment 18.9 (build 11.0.13+8-LTS)
OpenJDK 64-Bit Server VM 18.9 (build 11.0.13+8-LTS, mixed mode, sharing)
OpenJFX 13.0.1
OpenJFX 17.0.1
A DESCRIPTION OF THE PROBLEM :
ComboBoxes that have their items set to a backing list via setItems lose their selected value (= value becomes null) when the backing list's contents are __interactively__ (e.g. via a button click) changed via ObservableList#setAll for the __second__ (!) time. Programmatic #setAll calls to the backing list before the interactive changes do not change the selected value, and neither does the __first__ interactive #setAll call. Once the selected value was lost once, further attempts to set a selected value and trigger an interactive item list content change always cause the selected value to be lost/set to null.
It is fully unclear to me why the __second__ interactive change causes the misbehaviour, and not the programmatic changes or the __first__ interactive call.
Losing the selected value when the item list content is changed is very unexpected and seems like a bug, given that the ComboBox Javadoc states "If the value property is set to a non-null object, and subsequently the items list is cleared, the value property IS NOT nulled out": If the value remains unchanged when the item list is cleared, it should probably also remain unchanged if the item list contents change.
In addition, the behaviour should always be the same, no matter if the change is done interactively (e.g. from a button click handler) or not, and no matter how often changes are made.
The behaviour is always reproduceable with both OpenJFX 13.0.1 and OpenJFX 17.0.1 .
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
- Start the app
- Observe the initial state after the backing list's content have been programmatically changed 3 times:
--- expected + actual (13.0.1, 17.0.1): "B" is selected in the ComboBox <br />
- Use the button one time to interactively change the backing list: <br />
--- expected + actual (13.0.1, 17.0.1): "B" is selected in the ComboBox <br />
- Use the button a second time to interactively change the items: <br />
--- expected: "B" is selected in the ComboBox <br />
--- actual (13.0.1, 17.0.1): null is selected in the ComboBox !! <br />
- Reselect "B" in the ComboBox - Use the button a second time to interactively change the items: <br />
--- expected: "B" is selected in the ComboBox <br />
--- actual (13.0.1, 17.0.1): null is selected in the ComboBox !! <br />
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Selected value should remain unchanged (B) even when the contents of the item list are changed in any way.
ACTUAL -
Selected value is unchanged (B) after programmatic changes to the item list contents, and also after first interactive change. The second interactive change causes the selected value to become null.
---------- BEGIN SOURCE ----------
public static class ComboBoxSetItemsLosesSelectionOnSecondInteractiveSetAllApp extends Application {
/**
* What does this do? <br />
* - Creates a ComboBox backed by an ObservableList (ComboBox#setItems) <br />
* - The backing list is initialized with three strings, one of them is "B". "B" is programmatically selected in
* the ComboBox.<br />
* - We programmatically change the backing list's contents 3 times ("B" is always contained). <br />
* - There is a button to interactively change the backing list's contents ("B" is always contained).
* <p>
* What to try? <br />
* - Start the app <br />
* - Observe the initial state after the backing list's content are changed 3 times:<br />
* --- expected + actual (13.0.1, 17.0.1): "B" is selected in the ComboBox <br />
* - Use the button one time to interactively change the backing list: <br />
* --- expected + actual (13.0.1, 17.0.1): "B" is selected in the ComboBox <br />
* - Use the button a second time to interactively change the items: <br />
* --- expected: "B" is selected in the ComboBox <br />
* --- actual (13.0.1, 17.0.1): null is selected in the ComboBox !! <br />
* - Reselect "B" in the ComboBox - Use the button a second time to interactively change the items: <br />
* --- expected: "B" is selected in the ComboBox <br />
* --- actual (13.0.1, 17.0.1): null is selected in the ComboBox !! <br />
*/
@Override
public void start(final Stage stage) throws Exception {
final ObservableList<String> baseList = FXCollections.observableArrayList();
final List<String> strings1 = List.of("A", "B", "C");
final List<String> strings2 = List.of("D", "B", "E");
baseList.addAll(strings1);
final ComboBox<String> comboBoxWithSetItems = new ComboBox<>();
comboBoxWithSetItems.setItems(baseList);
initSelectionListeners(comboBoxWithSetItems, "comboBoxWithSetItems");
comboBoxWithSetItems.setValue("B");
System.out.println("Programmatic setAll calls");
setListContent(baseList, strings2);
setListContent(baseList, strings1);
setListContent(baseList, strings2);
System.out.println("------");
final Button setAllButton = new Button("Change contents");
setAllButton.setOnAction(evt -> {
System.out.println("Interactive setAll call");
if (baseList.equals(strings1)) {
setListContent(baseList, strings2);
} else {
setListContent(baseList, strings1);
}
});
final StackPane mainRoot = new StackPane(new VBox(comboBoxWithSetItems, setAllButton));
final Scene scene = new Scene(mainRoot);
stage.setTitle("ComboBoxSetAllApp");
stage.setScene(scene);
stage.sizeToScene();
stage.show();
}
private static void initSelectionListeners(final ComboBox<?> comboBox, String string) {
comboBox.valueProperty().addListener((obs, old, newV) -> {
System.out.println(string + " -- " + "new selected value: " + newV);
});
comboBox.getSelectionModel().selectedItemProperty().addListener((obs, old, newItem) -> {
System.out.println(string + " -- " + "new selected item: " + newItem);
});
comboBox.getSelectionModel().selectedIndexProperty().addListener((obs, old, newIndex) -> {
System.out.println(string + " -- " + "new selected index: " + newIndex);
});
}
private static void setListContent(final ObservableList<String> list, final List<String> newContent) {
System.out.println("Changing contents...");
list.setAll(newContent);
System.out.println("Changed contents");
}
public static void main(final String[] args) {
ComboBoxSetItemsLosesSelectionOnSecondInteractiveSetAllApp.launch(args);
}
}
---------- END SOURCE ----------
FREQUENCY : always
- duplicates
-
JDK-8279140 ComboBox can lose selected value on item change via setAll
- Resolved
- relates to
-
JDK-8263942 ComboBox: action incorrectly triggered after items.setAll
- Open