Edit TreeView / TreeItem Programmatically

Edit # 2 : Since this looks like a bug, I already posted a bug report in javaFx-jira , you must have an account to be able to access the problem. I will keep this post up to date if there is new information.

Original post: I have a simple UI with a button and a TreeView. If the button is clicked, a new item should be added to the TreeView. This element should be editable as soon as it appears in the tree.

import javafx.fxml.FXML;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.control.cell.TextFieldTreeCell;
import javafx.util.converter.DefaultStringConverter;

public class ClientController {

    @FXML
    private TreeView<String> tree;

    public void initialize() {
        tree.setEditable(true);
        tree.setCellFactory(p -> new TextFieldTreeCell<>(new DefaultStringConverter()));

        TreeItem<String> root = new TreeItem<>();
        root.setValue("Items");
        root.setExpanded(true);

        tree.setRoot(root);
    }

    @FXML
    public void createItem() {
        TreeItem<String> newItem = new TreeItem<>();
        newItem.setValue("Item " + tree.getExpandedItemCount());

        tree.getRoot().getChildren().add(newItem);
        tree.requestFocus();
        tree.getSelectionModel().select(newItem);
        tree.edit(newItem);
    }
}

      

In CellFactory, I am using the JavaFX api part .

If I look at the api documentation ( TreeView # edit ) there isn't much work on my site.

Lots of examples I found via google like this create a context menu for each TreeItem. Helpful, but not exactly what I want right now.

If I select / double click on an item in the UI, I can edit any previously created and existing TreeItem. Did I miss something?

Edit # 1:

If the createItem method changes to the following code:

@FXML
public void createItem() throws InterruptedException {
    TreeItem<String> newItem = new TreeItem<>();
    newItem.setValue("Item " + this.tree.getExpandedItemCount());

    this.tree.getRoot().getChildren().add(newItem);
    this.tree.requestFocus();
    this.tree.getSelectionModel().select(newItem);
    Thread.sleep(100);
    Platform.runLater(new Runnable() {
        @Override
        public void run() {
            SimpleController.this.tree.edit(newItem);
        }
    });
}

      

each new element is correctly marked for editing (the selected text box and its contents are selected). Using Platform.runLater without Thread.sleep doesn't work, and Thread.sleep without runLater doesn't.

It just isn't true. What is my problem?

I provide a very small example (eclipse): https://www.dropbox.com/s/duos0ynw4rqp3yn/Test.zip?dl=0 containing the main method, FXML file and the "problematic" controller.

+3


source to share


2 answers


TreeCells (that is, their content and binding to treeItem) are updated lazily in the layout, which is very "delayed" the next time the pulse is fired. The .runLater (..) framework or any hardcoded delay can occur before or after this pulse, which is (probably) why both seem to work or not perversely.

So another bumpy hack is to manually force reformat on the tree after adding a new item and before programmatically starting editing:



root.getChildren().add(newItem);
tree.layout();
tree.edit(newItem);

      

Needless to say, this is not necessary - the current behavior is a serious bug and should be fixed right away (... meaning ... jdk 9)

+4


source


The treeView seems to need more time to do internal calculations before the newly added item gets reflected. To pinpoint exactly which computations are causing the problem, you have to dig in the source code and see what's going on under the hood.

A workaround is to force treeView to compute it immediately,

@FXML
public void createItem() throws InterruptedException {
    TreeItem<String> newItem = new TreeItem<>();
    newItem.setValue("Item " + tree.getExpandedItemCount());

    tree.getRoot().getChildren().add(newItem);

    // Trigger whatever calculations there internally
    TreeItem<String> r = tree.getRoot();
    tree.setRoot( null );
    tree.setRoot( r );

    tree.requestFocus();
    tree.getSelectionModel().select(newItem);
    tree.edit(newItem);
}

      

This workaround was found entirely intuitively based on other bugs in JavaFX; -)




Alternatively, since Platform.runlaters' delayed time is clearly not enough, you can use PauseTransition

to increase the required time:

PauseTransition p = new PauseTransition( Duration.millis( 100 ) );
p.setOnFinished( new EventHandler<ActionEvent>()
{
    @Override
    public void handle( ActionEvent event )
    {
        tree.edit( newItem );
    }
} );
p.play();

      

+5


source







All Articles