A DESCRIPTION OF THE PROBLEM :
Listeners on tableView.getSelectionModel().getSelectedItems() are not notified, when the selection changes because an element was removed.
this works if the selectionMode is SINGLE, but not if it's MULTIPLE
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
run the sample application, select the first row "A" and press the "remove first" button.
check console output.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Listeners on tableView.getSelectionModel().getSelectedItems() are notified
ACTUAL -
Listeners on tableView.getSelectionModel().getSelectedItems() are NOT notified
---------- BEGIN SOURCE ----------
package test;
import java.util.ArrayList;
import java.util.List;
import javafx.application.Application;
import javafx.beans.InvalidationListener;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class TestTableSelectedItems extends Application {
public static void main(String[] pArgs) {
launch(pArgs);
}
@Override
public void start(Stage pStage) {
pStage.setTitle("TableView Test");
final TableView<SomeItem> tableView = new TableView<>();
tableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
ObservableList<SomeItem> list = FXCollections.observableArrayList();
list.addAll(createItems());
tableView.setItems(list.sorted());
addColumns(tableView);
BorderPane bp = new BorderPane(tableView);
Button bRemove = new Button("remove first");
bRemove.setOnAction(event -> {
if (!list.isEmpty()) {
list.remove(0);
}
});
bp.setTop(new HBox(bRemove));
pStage.setScene(new Scene(bp, 300, 500));
pStage.show();
tableView.getSelectionModel().selectedItemProperty().addListener((obs, o, n) -> {
System.err.println("selected item: " + n);
});
tableView.getSelectionModel().getSelectedItems().addListener((InvalidationListener)o -> {
System.err.println("invalidation listener - selected items: " + tableView.getSelectionModel().getSelectedItems());
});
tableView.getSelectionModel().getSelectedItems().addListener((ListChangeListener<SomeItem>) change -> {
System.err.println("change listenener - selected items: " + tableView.getSelectionModel().getSelectedItems());
});
}
private static void addColumns(final TableView<SomeItem> tableView) {
TableColumn<SomeItem, String> colName = new TableColumn<>("Name");
colName.setPrefWidth(200);
colName.setCellValueFactory(item -> item.getValue().name);
tableView.getColumns().add(colName);
TableColumn<SomeItem, Integer> colNumber = new TableColumn<>("Number");
colNumber.setPrefWidth(200);
colNumber.setCellValueFactory(item -> item.getValue().number);
tableView.getColumns().add(colNumber);
}
private static List<SomeItem> createItems() {
List<SomeItem> items = new ArrayList<>();
items.add(new SomeItem("A", 1));
items.add(new SomeItem("B", 2));
items.add(new SomeItem("C", 3));
items.add(new SomeItem("D", 4));
return items;
}
private static class SomeItem {
private final StringProperty name = new SimpleStringProperty(this, "name");
private final ObjectProperty<Integer> number = new SimpleObjectProperty<>(this, "number");
public SomeItem(String name, int number) {
this.name.set(name);
this.number.set(number);
}
@Override
public String toString() {
return "SomeItem [name=" + name.get() + ", number=" + number.get() + "]";
}
}
}
---------- END SOURCE ----------
FREQUENCY : always
Listeners on tableView.getSelectionModel().getSelectedItems() are not notified, when the selection changes because an element was removed.
this works if the selectionMode is SINGLE, but not if it's MULTIPLE
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
run the sample application, select the first row "A" and press the "remove first" button.
check console output.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Listeners on tableView.getSelectionModel().getSelectedItems() are notified
ACTUAL -
Listeners on tableView.getSelectionModel().getSelectedItems() are NOT notified
---------- BEGIN SOURCE ----------
package test;
import java.util.ArrayList;
import java.util.List;
import javafx.application.Application;
import javafx.beans.InvalidationListener;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class TestTableSelectedItems extends Application {
public static void main(String[] pArgs) {
launch(pArgs);
}
@Override
public void start(Stage pStage) {
pStage.setTitle("TableView Test");
final TableView<SomeItem> tableView = new TableView<>();
tableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
ObservableList<SomeItem> list = FXCollections.observableArrayList();
list.addAll(createItems());
tableView.setItems(list.sorted());
addColumns(tableView);
BorderPane bp = new BorderPane(tableView);
Button bRemove = new Button("remove first");
bRemove.setOnAction(event -> {
if (!list.isEmpty()) {
list.remove(0);
}
});
bp.setTop(new HBox(bRemove));
pStage.setScene(new Scene(bp, 300, 500));
pStage.show();
tableView.getSelectionModel().selectedItemProperty().addListener((obs, o, n) -> {
System.err.println("selected item: " + n);
});
tableView.getSelectionModel().getSelectedItems().addListener((InvalidationListener)o -> {
System.err.println("invalidation listener - selected items: " + tableView.getSelectionModel().getSelectedItems());
});
tableView.getSelectionModel().getSelectedItems().addListener((ListChangeListener<SomeItem>) change -> {
System.err.println("change listenener - selected items: " + tableView.getSelectionModel().getSelectedItems());
});
}
private static void addColumns(final TableView<SomeItem> tableView) {
TableColumn<SomeItem, String> colName = new TableColumn<>("Name");
colName.setPrefWidth(200);
colName.setCellValueFactory(item -> item.getValue().name);
tableView.getColumns().add(colName);
TableColumn<SomeItem, Integer> colNumber = new TableColumn<>("Number");
colNumber.setPrefWidth(200);
colNumber.setCellValueFactory(item -> item.getValue().number);
tableView.getColumns().add(colNumber);
}
private static List<SomeItem> createItems() {
List<SomeItem> items = new ArrayList<>();
items.add(new SomeItem("A", 1));
items.add(new SomeItem("B", 2));
items.add(new SomeItem("C", 3));
items.add(new SomeItem("D", 4));
return items;
}
private static class SomeItem {
private final StringProperty name = new SimpleStringProperty(this, "name");
private final ObjectProperty<Integer> number = new SimpleObjectProperty<>(this, "number");
public SomeItem(String name, int number) {
this.name.set(name);
this.number.set(number);
}
@Override
public String toString() {
return "SomeItem [name=" + name.get() + ", number=" + number.get() + "]";
}
}
}
---------- END SOURCE ----------
FREQUENCY : always