I'm trying to write a custom TableViewSelectionModel
to use with my TableView
in JavaFX. I've implemented all the abstract methods, and am updating the selected items and selected cells lists. However, the actual selection being shown in the table does not change when I click on another row.
Here is a simplified example, which shows this behavior:
import java.util.*;
import javafx.scene.control.*;
import javafx.scene.control.TableView.TableViewSelectionModel;
import javafx.collections.*;
import javafx.beans.property.ReadOnlyListWrapper;
public class MySelectionModel<S> extends TableViewSelectionModel<S>{
Set<Integer> selection = new HashSet<>();
final ObservableList<TablePosition<S, ?>> selectedCells = FXCollections.<TablePosition<S, ?>>observableArrayList();
final ObservableList<S> selectedItems = FXCollections.<S>observableArrayList();
final ObservableList<Integer> selectedIndices = FXCollections.<Integer>observableArrayList();
public MySelectionModel(TableView<S> tableview){
super(tableview);
setCellSelectionEnabled(false);
}
public ObservableList<S> getSelectedItems(){
return new ReadOnlyListWrapper<S>(selectedItems);
}
public void clearSelection(int row, TableColumn<S, ?> tableColumn){
selection.remove(Integer.valueOf(row));
updateSelection();
}
public void clearAndSelect(int row, TableColumn<S, ?> tableColumn){
selection = Collections.singleton(row);
updateSelection();
}
public void select(int row, TableColumn<S, ?> tableColumn){
selection.add(Integer.valueOf(row));
updateSelection();
}
public boolean isSelected(int row, TableColumn<S, ?> tableColumn){
return selection.contains(Integer.valueOf(row));
}
public ObservableList<TablePosition> getSelectedCells(){
return new ReadOnlyListWrapper<TablePosition>((ObservableList<TablePosition>)(Object)selectedCells);
}
public ObservableList<Integer> getSelectedIndices(){
return new ReadOnlyListWrapper<Integer>(selectedIndices);
}
public void selectBelowCell(){}
public void selectAboveCell(){}
public void selectRightCell(){}
public void selectLeftCell(){}
public void updateSelection(){
List<TablePosition<S, ?>> positions = new ArrayList<>();
List<S> items = new ArrayList<>();
List<Integer> indices = new ArrayList<>();
TableView<S> tableView = getTableView();
for(Integer i : selection){
positions.add(new TablePosition<S, Object>(tableView, i.intValue(), null));
items.add(getTableView().getItems().get(i.intValue()));
indices.add(i);
}
selectedCells.setAll(positions);
selectedItems.setAll(items);
selectedIndices.setAll(indices);
}
}
Which is being set on the TableView
like this:
TableView<ObservableList<MyData>> table = new TableView<>();
table.setSelectionModel(new MySelectionModel(table));
table.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
I've confirmed that the new indices are being correctly set by adding ListChangeListener
s on the two backing ObservableList
s and displaying the Change
s. The output is what I'd expect, e.g.
Selected Cells { [TablePosition [ row: 8, column: null, tableView: TableView@11924c25[styleClass=table-view] ]] replaced by [TablePosition [ row: 12, column: null, tableView: TableView@11924c25[styleClass=table-view] ]] at 0 }
Selected Items { [[MyCustomTableView$Cell@1c988ebb, MyCustomTableView$Cell@6b80b2be]] replaced by [[MyCustomTableView$Cell@4a17d76d, MyCustomTableView$Cell@6e48d6d]] at 0 }
Is there anything I'm missing that would is needed for the TableView
to update its selection when the TableViewSelectionModel
changes its selection?
It seems the method you need to override, in addition to the ones you already have, is
public void select(int row) ;
which you can implement trivially by delegating to your existing select
method:
@Override
public void select(int row) {
select(row, null);
}
I would also recommend overriding the following:
@Override
public void clearAndSelect(int row) {
clearAndSelect(row, null);
}
@Override
public void selectRange(int start, int end) {
IntStream.range(start, end).forEach(selection::add);
updateSelection();
}
@Override
public boolean isSelected(int row) {
return isSelected(row, null);
}
Note also you have one bug. Collections.singleton(...)
returns an unmodifiable list, so if you select a single row, then try to add items to the selection, you get an UnsupportedOperationException
. Your clearAndSelect
needs to be implemented as
public void clearAndSelect(int row, TableColumn<S, ?> tableColumn){
selection.clear();
selection.add(row);
updateSelection();
}