Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

I want to have control over the styling of some rows of a TreeTableView based on the level in the tree. I used setRowFactory and apply a styling if this row is part of the first level children of the root of the Table. The styling works fine, but I also want to disable clicking on the checkbox for those rows. I am able to setDisable(true) but that also disables the expanding of the TreeItem and SetEditable(false) does not seem to have any effect.

EDIT: What I understand is that the Table must be set editable, then the columns are by default editable. But if I set TreeTableRow.setEditable(true); or TreeTableRow.setEditable(false); I never see any effect. The description seems of setEditable seems exactly what I want but I am unable to use it that way.

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

setEditable public final void setEditable(boolean value)

Allows for certain cells to not be able to be edited. This is useful incases >where, say, a List has 'header rows' - it does not make sense forthe header rows >to be editable, so they should have editable set tofalse. Parameters:value - A boolean representing whether the cell is editable or not.If >true, the cell is editable, and if it is false, the cell can notbe edited.

Main:

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();
    }
}

Model Person :

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);
    }
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
154 views
Welcome To Ask or Share your Answers For Others

1 Answer

The base problem is that none of the editable (nor the pseudo-editable like CheckBoxXX) Tree/Table cells respect the editability of the row they are contained in. Which I consider a bug.

To overcome, you have to extend the (pseudo) editable cells and make them respect the row's editable. The exact implementation is different for pseudo- vs. real editing cells. Below are in-line examples, for frequent usage you would make them top-level and re-use.

CheckBoxTreeTableCell: subclass and override updateItem to re-bind its disabled property like

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;
});

For a real editing cell, f.i. TextFieldTreeTableCell: override startEdit and return without calling super if the row isn't editable

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

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

    };
    return cell;
});

Now you can toggle the row's editability as you do, changed the logic a bit to guarantee full cleanup in all cases:

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;");

            }
        }
    };
});

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...