Search code examples
javafxjavafx-2javafx-8

Context menu for JavaFX tree


I want to create context menu for JavaFX. This is the code that I tested. But for some reason there is no context menu when I right-click on the tree node. Can you help me to find my mistake.

import java.util.Arrays;
import java.util.List;
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.ContextMenuBuilder;
import javafx.scene.control.MenuItemBuilder;
import javafx.scene.control.TreeCell;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.control.cell.TextFieldTreeCell;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.util.Callback;

public class MainApp extends Application
{
    List<Employee> employees = Arrays.<Employee>asList(
        new Employee("New Chassi", "New Datacenter"),
        new Employee("New Battery", "New Rack"),
        new Employee("New Chassi", "New Server"),
        new Employee("Anna Black", "Sales Department"),
        new Employee("Rodger York", "Sales Department"),
        new Employee("Susan Collins", "Sales Department"),
        new Employee("Mike Graham", "IT Support"),
        new Employee("Judy Mayer", "IT Support"),
        new Employee("Gregory Smith", "IT Support"),
        new Employee("Jacob Smith", "Accounts Department"),
        new Employee("Isabella Johnson", "Accounts Department"));
    TreeItem<String> rootNode = new TreeItem<>("MyCompany Human Resources");

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

    TreeView<String> treeView = new TreeView<>(rootNode);

    @Override
    public void start(Stage stage)
    {

        rootNode.setExpanded(true);
        for (Employee employee : employees)
        {
            TreeItem<String> empLeaf = new TreeItem<>(employee.getName());
            boolean found = false;
            for (TreeItem<String> depNode : rootNode.getChildren())
            {
                if (depNode.getValue().contentEquals(employee.getDepartment()))
                {
                    depNode.getChildren().add(empLeaf);
                    found = true;
                    break;
                }
            }
            if (!found)
            {
                TreeItem<String> depNode = new TreeItem<>(
                    employee.getDepartment()//,new ImageView(depIcon)   // Set picture
                );
                rootNode.getChildren().add(depNode);
                depNode.getChildren().add(empLeaf);
            }
        }

        stage.setTitle("Tree View Sample");
        VBox box = new VBox();
        final Scene scene = new Scene(box, 400, 300);
        scene.setFill(Color.LIGHTGRAY);

        treeView.setCellFactory(new Callback<TreeView<String>, TreeCell<String>>()
        {

            @Override
            public TreeCell<String> call(TreeView<String> arg0)
            {
                // custom tree cell that defines a context menu for the root tree item
                return new MyTreeCell();
            }
        });

        box.getChildren().add(treeView);
        stage.setScene(scene);
        stage.show();
    }

    public static class Employee
    {

        private final SimpleStringProperty name;
        private final SimpleStringProperty department;

        private Employee(String name, String department)
        {
            this.name = new SimpleStringProperty(name);
            this.department = new SimpleStringProperty(department);
        }

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

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

        public String getDepartment()
        {
            return department.get();
        }

        public void setDepartment(String fName)
        {
            department.set(fName);
        }
    }

    class MyTreeCell extends TextFieldTreeCell<String>
    {
        private ContextMenu rootContextMenu;

        public MyTreeCell()
        {
            // instantiate the root context menu
            rootContextMenu
                = ContextMenuBuilder.create()
                .items(
                    MenuItemBuilder.create()
                    .text("Menu Item")
                    .onAction(
                        new EventHandler<ActionEvent>()
                        {
                            @Override
                            public void handle(ActionEvent arg0)
                            {
                                System.out.println("Menu Item Clicked!");
                            }
                        }
                    )
                    .build()
                )
                .build();
        }

        @Override
        public void updateItem(String item, boolean empty)
        {
            super.updateItem(item, empty);

            // if the item is not empty and is a root...
            if (!empty && getTreeItem().getParent() == null)
            {
                setContextMenu(rootContextMenu);
            }
        }
    }

}

Solution

  • You should assign the context Menu to the TreeView instead of assigning it to the cell factory. Here is the code with the context menu working:

    import java.util.Arrays;
    import java.util.List;
    import javafx.application.Application;
    import javafx.beans.property.SimpleStringProperty;
    import javafx.event.ActionEvent;
    import javafx.event.EventHandler;
    import javafx.scene.Scene;
    import javafx.scene.control.ContextMenu;
    import javafx.scene.control.ContextMenuBuilder;
    import javafx.scene.control.MenuItemBuilder;
    import javafx.scene.control.TreeCell;
    import javafx.scene.control.TreeItem;
    import javafx.scene.control.TreeView;
    import javafx.scene.control.cell.TextFieldTreeCell;
    import javafx.scene.layout.VBox;
    import javafx.scene.paint.Color;
    import javafx.stage.Stage;
    import javafx.util.Callback;
    
    public class Test extends Application
    {
        List<Employee> employees = Arrays.<Employee>asList(
            new Employee("New Chassi", "New Datacenter"),
            new Employee("New Battery", "New Rack"),
            new Employee("New Chassi", "New Server"),
            new Employee("Anna Black", "Sales Department"),
            new Employee("Rodger York", "Sales Department"),
            new Employee("Susan Collins", "Sales Department"),
            new Employee("Mike Graham", "IT Support"),
            new Employee("Judy Mayer", "IT Support"),
            new Employee("Gregory Smith", "IT Support"),
            new Employee("Jacob Smith", "Accounts Department"),
            new Employee("Isabella Johnson", "Accounts Department"));
        TreeItem<String> rootNode = new TreeItem<>("MyCompany Human Resources");
    
        public static void main(String[] args)
        {
            Application.launch(args);
        }
    
        TreeView<String> treeView = new TreeView<>(rootNode);
    
        @Override
        public void start(Stage stage)
        {
    
            // instantiate the root context menu
            ContextMenu rootContextMenu
                = ContextMenuBuilder.create()
                .items(
                    MenuItemBuilder.create()
                    .text("Menu Item")
                    .onAction(
                        new EventHandler<ActionEvent>()
                        {
                            @Override
                            public void handle(ActionEvent arg0)
                            {
                                System.out.println("Menu Item Clicked!");
                            }
                        }
                    )
                    .build()
                )
                .build();
    
            treeView.setContextMenu(rootContextMenu);
    
            rootNode.setExpanded(true);
            for (Employee employee : employees)
            {
                TreeItem<String> empLeaf = new TreeItem<>(employee.getName());
                boolean found = false;
                for (TreeItem<String> depNode : rootNode.getChildren())
                {
                    if (depNode.getValue().contentEquals(employee.getDepartment()))
                    {
                        depNode.getChildren().add(empLeaf);
                        found = true;
                        break;
                    }
                }
                if (!found)
                {
                    TreeItem<String> depNode = new TreeItem<>(
                        employee.getDepartment()//,new ImageView(depIcon)   // Set picture
                    );
                    rootNode.getChildren().add(depNode);
                    depNode.getChildren().add(empLeaf);
                }
            }
    
            stage.setTitle("Tree View Sample");
            VBox box = new VBox();
            final Scene scene = new Scene(box, 400, 300);
            scene.setFill(Color.LIGHTGRAY);
    
    
            box.getChildren().add(treeView);
            stage.setScene(scene);
            stage.show();
        }
    
        public static class Employee
        {
    
            private final SimpleStringProperty name;
            private final SimpleStringProperty department;
    
            private Employee(String name, String department)
            {
                this.name = new SimpleStringProperty(name);
                this.department = new SimpleStringProperty(department);
            }
    
            public String getName()
            {
                return name.get();
            }
    
            public void setName(String fName)
            {
                name.set(fName);
            }
    
            public String getDepartment()
            {
                return department.get();
            }
    
            public void setDepartment(String fName)
            {
                department.set(fName);
            }
        }
    
    
    }