javafxcheckboxtablecolumn

JavaFX EditCommit on Boolean TableCells


I develop a JavaFX application containing a table. The related data model has a number of fields of type BooleanProperty. What I am after is I need to distinguish between the setup when the tables' setItems() is called and when the model is altered by the user, in order to track whether the data needs to be saved at a later point in time. I understand that there is no setOnEditCommit() for checkboxes in tables. Therefore I tried a few things. The simplest one was

   metronomColumn.setCellValueFactory(p -> {
        hasUnsavedChanges=true;
        return p.getValue().withMetronomProperty();
   });      
   metronomColumn.setCellFactory( tc -> new CheckBoxTableCell<>());

but hasUnsavedChanges=true; is called also on setItems(). So I tried to move it from the CellValueFactory to the CellFactory

   metronomColumn.setCellValueFactory(p -> p.getValue().withMetronomProperty());
   metronomColumn.setCellFactory( tc -> {
        CheckBoxTableCell<Exercise, Boolean> cbCell = new CheckBoxTableCell<>();
        cbCell.setSelectedStateCallback(i -> {
            hasUnsavedChanges=true;
            return exerciseTable.getItems().get(i).withMetronomProperty();
        });
        return cbCell;
    });        

but here hasUnsavedChanges=true; is also called on setItems(). Hence I tried the following which doesn't work at all, since the listener is never called

    metronomColumn.setCellValueFactory(p -> p.getValue().withMetronomProperty());       
    metronomColumn.setCellFactory( tc -> {
        CheckBoxTableCell cbCell = new CheckBoxTableCell<>();
        cbCell.selectedProperty().addListener((ov, oldVal, newVal) -> {
            hasUnsavedChanges=true;
        });
        return cbCell;
    });

How can I seperate changes from the setItems() setup and user triggered once? Thank you in advance.


Solution

  • Don't use the table at all for this: just listen to the data.

    Create your list with an extractor. Then changes to any of the properties will fire an event on the list:

    ObservableList<Exercise> tableItems = FXCollections.observableArrayList(
        e -> new Observable[] {e.withMetronomProperty()});
    tableItems.addAll(...);
    
    tableView.setItems(tableItems);
    
    tableItems.addListener((ListChangeListener.Change<? extends Exercise> change) -> 
        hasUnsavedChanges = true);
    

    You can create the extractor with as many properties as you need:

    ObservableList<Exercise> tableItems = FXCollections.observableArrayList(
        e -> new Observable[] {e.withMetronomProperty(), e.someOtherProperty()});