Reputation: 608
How can I disable any cell editable in parent row in treetableview? Please look the pictures and check the sample code. Shortly I want to disable row editable if row is expandable (root row or sub root row)
**Example code **
import javafx.application.Application;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeTableCell;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableView;
import javafx.scene.control.cell.TreeItemPropertyValueFactory;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
import javafx.util.Callback;
public class TreeTableExample extends Application {
public static void main(String[] args) {
Application.launch(args);
}
@Override
@SuppressWarnings("unchecked")
public void start(Stage stage) {
HBox root = new HBox(createTable());
Scene scene = new Scene(root);
stage.setScene(scene);
stage.setTitle("Using a TreeTableView");
stage.show();
}
public TreeTableView createTable() {
TreeTableView<Person> treeTable = new TreeTableView<>();
treeTable.setEditable(true);
Callback<TreeTableColumn<Person, String>,
TreeTableCell<Person, String>> cellFactory
= (TreeTableColumn<Person, String> p) -> new EditingCell();
TreeTableColumn<Person, String> firstName = new TreeTableColumn<>("First Name");
firstName.setCellValueFactory(new TreeItemPropertyValueFactory<>("firstName"));
firstName.setCellFactory(cellFactory);
firstName.setOnEditCommit((TreeTableColumn.CellEditEvent<Person, String> event) -> {
if(event.getNewValue()!=null)
event.getRowValue().getValue().setFirstName(event.getNewValue());
});
TreeTableColumn<Person, String> lastName = new TreeTableColumn<>("Last Name");
lastName.setCellValueFactory(new TreeItemPropertyValueFactory<>("lastName"));
lastName.setCellFactory(cellFactory);
lastName.setOnEditCommit((TreeTableColumn.CellEditEvent<Person, String> event) -> {
if(event.getNewValue()!=null)
event.getRowValue().getValue().setLastName(event.getNewValue());
});
treeTable.getColumns().addAll(firstName, lastName);
TreeItem<Person> root = new TreeItem<>();
for (int i = 0; i < 5; i++) {
root.getChildren().add(new TreeItem<>(new Person()));
}
treeTable.setRoot(root);
return treeTable;
}
public class Person {
private SimpleStringProperty firstName;
private SimpleStringProperty lastName;
public Person(){
firstName = new SimpleStringProperty(this, "firstName");
lastName = new SimpleStringProperty(this, "lastName");
};
public String getFirstName() {
return firstName.get();
}
public void setFirstName(String fName) {
firstName.set(fName);
}
public String getLastName() {
return lastName.get();
}
public void setLastName(String fName) {
lastName.set(fName);
}
}
class EditingCell extends TreeTableCell<Person, String> {
private TextField textField;
public EditingCell() {
}
@Override
public void startEdit() {
if (!isEmpty()) {
super.startEdit();
createTextField();
setText(null);
setGraphic(textField);
textField.selectAll();
}
}
@Override
public void cancelEdit() {
super.cancelEdit();
setText((String) getItem());
setGraphic(null);
}
@Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else if (isEditing()) {
if(!getTreeTableView().getTreeItem(getIndex()).isLeaf())
setEditable(false);
if (textField != null) {
textField.setText(getString());
}
setText(null);
setGraphic(textField);
} else {
setText(getString());
setGraphic(null);
}
}
private void createTextField() {
textField = new TextField(getString());
textField.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2);
textField.focusedProperty().addListener(
(ObservableValue<? extends Boolean> arg0,
Boolean arg1, Boolean arg2) -> {
if (!arg2) {
commitEdit(textField.getText());
}
});
}
private String getString() {
return getItem() == null ? "" : getItem();
}
}
}
just run it and double click on the root item
make-individual-cell-editable-in-javafx-tableview I checked the solution works for tableview but for treetaleview does not work.
Upvotes: 2
Views: 1454
Reputation: 51524
Actually I disagree with the reasoning in the other answer: there is nothing wrong with core TreeTableCell (it does check for its editability before actually starting an edit) - instead the logic in the custom cell implementation is broken. Particularly, the part of updateItem that sets the editable property:
} else if (isEditing()) {
if(!getTreeTableView().getTreeItem(getIndex()).isLeaf())
setEditable(false);
Besides being incomplete in not resetting the editable back to true anywhere (remember: cells are re-used), we allow super to first start editing and only after it started, it's disabled.
This logic error is fixed (in the other answer, copied here for convenience) by unconditionally setting the editability in updateItem:
super.updateItem(item, empty);
TreeItem<Person> treeItem = getTreeTableRow().getTreeItem();
setEditable(treeItem != null && treeItem.isLeaf());
The other usage error (as already noted) was not fully checking cell state before actually configuring the editor. The suggested fix - check cell's editable - isn't quite complete because table/column editability might be disabled as well. To take that into account, I would tend to let super do its job and only configure the editor if editability actually changed, like
super.startEdit();
// super changed state into editing
if (isEditing()) {
// create and install the textField
}
Upvotes: 1
Reputation: 209319
It seems that TreeTableCell
does not properly check its editable
property before deciding whether or not to call startEdit()
. I think that's a bug. You can work around it by checking that yourself in your startEdit()
method:
@Override
public void startEdit() {
if (isEditable() && !isEmpty()) {
super.startEdit();
createTextField();
setText(null);
setGraphic(textField);
textField.selectAll();
}
}
and now in your updateItem()
method, you can check the current tree item from the row, and update editable
as required:
@Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
TreeItem<Person> treeItem = getTreeTableRow().getTreeItem();
setEditable(treeItem != null && treeItem.isLeaf());
if (empty) {
setText(null);
setGraphic(null);
} else if (isEditing()) {
if(!getTreeTableView().getTreeItem(getIndex()).isLeaf())
setEditable(false);
if (textField != null) {
textField.setText(getString());
}
setText(null);
setGraphic(textField);
} else {
setText(getString());
setGraphic(null);
}
}
Upvotes: 2