Search code examples
javajavafxtreeviewtreeviewitem

javafx how to disable one item in checkboxTreeItem?


I am new to JavaFX and I am having a problem in checkbox Tree View.
I want to disable one item inside root value **Serial No but as per JavaFX documentation, it seems that it is not possible.

I have disabled click effect cbTreeViewdisable(treeItems, 3)
Is there any way to grey out any one value (or set an id) so that user knows it is disabled.

private TreeView<String> cbTreeView;    
@Override
public void start(Stage primaryStage) {
 StackPane root = new StackPane();
 cbTreeView = new TreeView();
 CheckboxTreeItem<String> rootItem = new CheckboxTreeItem("Serial No.");
 final CheckboxTreeItem[] treeItems = new CheckboxTreeItem[6];
 for (int i = 0; i < 6; i++) {
   treeItems[i] = new CheckboxTreeItem(""+i+"");
   rootItem.getChildren().addAll(treeItems[i]);  
 }

 root.setExpanded(true);
 CheckboxTreeItem rootItem2 = new CheckboxTreeItem("child1");
 final CheckboxTreeItem[] treeItems2 = new CheckboxTreeItem[6];

 for (int i = 0; i < 6; i++) {
   treeItems2[i] = new CheckboxTreeItem(""+i+"");
   rootItem2.getChildren().addAll(treeItems2[i]);  
 }

 cbTreeView.setRoot(rootItem);
 cbTreeView.setCellFactory(CheckboxTreeCell.<String>forTreeView());
 rootItem.getChildren().set(2,rootItem2);
 cbTreeViewdisable(treeItems, 3);
 //rest of code imports css and draws stage which is not relevant to my question
 }

 private void cbTreeViewdisable(final CheckboxTreeItem[] treeItems, final int id) {
    treeItems[id].setIndependent(Boolean.TRUE);
    treeItems[id].setValue(treeItems[id].getValue().toString()+" Note: you can't select this!)");
    treeItems[id].selectedProperty().addListener(new ChangeListener<Boolean>() {

    @Override
    public void changed(ObservableValue<? extends Boolean> observable, 
        Boolean oldvalue, Boolean newvalue) {
        Platform.runLater(new Runnable() {
        @Override
        public void run() {
            treeItems[id].setSelected(Boolean.FALSE);       
   } 

Solution

  • One way to allow disabling single items of the tree is binding their cell disable property to an external property you can modify on runtime. For that, when we set the cell factory, we can add this binding to the cell implementation. But we need a reference, so we'll use the value of the item, for the sake of simplicity (this requires unique values).

    Let's use this pojo:

    class Wrap {
    
        public Wrap(String name){
            this.name.set(name);
            this.disabled.set(false);
        }
    
        private final StringProperty name = new SimpleStringProperty();
    
        public String getName() {
            return name.get();
        }
    
        public void setName(String value) {
            name.set(value);
        }
    
        public StringProperty nameProperty() {
            return name;
        }
        private final BooleanProperty disabled = new SimpleBooleanProperty();
    
        public boolean isDisabled() {
            return disabled.get();
        }
    
        public void setDisabled(boolean value) {
            disabled.set(value);
        }
    
        public BooleanProperty disabledProperty() {
            return disabled;
        }
    }
    

    so we can have one collection for all of the items on the tree view:

    private TreeView<String> cbTreeView; 
    private final List<Wrap> disableList = new ArrayList<>();
    
    @Override
    public void start(Stage primaryStage) {
        cbTreeView = new TreeView();
    
        CheckBoxTreeItem<String> rootItem = new CheckBoxTreeItem("Serial No.");
        final List<CheckBoxTreeItem<String>> treeItems = new ArrayList<>(6);
        for (int i = 0; i < 6; i++) {
            CheckBoxTreeItem<String> item = new CheckBoxTreeItem("0"+i+"");
            item.setIndependent(true);
            treeItems.add(item);  
            disableList.add(new Wrap("0"+i+""));
        }
        rootItem.getChildren().addAll(treeItems);
    
        rootItem.setExpanded(true);
        rootItem.setIndependent(true);
        CheckBoxTreeItem<String> rootItem2 = new CheckBoxTreeItem("child1");
        final List<CheckBoxTreeItem<String>> treeItems2 = new ArrayList<>(6);
        for (int i = 0; i < 6; i++) {
            CheckBoxTreeItem<String> item = new CheckBoxTreeItem("1"+i+"");
            item.setIndependent(true);
            treeItems2.add(item); 
            disableList.add(new Wrap("1"+i+""));
        }
        rootItem2.getChildren().addAll(treeItems2);
        rootItem2.setIndependent(true);
        rootItem.getChildren().set(2,rootItem2);
    
        cbTreeView.setRoot(rootItem);
    

    Now we create the cell factory, and provide the binding, when the value of the item is set:

        cbTreeView.setCellFactory((TreeView<String> item) -> {
    
            final CheckBoxTreeCell<String> cell = new CheckBoxTreeCell<>();
    
            cell.itemProperty().addListener((obs,s,s1)->{
                cell.disableProperty().unbind();
                if(s1!=null && !s1.isEmpty()){
                    Wrap wrap=disableList.stream()
                            .filter(w->w.getName().equals(s1))
                            .findFirst().orElse(null);
                    if(wrap!=null){
                        cell.disableProperty().bind(wrap.disabledProperty());
                    }
                }
            });
            return cell;
        });
    

    And finally, we show the stage, and disable some random items:

        Scene scene=new Scene(cbTreeView);
        primaryStage.setScene(scene);
        primaryStage.show();
    
        // disable by name
        disableList.stream()
                .filter(w->w.getName().equals("03"))
                .findFirst().ifPresent(w->w.setDisabled(true));
        // disable by order
        disableList.get(7).setDisabled(true);
    

    }

    TreeView with disabled items