0

I have a TreeTableView with two columns. The first column shows a label and the second column shows an action text with a custom CellFactory.

The first time I expand the node, the action text is set correctly. But when I repeat to expand the Node, the action text doesn't update.

The screenshots demonstrate the not expected behavior: application start, first expand, second expand


    import javafx.application.Application;
    import javafx.scene.Scene;
    import javafx.scene.control.*;
    import javafx.scene.control.cell.TreeItemPropertyValueFactory;
    import javafx.scene.layout.StackPane;
    import javafx.stage.Stage;
    
    public class MainApp extends Application {
    
        public static void main(String[] args) {
            Application.launch(MainApp.class, args);
        }
    
        @Override
        public void start(Stage primaryStage) throws Exception {
            StackPane stackPane = new StackPane();
    
            final TreeItem<LabeldTreeItem> root = buildTree();
    
            // Creating columns
            TreeTableColumn<LabeldTreeItem, String> labelColumn = new TreeTableColumn<>("label");
            TreeTableColumn<LabeldTreeItem, String> actionColumn = new TreeTableColumn<>("action");
    
            labelColumn.setMinWidth(120);
            actionColumn.setMinWidth(120);
    
            // Defining cell content
            labelColumn.setCellValueFactory(new TreeItemPropertyValueFactory<>("label"));
    
            // cell factory to display action text **click me!**
            actionColumn.setCellFactory(ttc -> new TreeTableCell<LabeldTreeItem, String>() {
                @Override
                protected void updateItem(String value, boolean empty) {
                    super.updateItem(value, empty);
    
                    final LabeldTreeItem item = this.getTreeTableRow().getItem();
    
                    this.setText(null);
                    this.setGraphic(null);
    
                    if (empty) {
                        this.setText("empty");
                    } else if (item == null) {
                        this.setText("null item");
                    } else if (item.isClickMe()) {
                        this.setText("click me!");
                    }
                }
            });
    
            //Creating a tree table view
            final TreeTableView<LabeldTreeItem> treeTableView = new TreeTableView<>(root);
            treeTableView.getColumns().addAll(List.of(labelColumn, actionColumn));
            treeTableView.setShowRoot(false);
    
            stackPane.getChildren().add(treeTableView);
            primaryStage.setScene(new Scene(stackPane, 300, 250));
            primaryStage.show();
        }
    
        public class LabeldTreeItem {
            private final String label;
            private final boolean clickMe;
    
            public LabeldTreeItem(String label, boolean clickMe) {
                this.label = label;
                this.clickMe = clickMe;
            }
    
            public String getLabel() {
                return label;
            }
    
            public boolean isClickMe() {
                return clickMe;
            }
        }
    
        private TreeItem<LabeldTreeItem> buildTree() {
            final TreeItem<LabeldTreeItem> root = new TreeItem<>();
            root.setExpanded(true);
    
            final TreeItem<LabeldTreeItem> node = new TreeItem<>(new LabeldTreeItem("Node", false));
            final TreeItem<LabeldTreeItem> item1 = new TreeItem<>(new LabeldTreeItem("Item 1", true));
            final TreeItem<LabeldTreeItem> item2 = new TreeItem<>(new LabeldTreeItem("Item 2", false));
            final TreeItem<LabeldTreeItem> item3 = new TreeItem<>(new LabeldTreeItem("Item 3", true));
    
            node.getChildren().addAll(List.of(item1, item2, item3));
            root.getChildren().add(node);
    
            return root;
        }    
    }

I have tried these with Java 11 and JavafX 16.

Moritz
  • 1,954
  • 2
  • 18
  • 28
tinyman
  • 1
  • 1
  • 2
    why don't you have a valueFactory on the second column? you need some trigger that the cell is updated .. – kleopatra Aug 09 '21 at 12:22
  • hi @kleopatra, thank you for your suggestion. The code is just an example and the goal of the second column should contain many Buttons with different possible action, that could be triggered. – tinyman Aug 10 '21 at 06:51
  • (at least) if the cell content changes on some condition, a valueFactory is _mandatory_ (otherwise it's not updated which is what you see). F.i. let it return an observable that's bound to the treeItem's expanded, value, property of value as needed. – kleopatra Aug 10 '21 at 09:24
  • Thank you @kleopatra for the inspiring comments. I was able to solve the problem myself. Finally, a valueFactory must be set. In my case it was the "clickMe" property. Thanks – tinyman Aug 11 '21 at 12:41

1 Answers1

0

This Solution works for me. I have to set a valueFactory for the second column. In these case I use my existing "cklickMe" property.


import java.util.List;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.cell.TreeItemPropertyValueFactory;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class MainApp extends Application {

    public static void main(String[] args) {
        Application.launch(MainApp.class, args);
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        StackPane stackPane = new StackPane();

        final TreeItem<LabeldTreeItem> root = buildTree();

        //Creating columns
        TreeTableColumn<LabeldTreeItem, String> labelColumn = new TreeTableColumn<>("label");
        TreeTableColumn<LabeldTreeItem, Boolean> actionColumn = new TreeTableColumn<>("action");

        labelColumn.setMinWidth(120);
        actionColumn.setMinWidth(120);

        // Defining cell content
        labelColumn.setCellValueFactory(new TreeItemPropertyValueFactory<>("label"));
        // ** must be set **
        actionColumn.setCellValueFactory(new TreeItemPropertyValueFactory<>("clickMe"));

        // cell factory to display button:
        actionColumn.setCellFactory(ttc -> new TreeTableCell<LabeldTreeItem, Boolean>() {
            @Override
            protected void updateItem(Boolean value, boolean empty) {
                super.updateItem(value, empty);

                final LabeldTreeItem item = this.getTreeTableRow().getItem();

                this.setText(null);
                this.setGraphic(null);

                if (empty) {
                    this.setText("empty");
                } else if (item == null) {
                    this.setText("null item");
                } else if (item.isClickMe()) {
                    this.setText("click me!");
                }
            }
        });

        //Creating a tree table view
        final TreeTableView<LabeldTreeItem> treeTableView = new TreeTableView<>(root);
        treeTableView.getColumns().addAll(List.of(labelColumn, actionColumn));
        treeTableView.setShowRoot(false);

        stackPane.getChildren().add(treeTableView);
        primaryStage.setScene(new Scene(stackPane, 300, 250));
        primaryStage.show();
    }

    public class LabeldTreeItem {
        private final String label;
        private final Boolean clickMe;

        public LabeldTreeItem(String label, Boolean clickMe) {
            this.label = label;
            this.clickMe = clickMe;
        }

        public String getLabel() {
            return label;
        }

        public boolean isClickMe() {
            return clickMe;
        }
    }

    private TreeItem<LabeldTreeItem> buildTree() {
        final TreeItem<LabeldTreeItem> root = new TreeItem<>();
        root.setExpanded(true);

        final TreeItem<LabeldTreeItem> node = new TreeItem<>(new LabeldTreeItem("Node", false));
        final TreeItem<LabeldTreeItem> item1 = new TreeItem<>(new LabeldTreeItem("Item 1", true));
        final TreeItem<LabeldTreeItem> item2 = new TreeItem<>(new LabeldTreeItem("Item 2", false));
        final TreeItem<LabeldTreeItem> item3 = new TreeItem<>(new LabeldTreeItem("Item 3", true));

        node.getChildren().addAll(List.of(item1, item2, item3));
        root.getChildren().add(node);

        return root;
    }

}

tinyman
  • 1
  • 1