1

I know that TreeCell objects are generated dynamically by the TreeView using a cell factory.

Is there a way to get a list of all TreeCell objects that currently exist?

I suppose I could keep track of them by modifying the cell factory. As in, whenever I create a new cell add it to some list. But then I'm not sure how to remove cells from my list once the TreeView disposes them (because they went out of view).

Saturn
  • 17,888
  • 49
  • 145
  • 271
  • In short, no, there isn't (at least, not with the public API). Why do you think you want to do this? (Note that they typically get reused, rather than disposed, when they go out of view, but there is no guarantee of either.) – James_D Sep 01 '15 at 15:06
  • @James_D sometimes, my application needs to change the style of all the cells in my TreeView (font/color/etc) depending on a number of factors. This is no problem for newly generated cells, because I can generate them with the needed styles, but for old already-existing cells, they will retain the style they had when they were created and it needs to be updated somehow. – Saturn Sep 01 '15 at 15:13
  • 1
    So your question is really: "how do I dynamically update the style of a `TreeCell` when properties change after the cell is displayed?". Keeping a list of cells is not the way to do it: you should have the cell factory generate cells that observe the appropriate properties. I recommend you edit your question to ask this instead... it would probably help to add some code. – James_D Sep 01 '15 at 15:25
  • Actually: if you're changing the style of *all* the cells, it might be as easy as just changing the style class on the `TreeView` itself, and then using your CSS file to apply styles to the cells that are child nodes of different classes of `TreeView`. But you need to explain what you're really trying to do. – James_D Sep 01 '15 at 15:27

2 Answers2

2

My solution to this is to use weak references:

[A] weak reference is a reference that does not protect the referenced object from collection by a garbage collector[.]

So, add this to your controller:

private final Set<MyTreeCell> myTreeCells = Collections.newSetFromMap(new WeakHashMap<>());

And make your CellFactory look like this:

myTreeView.setCellFactory((treeItem) -> {
        MyTreeCell c = new MyTreeCell(icf);
        Platform.runLater(() -> myTreeCells.add(c));
        return c;
    });

Now, myTreeCells will always contain the currently existing TreeCells.

Update

There is another, quite ugly solution using reflection to get a List of TreeCells. Note that this List is – as far as I know – a snapshot of JavaFXs List of TreeCells at one point in time. It is not backed.

@SuppressWarnings({ "unchecked" })
private Set<MyTreeCell> getListOfTreeCells() {
    try {
        final Field f = VirtualContainerBase.class.getDeclaredField("flow");
        f.setAccessible(true);
        final Field g = VirtualFlow.class.getDeclaredField("cells");
        g.setAccessible(true);
        final Set<MyTreeCell> s = new HashSet<>();
        s.addAll((ArrayLinkedList<MyTreeCell>) g
                .get((f.get((myTreeView.skinProperty().get())))));
        return s;
    }
    catch (NoSuchFieldException | SecurityException | IllegalArgumentException
            | IllegalAccessException e) {
        e.printStackTrace();
    }
    return null;
}
spilot
  • 625
  • 1
  • 7
  • 20
  • 1
    Hahaha dude i love the reflection example xD I feel like javafx forces you to do something like this because of poor design choices on their side. The fact that its so difficult just to style the tree items is really frustrating. – Jp Silver Dec 04 '20 at 08:50
1
treeView.lookupAll(".tree-cell");

This will return a Set of Nodes that can be cast to TreeCells.