Reputation: 19
I am learning ComboBox in JavaFX and I am trying to build a UI to let the user select the selection mode, SINGLE or MULTIPLE and select the countries listed in the model. I was able to add the change listener for the model and it works when I hold Ctrl + Select the countries while in the Multiple selection mode and the data is also selected. However, when I deselect the earlier selected country, it won't trigger the change and it also won't remove from the String displayed on the bottom of the UI - 'Selected Items are '. I am trying to understand how I can trigger the ComboBox change listener to remove the selected value. Here is the code so far.
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.control.SelectionMode;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
public class Exercise_16_16_Copy extends Application {
ListView<String> listCountries = new ListView<String>();
public static void main(String[] args) {
Application.launch(args);
}
@Override
public void start(Stage primaryStage) throws Exception {
HBox topSection = new HBox();
topSection.setPadding(new Insets(3));
topSection.setSpacing(5);
topSection.setAlignment(Pos.CENTER);
ComboBox<SelectionMode> comboList = new ComboBox<SelectionMode>();
comboList.getItems().add(SelectionMode.SINGLE);
comboList.getItems().add(SelectionMode.MULTIPLE);
comboList.setValue(comboList.getItems().get(0));
comboList.setOnAction(e-> listCountries.getSelectionModel().setSelectionMode(comboList.getValue()));
Label comboBoxLabel = new Label("Selection Mode: ", comboList);
comboBoxLabel.setContentDisplay(ContentDisplay.RIGHT);
topSection.getChildren().add(comboBoxLabel);
HBox bottomSection = new HBox();
bottomSection.setPadding(new Insets(3));
bottomSection.setAlignment(Pos.CENTER_LEFT);
Label lblSelectedItem = new Label("No Selected Items");
lblSelectedItem.setPadding(new Insets(5));
bottomSection.getChildren().add(lblSelectedItem);
Pane centerSection = new Pane();
listCountries.getItems().addAll("China", "Japan", "Korea", "India", "Malaysia", "Vietnam");
listCountries.setPrefHeight(listCountries.getItems().size() * 26);
centerSection.getChildren().add(listCountries);
listCountries.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
lblSelectedItem.setText(getItems());
primaryStage.sizeToScene();
});
BorderPane brdrPane = new BorderPane();
brdrPane.setTop(topSection);
brdrPane.setCenter(centerSection);
brdrPane.setBottom(bottomSection);
Scene scene = new Scene(brdrPane);
primaryStage.setScene(scene);
primaryStage.setTitle("ListView");
primaryStage.show();
}
private String getItems() {
ObservableList<String> selectedCountries = listCountries.getSelectionModel().getSelectedItems();
if (selectedCountries.size() == 0) {
return "No Countries Selected";
}
if (selectedCountries.size() == 1) {
return "Selected Item " + selectedCountries.get(0);
}
String displayText = "Selected Items are ";
for (String country : selectedCountries) {
displayText += country + " ";
}
return displayText;
}
}
getItems() method is used to fetch the selected number of items which I display at the footer of the GUI created. Is it even possible to implement this functionality using ComboBox or should I use any other component? Kindly help!
Upvotes: 0
Views: 4052
Reputation: 19
I tried the approach Jai proposed and it is working but with the limitation that whenever you multi-select the items in the combo-box, it would print the items selected in the label below and if you select more items, it was supposed to expand the scene automatically but there is a limitation with resizing the stage. Here is the code I used for this.
final ObservableList<String> selectedCountries = listCountries.getSelectionModel().getSelectedItems();
lblSelectedItem.textProperty().bind(Bindings.createStringBinding(() -> getItems(pStage), selectedCountries));
In case anyone is looking how I solved this issue, this answer could help! I handled this issue by implementing the following code:
listCountries.setOnMouseClicked(new EventHandler<MouseEvent>() {
@Override
public void handle(final MouseEvent mouseEvent) {
lblSelectedItem.setText(getItems());
if(listCountries.getSelectionModel().getSelectedItems().size() > 2)
listCountries.setPrefWidth(listCountries.getSelectionModel().getSelectedItems().size() * 20);
pStage.sizeToScene();
}
});
Upvotes: 0
Reputation: 8363
You could do all of that in a single binding.
final List<String> selectedCountries = listCountries.getSelectionModel().getSelectedItems();
lblSelectedItem.textProperty().bind(Bindings.createStringBinding(() -> {
if (selectedCountries.isEmpty()) {
return "No Countries Selected";
}
else if (selectedCountries.size() == 1) {
return "Selected Item is " + selectedCountries.get(0);
}
else {
return "Selected Items are " + String.join(", ", selectedCountries);
}
}, selectedCountries));
Upvotes: 1
Reputation: 9869
The issue is with what you are listening to. If you are in MultiSelection mode you can listen to getSelectedItems(). Ofcourse this will work with Single selection as well.. so change your listener implementation to
listCountries.getSelectionModel().getSelectedItems().addListener((ListChangeListener)observable -> {
lblSelectedItem.setText(getItems());
primaryStage.sizeToScene();
});
Upvotes: 0