Forum Stats

  • 3,826,033 Users
  • 2,260,587 Discussions
  • 7,896,768 Comments

Discussions

[JavaFX] editable TreeTableCells

TPD-Opitz
TPD-Opitz Member Posts: 2,465 Silver Trophy
edited Jul 10, 2016 4:40PM in Java 8 Questions

Hello,

I'd like to implement a TreeTableView where cells within a column might be editable depending on other properties of the Object being displayed.

In Swing I'd control this via isCellEditable() Method of TableModel.

What is the intended Way of doing thin in JavaFX?

Here is an SSCCE which is lacking the desired behavior.

Could please someone add the lines to bfing it in?

/*
 * //from www.java2s.com Copyright (c) 2008, 2014, Oracle and/or its affiliates. All rights reserved. Use is
 * subject to license terms. This file is available and licensed under the following license: Redistribution
 * and use in source and binary forms, with or without modification, are permitted provided that the following
 * conditions are met: - Redistributions of source code must retain the above copyright notice, this list of
 * conditions and the following disclaimer. - Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer in the documentation and/or other
 * materials provided with the distribution. - Neither the name of Oracle nor the names of its contributors
 * may be used to endorse or promote products derived from this software without specific prior written
 * permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */
import java.util.Arrays;
import java.util.List;

import javafx.application.Application;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableView;
import javafx.scene.control.cell.CheckBoxTreeTableCell;
import javafx.stage.Stage;

public class FxMain extends Application {

    List<Employee> employees =
            Arrays.<Employee> asList(new Employee("Ethan Williams", "[email protected]", false),
                    new Employee("Emma Jones", "[email protected]", false),
                    new Employee("Michael Brown", "[email protected]", true),
                    new Employee("Anna Black", "[email protected]", true),
                    new Employee("Rodger York", "[email protected]", false),
                    new Employee("Susan Collins", "[email protected]", true));

    final TreeItem<Employee> root = new TreeItem<>(new Employee("Sales Department", "", false));

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

    @Override
    public void start(Stage stage) {
        root.setExpanded(true);
        employees.stream().forEach((employee) -> {
            root.getChildren().add(new TreeItem<>(employee));
        });
        Scene scene = new Scene(new Group(), 400, 400);
        Group sceneRoot = (Group) scene.getRoot();

        TreeTableColumn<Employee, String> empColumn = new TreeTableColumn<>("Employee");
        empColumn.setPrefWidth(150);
        empColumn.setCellValueFactory((TreeTableColumn.CellDataFeatures<Employee, String> param) -> param.getValue()
                .getValue()
                .nameProperty());

        TreeTableColumn<Employee, String> emailColumn = new TreeTableColumn<>("Email");
        emailColumn.setPrefWidth(190);
        emailColumn.setCellValueFactory((TreeTableColumn.CellDataFeatures<Employee, String> param) -> param.getValue()
                .getValue()
                .emailProperty());

        TreeTableColumn<Employee, Boolean> superiorColumn = new TreeTableColumn<>("is Superior");
        superiorColumn.setPrefWidth(190);
        superiorColumn.setCellValueFactory((TreeTableColumn.CellDataFeatures<Employee, Boolean> param) -> {
            Employee employee = param.getValue().getValue();
            return employee.isSuperiorProperty();
        });
        superiorColumn.setCellFactory(col -> {
            // what to change here to get no checkbox for department entry??
            CheckBoxTreeTableCell<Employee, Boolean> checkBoxTreeTableCell = new CheckBoxTreeTableCell<>();
            // what to change here to deactivate checkbox for all superiors??
            checkBoxTreeTableCell.setEditable(false);
            return checkBoxTreeTableCell;
        });

        TreeTableView<Employee> treeTableView = new TreeTableView<>(root);
        treeTableView.setEditable(true);
        treeTableView.getColumns().setAll(empColumn, emailColumn, superiorColumn);
        sceneRoot.getChildren().add(treeTableView);
        stage.setScene(scene);
        stage.show();
    }

    public class Employee {

        private final SimpleStringProperty name;
        private final SimpleStringProperty email;
        private final BooleanProperty isSuperior;

        public Boolean getIsSuperior() {
            return isSuperior.get();
        }

        public void setIsSuperior(Boolean isSuperior) {
            this.isSuperior.set(isSuperior);
        }

        public SimpleStringProperty nameProperty() {
            return name;
        }

        public BooleanProperty isSuperiorProperty() {
            return isSuperior;
        }

        public SimpleStringProperty emailProperty() {
            return email;
        }

        private Employee(String name, String email, Boolean isSuperior) {
            this.name = new SimpleStringProperty(name);
            this.email = new SimpleStringProperty(email);
            this.isSuperior = new SimpleBooleanProperty(isSuperior);
        }

        public String getName() {
            return name.get();
        }

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

        public String getEmail() {
            return email.get();
        }

        public void setEmail(String fName) {
            email.set(fName);
        }

    }
}

Thanks

TPD

Tagged:

Best Answer

  • Unknown
    edited Jul 9, 2016 8:14PM Answer ✓
    I'd like to implement a TreeTableView where cells within a column might be editable depending on other properties of the Object being displayed.
    
    In Swing I'd control this via isCellEditable() Method of TableModel.
    What is the intended Way of doing thin in JavaFX?
    

    Haven't worked with this but a simple web search for EXACTLY what you ask about 'javafx editable tree table cell' produces the Oracle API doc for TreeTableVIew.

    https://docs.oracle.com/javase/8/javafx/api/javafx/scene/control/TreeTableView.html

    Have you reviewed that API? It appears to have the info you need.

    Editing

    This control supports inline editing of values, and this section attempts to give an overview of the available APIs and how you should use them.
    
    Firstly, cell editing most commonly requires a different user interface than when a cell is not being edited. This is the responsibility of the Cell implementation being used. For TreeTableView, it is highly recommended that editing be per-TreeTableColumn, rather than per row, as more often than not you want users to edit each column value differently, and this approach allows for editors specific to each column. It is your choice whether the cell is permanently in an editing state (e.g. this is common for CheckBox cells), or to switch to a different UI when editing begins (e.g. when a double-click is received on a cell).
    
    To know when editing has been requested on a cell, simply override the Cell.startEdit() method, and update the cell text and graphic properties as appropriate (e.g. set the text to null and set the graphic to be a TextField). 
    
    Additionally, you should also override Cell.cancelEdit() to reset the UI back to its original visual state when the editing concludes. In both cases it is important that you also ensure that you call the super method to have the cell perform all duties it must do to enter or exit its editing mode.
    
    Once your cell is in an editing state, the next thing you are most probably interested in is how to commit or cancel the editing that is taking place. This is your responsibility as the cell factory provider. Your cell implementation will know when the editing is over, based on the user input (e.g. when the user presses the Enter or ESC keys on their keyboard). When this happens, it is your responsibility to call Cell.commitEdit(Object) or Cell.cancelEdit(), as appropriate.
    
    When you call Cell.commitEdit(Object) an event is fired to the TreeTableView, which you can observe by adding an EventHandler via TreeTableColumn.setOnEditCommit(javafx.event.EventHandler). Similarly, you can also observe edit events for edit start and edit cancel.
    
    By default the TreeTableColumn edit commit handler is non-null, with a default handler that attempts to overwrite the property value for the item in the currently-being-edited row. It is able to do this as the Cell.commitEdit(Object) method is passed in the new value, and this is passed along to the edit commit handler via the CellEditEvent that is fired. It is simply a matter of calling TreeTableColumn.CellEditEvent.getNewValue() to retrieve this value.  
    
    It is very important to note that if you call TreeTableColumn.setOnEditCommit(javafx.event.EventHandler) with your own EventHandler, then you will be removing the default handler. Unless you then handle the writeback to the property (or the relevant data source), nothing will happen. You can work around this by using the TableColumnBase.addEventHandler(javafx.event.EventType, javafx.event.EventHandler) method to add a TreeTableColumn.EDIT_COMMIT_EVENT EventType with your desired EventHandler as the second argument. Using this method, you will not replace the default implementation, but you will be notified when an edit commit has occurred.
    
    Hopefully this summary answers some of the commonly asked questions. Fortunately, JavaFX ships with a number of pre-built cell factories that handle all the editing requirements on your behalf. You can find these pre-built cell factories in the javafx.scene.control.cell package.
    
    TPD-Opitz

Answers

  • flbauer
    flbauer Member Posts: 1
    edited Jul 9, 2016 11:17AM
        /* 
         * //from www.java2s.com Copyright (c) 2008, 2014, Oracle and/or its affiliates. All rights reserved. Use is 
         * subject to license terms. This file is available and licensed under the following license: Redistribution 
         * and use in source and binary forms, with or without modification, are permitted provided that the following 
         * conditions are met: - Redistributions of source code must retain the above copyright notice, this list of 
         * conditions and the following disclaimer. - Redistributions in binary form must reproduce the above 
         * copyright notice, this list of conditions and the following disclaimer in the documentation and/or other 
         * materials provided with the distribution. - Neither the name of Oracle nor the names of its contributors 
         * may be used to endorse or promote products derived from this software without specific prior written 
         * permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR 
         * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
         * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 
         * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
         * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
         * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
         * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 
         * THE POSSIBILITY OF SUCH DAMAGE. 
         */  
        import java.util.Arrays;
    import java.util.List;
    
    import javafx.application.Application;
    import javafx.beans.property.BooleanProperty;
    import javafx.beans.property.SimpleBooleanProperty;
    import javafx.beans.property.SimpleStringProperty;
    import javafx.event.EventHandler;
    import javafx.scene.Group;
    import javafx.scene.Scene;
    import javafx.scene.control.TreeItem;
    import javafx.scene.control.TreeTableColumn;
    import javafx.scene.control.TreeTableColumn.CellEditEvent;
    import javafx.scene.control.TreeTableView;
    import javafx.scene.control.cell.CheckBoxTreeTableCell;
    import javafx.scene.control.cell.TextFieldTreeTableCell;
    import javafx.stage.Stage;  
          
        public class FxMain extends Application {  
          
            List employees =  
                    Arrays. asList(new Employee("Ethan Williams", "[email protected]", false),  
                            new Employee("Emma Jones", "[email protected]", false),  
                            new Employee("Michael Brown", "[email protected]", true),  
                            new Employee("Anna Black", "[email protected]", true),  
                            new Employee("Rodger York", "[email protected]", false),  
                            new Employee("Susan Collins", "[email protected]", true));  
          
            final TreeItem root = new TreeItem<>(new Employee("Sales Department", "", false));  
          
            public static void main(String[] args) {  
                Application.launch(FxMain.class, args);  
            }  
          
            @Override  
            public void start(Stage stage) {  
                root.setExpanded(true);  
                employees.stream().forEach((employee) -> {  
                    root.getChildren().add(new TreeItem<>(employee));  
                });  
                Scene scene = new Scene(new Group(), 400, 400);  
                Group sceneRoot = (Group) scene.getRoot();  
          
                TreeTableColumn empColumn = new TreeTableColumn<>("Employee");  
                empColumn.setPrefWidth(150);  
                empColumn.setCellValueFactory((TreeTableColumn.CellDataFeatures param) -> param.getValue()  
                        .getValue()  
                        .nameProperty());  
          
                TreeTableColumn emailColumn = new TreeTableColumn<>("Email");  
                emailColumn.setPrefWidth(190);  
                emailColumn.setCellValueFactory((TreeTableColumn.CellDataFeatures param) -> param.getValue()  
                        .getValue()  
                        .emailProperty());  
                
                // For Edit:
                emailColumn.setCellFactory( TextFieldTreeTableCell.forTreeTableColumn() );      
                emailColumn.setOnEditCommit(
                        new EventHandler>() {
                            @Override
                            public void handle(CellEditEvent t) {
                            }
                        }
                    );
                
                TreeTableColumn superiorColumn = new TreeTableColumn<>("is Superior");  
                superiorColumn.setPrefWidth(190);  
                superiorColumn.setCellValueFactory((TreeTableColumn.CellDataFeatures param) -> {  
                    Employee employee = param.getValue().getValue();  
                    return employee.isSuperiorProperty();  
                });  
                superiorColumn.setCellFactory(col -> {  
                    // what to change here to get no checkbox for department entry??  
                    CheckBoxTreeTableCell checkBoxTreeTableCell = new CheckBoxTreeTableCell<>();  
                    // what to change here to deactivate checkbox for all superiors??  
                    checkBoxTreeTableCell.setEditable(false);  
                    return checkBoxTreeTableCell;  
                });  
          
                TreeTableView treeTableView = new TreeTableView<>(root);  
             // For Edit:
                treeTableView.setEditable(true);  
                     
                treeTableView.getColumns().setAll(empColumn, emailColumn, superiorColumn);  
                
                sceneRoot.getChildren().add(treeTableView);  
                stage.setScene(scene);  
                stage.show();  
            }  
          
            public class Employee {  
          
                private final SimpleStringProperty name;  
                private final SimpleStringProperty email;  
                private final BooleanProperty isSuperior;  
          
                public Boolean getIsSuperior() {  
                    return isSuperior.get();  
                }  
          
                public void setIsSuperior(Boolean isSuperior) {  
                    this.isSuperior.set(isSuperior);  
                }  
          
                public SimpleStringProperty nameProperty() {  
                    return name;  
                }  
          
                public BooleanProperty isSuperiorProperty() {  
                    return isSuperior;  
                }  
          
                public SimpleStringProperty emailProperty() {  
                    return email;  
                }  
          
                private Employee(String name, String email, Boolean isSuperior) {  
                    this.name = new SimpleStringProperty(name);  
                    this.email = new SimpleStringProperty(email);  
                    this.isSuperior = new SimpleBooleanProperty(isSuperior);  
                }  
          
                public String getName() {  
                    return name.get();  
                }  
          
                public void setName(String fName) {  
                    name.set(fName);  
                }  
          
                public String getEmail() {  
                    return email.get();  
                }  
          
                public void setEmail(String fName) {  
                    email.set(fName);  
                }  
          
            }  
        }  
    
  • Unknown
    edited Jul 9, 2016 8:14PM Answer ✓
    I'd like to implement a TreeTableView where cells within a column might be editable depending on other properties of the Object being displayed.
    
    In Swing I'd control this via isCellEditable() Method of TableModel.
    What is the intended Way of doing thin in JavaFX?
    

    Haven't worked with this but a simple web search for EXACTLY what you ask about 'javafx editable tree table cell' produces the Oracle API doc for TreeTableVIew.

    https://docs.oracle.com/javase/8/javafx/api/javafx/scene/control/TreeTableView.html

    Have you reviewed that API? It appears to have the info you need.

    Editing

    This control supports inline editing of values, and this section attempts to give an overview of the available APIs and how you should use them.
    
    Firstly, cell editing most commonly requires a different user interface than when a cell is not being edited. This is the responsibility of the Cell implementation being used. For TreeTableView, it is highly recommended that editing be per-TreeTableColumn, rather than per row, as more often than not you want users to edit each column value differently, and this approach allows for editors specific to each column. It is your choice whether the cell is permanently in an editing state (e.g. this is common for CheckBox cells), or to switch to a different UI when editing begins (e.g. when a double-click is received on a cell).
    
    To know when editing has been requested on a cell, simply override the Cell.startEdit() method, and update the cell text and graphic properties as appropriate (e.g. set the text to null and set the graphic to be a TextField). 
    
    Additionally, you should also override Cell.cancelEdit() to reset the UI back to its original visual state when the editing concludes. In both cases it is important that you also ensure that you call the super method to have the cell perform all duties it must do to enter or exit its editing mode.
    
    Once your cell is in an editing state, the next thing you are most probably interested in is how to commit or cancel the editing that is taking place. This is your responsibility as the cell factory provider. Your cell implementation will know when the editing is over, based on the user input (e.g. when the user presses the Enter or ESC keys on their keyboard). When this happens, it is your responsibility to call Cell.commitEdit(Object) or Cell.cancelEdit(), as appropriate.
    
    When you call Cell.commitEdit(Object) an event is fired to the TreeTableView, which you can observe by adding an EventHandler via TreeTableColumn.setOnEditCommit(javafx.event.EventHandler). Similarly, you can also observe edit events for edit start and edit cancel.
    
    By default the TreeTableColumn edit commit handler is non-null, with a default handler that attempts to overwrite the property value for the item in the currently-being-edited row. It is able to do this as the Cell.commitEdit(Object) method is passed in the new value, and this is passed along to the edit commit handler via the CellEditEvent that is fired. It is simply a matter of calling TreeTableColumn.CellEditEvent.getNewValue() to retrieve this value.  
    
    It is very important to note that if you call TreeTableColumn.setOnEditCommit(javafx.event.EventHandler) with your own EventHandler, then you will be removing the default handler. Unless you then handle the writeback to the property (or the relevant data source), nothing will happen. You can work around this by using the TableColumnBase.addEventHandler(javafx.event.EventType, javafx.event.EventHandler) method to add a TreeTableColumn.EDIT_COMMIT_EVENT EventType with your desired EventHandler as the second argument. Using this method, you will not replace the default implementation, but you will be notified when an edit commit has occurred.
    
    Hopefully this summary answers some of the commonly asked questions. Fortunately, JavaFX ships with a number of pre-built cell factories that handle all the editing requirements on your behalf. You can find these pre-built cell factories in the javafx.scene.control.cell package.
    
    TPD-Opitz
  • TPD-Opitz
    TPD-Opitz Member Posts: 2,465 Silver Trophy
    edited Jul 10, 2016 4:35PM

    Thanks for your reply,

    but whre ist the part that answers my question?

    bye

    TPD

  • TPD-Opitz
    TPD-Opitz Member Posts: 2,465 Silver Trophy
    edited Jul 10, 2016 4:40PM
    rp0428 wrote:
    Have you reviewed that API? It appears to have the info you need.
    

    Editing

    For TreeTableView, it is highly recommended that editing be per-TreeTableColumn, rather than per row, 
    
    

    The "per row" link has the Information needed.

    Thanks,

    bye

    TPD