Reputation: 1403
Using the CheckBoxTableCell
in a TableView
I wan't to perform some code when the user clicks the checkbox.
This code needs to be performed before the CheckBox get's checked and before the associated property is changed.
Since CheckBoxTableCell
does not trigger startEdit()
, commitEdit()
and so on I need another point where I can put my code.
updateItem()
would be too late, since the value has already been changed there.
Does anybody know any other place where I can insert my code? Or am I better of just writing my own CheckBoxTableCell?
For those who want to think further: When the user checks the checkBox I want to run a code that checks if his decision may cause him problems later on. When the property changes I have a changeListener that uploads the new value to a database. If there may be any problems (my code returns true) then there should be the obligatory error message: "Do you really want to do this...blabla" and only if he confirms the check box should be actually checked, the value changed and uploaded to the database.
This is how I set up my CheckBoxTableCell
:
//init cell factories
Callback<TableColumn<PublicMovie, Boolean>, TableCell<PublicMovie, Boolean>> checkBoxCellFactory
= (TableColumn<PublicMovie, Boolean> p) -> new CheckBoxTableCell<>();
//Assign Column
allMoviesCheckBoxColumn = (TableColumn<PublicMovie, Boolean>) allMoviesTableView.getColumns().get(0);
//Set CellValueFactory
allMoviesCheckBoxColumn.setCellValueFactory(cellData -> cellData.getValue().getSomeBooleanProperty();
someBooleanProperty
has a listener. When changed to true, the statusProperty
changes to 1
.
This code snippet is in the constructor of the TableView
s underlying class. Whenever the user clicks the CheckBox, it is executed.
this.status.addListener((ObservableValue<? extends Number> observable, Number oldValue, Number newValue) -> {
DataHandler.fireOnChangedEvent(PublicMovieChangeListener.STATUS_CHANGED, getMovie(), newValue, oldValue);
});
DataHandler
holds a list of Listeners
(so far only one). The following code is executed when the onChange
event is called by the DataHandler
:
@Override
public void onChange(int changeID, PublicMovie changedPublicMovie, Object newValue, Object oldValue) {
switch (changeID) {
case STATUS_CHANGED:
boolean mayUpdate = check();
//check is a placeholder for the code that checks for problems.
//It will return false if there might be a problem.
if(!mayUpdate){
mayUpdate = ErrorDialogs.getConfirmationDialog("Überfüllung!", header, content);
}
if (mayUpdate) {
updateMovie(changedPublicMovie);
} else {
changedPublicMovie.getStatusProperty().set((int) oldValue);
refreshAllMoviesTable();
}
break;
case THREE_DIM_CHANGED:
updateMovie(changedPublicMovie);
break;
case CHILDREN_CHANGED:
updateMovie(changedPublicMovie);
break;
}
What you can see here is my attempt to reverse the changed Property... but the checkbox remains checked. Thus I'm looking for a way to perform the checking prior to changing the Property.
Upvotes: 3
Views: 2605
Reputation: 21799
Based on the documentation of CheckBoxTableCell
:
If you want to be notified of changes, it is recommended to directly observe the boolean properties that are manipulated by the
CheckBox
.
I don't say that it is impossible with CheckBoxTableCell
, but most probably it is not so straightforward.
This kind of "pre-select" events can be managed in most of the cases with EventFilter
s.
Therefore you could try to use a normal TableColumn
with the appropriate cellFactory
that utilizes these EventFilter
s.
My example
The used cellFactory
:
tableCol.setCellFactory(p -> {
CheckBox checkBox = new CheckBox();
TableCell<Person, Boolean> tableCell = new TableCell<Person, Boolean>() {
@Override
protected void updateItem(Boolean item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null)
setGraphic(null);
else {
setGraphic(checkBox);
checkBox.setSelected(item);
}
}
};
checkBox.addEventFilter(MouseEvent.MOUSE_PRESSED, event ->
validate(checkBox, (Person) cell.getTableRow().getItem(), event));
checkBox.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
if(event.getCode() == KeyCode.SPACE)
validate(checkBox, (Person) cell.getTableRow().getItem(), event);
});
tableCell.setAlignment(Pos.CENTER);
tableCell.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
return tableCell;
});
and the used validate
method:
private void validate(CheckBox checkBox, Person item, Event event){
// Validate here
event.consume();
Alert alert = new Alert(AlertType.CONFIRMATION);
alert.setTitle("Confirmation Dialog");
alert.setHeaderText("Look, a Confirmation Dialog");
alert.setContentText("Are you ok with this?");
// Set the checkbox if the user want to continue
Optional<ButtonType> result = alert.showAndWait();
if (result.get() == ButtonType.OK)
checkBox.setSelected(!checkBox.isSelected());
}
What is does:
It renders a simple CheckBox
and adds two EventFilter
s to this CheckBox
. The first one is executed on mouse press, the second one on keypress (here the KeyCode.SPACE
is used). Both of them call the validate
method. The validate
method consumes the event (ensures that the selection state of the CheckBox
is not modified), "validates" the input (no validation on my side) then shows a confirmation dialog. If the user agrees, the selectedProperty
of the CheckBox
is set.
Upvotes: 6