javafx how to disable one item in checkboxTreeItem?


Question

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);       
   } 
1
1
11/25/2014 7:57:28 AM

Accepted Answer

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

0
11/25/2014 11:45:39 AM

Extended TreeItem class with bindable Boolean

   import javafx.beans.property.SimpleBooleanProperty;
   import javafx.scene.control.TreeItem;

    public class TreeItemDeactivatable<T> extends TreeItem<T> {
        private SimpleBooleanProperty disabledProperty = new SimpleBooleanProperty(false);

        public TreeItemDeactivatable() {
            super();
        }

        public TreeItemDeactivatable(T t) {
            super(t);
        }

        public SimpleBooleanProperty disabledProperty() {
            return disabledProperty;
        }

        public boolean isDisabled() {
            return disabledProperty.get();
        }

        public void setDisabled(boolean disabled) {
            disabledProperty.set(disabled);
        }
    }

Override call() in CellFactory and put

    @Override
    public TreeCell<T> call(TreeView<T> treeView) {
    TreeCell<T> cell = (TreeCell<T>) new TextFieldTreeCellImpl();

        final PseudoClass clippedStylePseudoClass = PseudoClass.getPseudoClass("clipped");

        cell.treeItemProperty().addListener((ov, oldTreeItem, newTreeItem) -> {
            if (newTreeItem != null && newTreeItem instanceof TreeItemDeactivatable) {
                TreeItemDeactivatable<T> tid = (TreeItemDeactivatable<T>) newTreeItem;
                 tid.setDisabled(false);
                 cell.disableProperty().unbind();
                 cell.disableProperty().bind(tid.disabledProperty());

cell.pseudoClassStateChanged(clippedStylePseudoClass,cell.isDisabled());
            }
        });
}

Use PseudoClass in your CSS setup

.tree-view .tree-cell:clipped {
    -fx-background-color:   color-clipped;
}

Line "cell.pseudoClassStateChanged()" is just in case you need an independent style otherwise you can also use the default pseudo class

.tree-view .tree-cell:disabled {}

how i control the disable state in my controller

@FXML
    private void handleCut() {
        Object selection = getFocusedSelectedItem();

        if (selection != null) {
            this.clipboard.add(selection);

             if (selection instanceof NodeDataModel) {
                NodeDataModel toPaste = (NodeDataModel) selection;
                TreeItemDeactivatable<NodeDataModel> tid = toPaste.getTreeItem();
                tid.setDisabled(true);
            }
        }
    }

Licensed under: CC-BY-SA with attribution
Not affiliated with: Stack Overflow
Icon