TreeTableView:设置行不可编辑

时间:2018-09-27 02:52:09

标签: java javafx row treetableview

我想根据树中的级别来控制TreeTableView的某些行的样式。如果此行是Table根目录的第一级子级的一部分,则使用setRowFactory并应用样式。样式工作正常,但我也想禁用这些行的复选框。我可以setDisable(true)进行操作,但这也禁用了TreeItem的扩展,并且SetEditable(false)似乎没有任何作用。

编辑:据我了解,表必须设置为可编辑的,然后默认情况下列是可编辑的。但是,如果我设置TreeTableRow.setEditable(true);TreeTableRow.setEditable(false);,我将看不到任何效果。 setEditable的描述似乎正是我想要的,但是我无法以这种方式使用它。

  

void javafx.scene.control.Cell.setEditable(boolean arg0)

     

setEditable   public final void setEditable(boolean value)

     

允许某些单元格无法被编辑。这在以下情况下很有用:例如,列表具有“标题行”-标题行不可编辑是没有意义的,因此它们应将editable设置为false。   参数:value-一个布尔值,表示该单元格是否可编辑。如果> true,则该单元格是可编辑的;如果为false,则该单元格不能被编辑。

主要:

public class TreeTableViewRowStyle extends Application {

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

@Override
public void start(Stage stage) throws Exception {

    // create the treeTableView and colums
    TreeTableView<Person> ttv = new TreeTableView<Person>();
    TreeTableColumn<Person, String> colName = new TreeTableColumn<>("Name");
    TreeTableColumn<Person, Boolean> colSelected = new TreeTableColumn<>("Selected");
    colName.setPrefWidth(100);
    ttv.getColumns().add(colName);
    ttv.getColumns().add(colSelected);
    ttv.setShowRoot(false);
    ttv.setEditable(true);


    // set the columns
    colName.setCellValueFactory(new TreeItemPropertyValueFactory<>("name"));
    colSelected.setCellFactory(CheckBoxTreeTableCell.forTreeTableColumn(colSelected));
    colSelected.setCellValueFactory(new TreeItemPropertyValueFactory<>("selected"));


    ttv.setRowFactory(table-> {
        return new TreeTableRow<Person>(){
            @Override
            public void updateItem(Person pers, boolean empty) {
                super.updateItem(pers, empty);
                boolean isTopLevel = table.getRoot().getChildren().contains(treeItemProperty().get());
                if (!isEmpty()) {
                    if(isTopLevel){
                        setStyle("-fx-background-color:lightgrey;");
                        setEditable(false); //THIS DOES NOT SEEM TO WORK AS I WANT
                        //setDisable(true); //this would disable the checkbox but also the expanding of the tree
                    }else{

                        setStyle("-fx-background-color:white;");
                    }
                }
            }
        };
    });


    // creating treeItems to populate the treetableview
    TreeItem<Person> rootTreeItem = new TreeItem<Person>();
    TreeItem<Person> parent1 = new TreeItem<Person>(new Person("Parent 1"));
    TreeItem<Person> parent2 = new TreeItem<Person>(new Person("Parent 1"));
    parent1.getChildren().add(new TreeItem<Person>(new Person("Child 1")));
    parent2.getChildren().add(new TreeItem<Person>(new Person("Child 2")));
    rootTreeItem.getChildren().addAll(parent1,parent2);


    ttv.setRoot(rootTreeItem);

    // build and show the window
    Group root = new Group();
    root.getChildren().add(ttv);
    stage.setScene(new Scene(root, 300, 300));
    stage.show();
}
}

模特人物:

public class Person {
private StringProperty name;
private BooleanProperty selected;

public Person(String name) {
    this.name = new SimpleStringProperty(name);
    selected = new SimpleBooleanProperty(false);
}

public StringProperty nameProperty() {
    return name;
}

public BooleanProperty selectedProperty() {
    return selected;
}

public void setName(String name){
    this.name.set(name);
}

public void setSelected(boolean selected){
    this.selected.set(selected);
}
}

3 个答案:

答案 0 :(得分:2)

基本问题是,所有可编辑的树/表单元格(或类似CheckBoxXX的伪可编辑单元)都不尊重它们所在行的可编辑性。我认为这是一个错误。

要克服这一点,您必须扩展(伪)可编辑单元格,并使它们尊重行的可编辑单元格。伪编辑单元与实际编辑单元的确切实现方式有所不同。下面是一些内联示例,对于经常使用的情况,您可以将其设为顶级并重复使用。

CheckBoxTreeTableCell:子类并覆盖updateItem以重新绑定其禁用的属性,例如

colSelected.setCellFactory(c -> {
    TreeTableCell cell = new CheckBoxTreeTableCell() {

        @Override
        public void updateItem(Object item, boolean empty) {
            super.updateItem(item, empty);
            if (getGraphic() != null) {
                getGraphic().disableProperty().bind(Bindings
                        .not(
                              getTreeTableView().editableProperty()
                             .and(getTableColumn().editableProperty())
                             .and(editableProperty())
                             .and(getTreeTableRow().editableProperty())
                    ));
            }
        }

    };
    return cell;
});

对于真正的编辑单元,f.i。 TextFieldTreeTableCell:如果行不可编辑,则覆盖startEdit并返回而不调用super

colName.setCellFactory(c -> {
    TreeTableCell cell = new TextFieldTreeTableCell() {

        @Override
        public void startEdit() {
            if (getTreeTableRow() != null && !getTreeTableRow().isEditable()) return;
            super.startEdit();
        }

    };
    return cell;
});

现在,您可以像执行操作一样切换行的可编辑性,对逻辑进行一些更改以确保在所有情况下都可以完全清除:

ttv.setRowFactory(table-> {
    return new TreeTableRow<Person>(){
        @Override
        public void updateItem(Person pers, boolean empty) {
            super.updateItem(pers, empty);
            // tbd: check for nulls!
            boolean isTopLevel = table.getRoot().getChildren().contains(treeItemProperty().get());
            if (!isEmpty() && isTopLevel) {
                //                        if(isTopLevel){
                setStyle("-fx-background-color:lightgrey;");
                setEditable(false); 
            }else{
                setEditable(true);
                setStyle("-fx-background-color:white;");

            }
        }
    };
});

答案 1 :(得分:1)

如果要禁用特定的单元,则在CellFactory中而不是RowFactory中处理禁用逻辑。静态方法 forTreeTableColumn(..) 是一种方便使用的快捷方法。但这不是唯一的方法。您仍然可以为CheckBoxTreeTableCell创建自己的工厂。

所以不是

colSelected.setCellFactory(CheckBoxTreeTableCell.forTreeTableColumn(colSelected));

将细胞工厂设置如下,这应该对您有用。

colSelected.setCellFactory(new Callback<TreeTableColumn<Person, Boolean>, TreeTableCell<Person, Boolean>>() {
            @Override
            public TreeTableCell<Person, Boolean> call(TreeTableColumn<Person, Boolean> column) {
                return new CheckBoxTreeTableCell<Person, Boolean>(){
                    @Override
                    public void updateItem(Boolean item, boolean empty) {
                        super.updateItem(item, empty);
                        boolean isTopLevel = column.getTreeTableView().getRoot().getChildren().contains(getTreeTableRow().getTreeItem());
                        setEditable(!isTopLevel);
                    }
                };
            }
        });

答案 2 :(得分:0)

您可以使用以下实用程序方法,而不是创建自定义TreeTableCell子类,该方法基本上将新的cell-factory安装在委托给原始cell-factory的列上,但是每当一个cell时添加行可编辑性绑定已创建。

public <S, T> void bindCellToRowEditability(TreeTableColumn<S, T> treeTableColumn) {
    // Keep a handle on the original cell-factory.
    Callback<TreeTableColumn<S, T>, TreeTableCell<S, T>> callback = treeTableColumn.getCellFactory();
    // Install a new cell-factory that performs the delegation.
    treeTableColumn.setCellFactory(column -> {
        TreeTableCell<S, T> cell = callback.call(column);
        // Add a listener so that we pick up when a new row is set for the cell.
        cell.tableRowProperty().addListener((observable, oldRow, newRow) -> {
            // If the new row is non-null, we proceed.
            if (newRow != null) {
                // We get the cell and row editable-properties.
                BooleanProperty cellEditableProperty = cell.editableProperty();
                BooleanProperty rowEditableProperty = newRow.editableProperty();
                // Bind the cell's editable-property with its row's property.
                cellEditableProperty.bind(rowEditableProperty);
            }
        });
        return cell;
    });
}

然后您可以将TreeTableView的所有列设置为:

List<TreeTableColumn<S, ?>> columns = treeTableView.getColumns();
columns.forEach(this::bindCellToRowEditability);

您仍然需要自定义TreeTableRow,以检查它是否为顶级,以便为行本身正确设置可编辑的值。但是,现在在该行上设置可编辑值将确保该行中的所有单元格正确反映该行的可编辑属性。