Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SelectedItems empty if multiple rows selected using different columns

I have a TableView in SelectionMode.MULTIPLE. Using a ListChangeListener I'm able to catch the selection of multiple rows (by pressing Shift).

However my solution only works if the items are being selected in the same column OR in the area without columns. Gif for illustration with 4 examples:

  1. OK: Selecting 3 items using Shift in State column
  2. OK: Selecting 4 items using Shift in Idx column
  3. OK: Selecting 4 items using Shift starting from State column to area without columns
  4. Error: Trying to select 4 items using Shift starting from State column to Data Item column

Selection of multiple items in different columns

The problem seems to be that the SelectedItems-list is apparently empty in the last example. I'd really appreciate your help regarding this issue.

Here is my approach:

ObservableList<DataRowModel> dataRows = FXCollections.observableArrayList();
dataRows.addAll(dataSetModel.getRows());
tableDataRow.setItems(dataRows);
tableDataRowStateColumn.setCellValueFactory(f -> f.getValue().getState());

tableDataRow.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
tableDataRow.getSelectionModel().getSelectedItems()
.addListener((ListChangeListener.Change<? extends DataRowModel> c) -> {
    while (c.next()) {
        c.getRemoved().stream().forEach(remitem -> remitem.setSelected(false));
        c.getAddedSubList().stream().forEach(additem -> additem.setSelected(true));
        System.out.println(c.getList()); //Empty [] when selected using different columns
    }
});

Just for a better understanding of my code: setSelected(...) sets a BooleanProperty on my DataRowModel which is bound to the State-Column.

Without context the reason for using this selected-property seems to be quite silly. However, there are various other fragments of code with ChangeListeners bound to the selected-property.

SSCCE ready to run:

import javafx.application.Application;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
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.Group;
import javafx.scene.Scene;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.stage.Stage;

public class TableViewSample extends Application {

    private TableView<DataRowModel> tableDataRow = new TableView<DataRowModel>();
    private TableColumn<DataRowModel, String> tableDataRowNameColumn = new TableColumn<>("Data Item");
    private TableColumn<DataRowModel, String> tableDataRowStateColumn = new TableColumn<>("State");

    private final ObservableList<DataRowModel> dataRows =
        FXCollections.observableArrayList(
            new DataRowModel("Concinna",  false),
            new DataRowModel("Concinna",  false),
            new DataRowModel("Concinna",  false),
            new DataRowModel("Concinna",  false),
            new DataRowModel("Concinna",  false)
        );

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage stage) {
        Scene scene = new Scene(new Group());
        stage.setTitle("Table View Sample");
        stage.setWidth(500);
        stage.setHeight(500);

        tableDataRow.setItems(dataRows);
        tableDataRowNameColumn.setCellValueFactory(f -> f.getValue().getName());
        tableDataRowStateColumn.setCellValueFactory(f -> f.getValue().getState());

        tableDataRow.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
        tableDataRow.getSelectionModel().getSelectedItems()
          .addListener((ListChangeListener.Change<? extends DataRowModel> c) -> {
            while (c.next()) {
                c.getRemoved().stream().forEach(remitem -> remitem.setSelected(false));    
                c.getAddedSubList().stream().forEach(additem -> additem.setSelected(true));                
            }
        });

        tableDataRow.getColumns().addAll(tableDataRowNameColumn, tableDataRowStateColumn);

        ((Group) scene.getRoot()).getChildren().addAll(tableDataRow);

        stage.setScene(scene);
        stage.show();
    }

    public static class DataRowModel {
        private StringProperty name = new SimpleStringProperty(this, "name", "");
        private BooleanProperty selected = new SimpleBooleanProperty(this, "selected", true);
        private StringProperty state = new SimpleStringProperty(this, "state", "");

        public DataRowModel(String name, boolean selected) {
            this.name.setValue(name);
            this.selected.setValue(selected);

            this.selected.addListener((observable, oldVal, newVal) -> {
                getState(); // Refresh State value
            });
        }

        public StringProperty getName() {
            return name;
        }
        public BooleanProperty isSelected() {
            return selected;
        }
        public void setSelected(boolean selected) {
            if (this.selected.getValue() != selected)
                this.selected.setValue(selected);
        }

        public StringProperty getState() {
            String stateStr = "";
            if (selected.getValue())
                stateStr += "Selected";
            state.setValue(stateStr);
            return state;
        }
    }
}
like image 319
Luke Avatar asked Jun 15 '15 12:06

Luke


1 Answers

I was able to generate this by editing the Oracle's Person tableview example.

This is a bug, filed as https://bugs.openjdk.java.net/browse/JDK-8096787, and fixed in version 8u60 which is expected to be released in August 2015.

like image 178
Uluk Biy Avatar answered Sep 16 '22 17:09

Uluk Biy