javafx: How do I hide the "drop down arrow" in a TreeView?

geekTechnique :

Hope everyone is doing well.

My question is pretty basic: How do I make the arrow at the root of a TreeView hidden, if it's possible? A good example of what I want to achieve can be seen in Windows Event Viewer.

enter image description here

While my javafx application looks like the image below

enter image description here

By default, that arrow next to vehicles is present.

I read the documentation here, but I only found .showRoot(), which doesn't achieve what I want. I would really appreciate any input, even if it's a cheat way to do it (using the x-offset property crossed my mind).

Slaw :

The arrow is part of the disclosureNode of the TreeCell. When not specified it is the responsibility of the TreeCell's skin to provide a default disclosure node (e.g. the triangle). This is stated by the property-setter documentation:

The node to use as the "disclosure" triangle, or toggle, used for expanding and collapsing items. This is only used in the case of an item in the tree which contains child items. If not specified, the TreeCell's Skin implementation is responsible for providing a default disclosure node.

Yet looking at the TreeCellSkin it appears it does not provide the default disclosure node. Instead, this seems to be handled by the TreeViewSkin (in both JavaFX 8 and JavaFX 11). Looking at the implementation the default disclosure node is a StackPane with a child StackPane that functions as the actual arrow. Their style class is tree-disclosure-node and arrow, respectively. Note that this doesn't appear documented anywhere, including the JavaFX CSS Reference Guide.

The easiest and least bug-prone method to hiding the root disclosure node is, in my opinion, to use CSS. The only problem here is that TreeCell provides no way to target only the root cell. But this can still be accomplished by subclassing TreeCell and providing our own PseudoClass. Then we set the cellFactory on our TreeView.

To set the graphic of the root set the TreeItem.graphic property of the root TreeItem.


CustomTreeCell

import javafx.beans.InvalidationListener;
import javafx.css.PseudoClass;
import javafx.scene.control.TreeCell;
import javafx.scene.control.TreeView;
import javafx.util.Callback;

public class CustomTreeCell<T> extends TreeCell<T> {

    private static final PseudoClass ROOT = PseudoClass.getPseudoClass("root");

    public static <T> Callback<TreeView<T>, TreeCell<T>> forTreeView() {
        return treeView -> new CustomTreeCell<>();
    }

    public CustomTreeCell() {
        getStyleClass().add("custom-tree-cell");

        InvalidationListener listener = observable -> {
            boolean isRoot = getTreeView() != null && getTreeItem() == getTreeView().getRoot();
            pseudoClassStateChanged(ROOT, isRoot);
        };

        treeViewProperty().addListener(listener);
        treeItemProperty().addListener(listener);
    }

    @Override
    protected void updateItem(T item, boolean empty) {
        super.updateItem(item, empty);
        if (empty || item == null) {
            setText(null);
            graphicProperty().unbind();
            setGraphic(null);
        } else {
            setText(item.toString()); // Really only works if item is a String. Change as needed.
            graphicProperty().bind(getTreeItem().graphicProperty());
        }
    }

}

CSS File

.custom-tree-cell:root .tree-disclosure-node,
.custom-tree-cell:root .arrow {

    -fx-min-width: 0;
    -fx-pref-width: 0;
    -fx-max-width: 0;

    -fx-min-height: 0;
    -fx-pref-height: 0;
    -fx-max-height: 0;

}

/* Related to question asked in the comments by OP */
.custom-tree-cell > .tree-disclosure-node > .arrow {
    -fx-shape: "M 0 0 L 10 5 L 0 10 L 0 8 L 8 5 L 0 2 Z";
}

Some notes:

  1. Since I bound the TreeCell.graphic property to the TreeItem.graphic property in my cell implementation you won't be able to set the graphic from CSS. You can modify this to simply set the graphic, rather than bind, in order to enable that functionality. Then you won't have to set the graphic of the root TreeItem through code but could do .custom-tree-cell:root { -fx-graphic: ...; }.
  2. This doesn't remove the disclosure node, it just makes it have no width and no height.
  3. This solution relies on implementation details; be careful when changing versions of JavaFX. I only tried this on JavaFX 11.0.2 but I believe it should work for JavaFX 8 as well.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=97379&siteId=1