I am trying to fetch back the TreeItem
s valueProperty
during an onMouseClick
event. I have a TreeView
of following type:
@FXML
private TreeView<Pair<URIImpl,String>> myTreeview;
The FXML as follows:
<TreeView fx:id="myTreeview" onMouseClicked="#myTreeview_Clicked"
prefHeight="551.0" prefWidth="166.0"
AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0"
AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
I would like to get back my model (Pair
instance) on click.
void myTreeview_Clicked(MouseEvent event) {
if(event.getTarget() instanceof ...) {
// Fetch Target
}
}
The target object in my case is Label
text which do not contain the encapsulated model reference, to the best of my knowledge. Could anyone please advise? This seems to be a very fundamental feature. A one-way binding...with some sort of reference. I am not looking to edit the TreeItem
s.
This thread seems similar to question: Model-Identifier for Node in JavaFX 2 TreeItem
Thanks
The key is to define a cell factory on the TreeView and in the cell factory add your mouse click event handler to the tree cell. A mouse click event handler so defined will have full access to the model object backing the tree cell and can do what it want with it.
In the example below, when the user clicks cells in the tree, click handlers extract information from the underlying model item related to the clicked cell and render that information into a label below the tree.
TreeApplication.java
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.*;
import javafx.stage.*;
public class TreeApplication extends Application {
@Override public void start(final Stage stage) throws Exception {
final FXMLLoader loader = new FXMLLoader(
getClass().getResource("treeInterface.fxml")
);
final Parent root = (Parent) loader.load();
stage.setScene(new Scene(root));
stage.show();
}
public static void main(String[] args) { launch(args); }
}
TreeController.java
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.*;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.util.Callback;
import javafx.util.Pair;
import java.net.URL;
import java.util.ResourceBundle;
public class TreeController implements Initializable {
@FXML private TreeView<Pair<URIImpl,String>> treeView;
@FXML private Label clickedPair;
@Override public void initialize(URL location, ResourceBundle resources) {
treeView.setCellFactory(new Callback<TreeView<Pair<URIImpl, String>>, TreeCell<Pair<URIImpl, String>>>() {
@Override public TreeCell<Pair<URIImpl, String>> call(TreeView<Pair<URIImpl, String>> treeView) {
return new TreeCell<Pair<URIImpl, String>>() {
final ImageView iconView = new ImageView();
@Override protected void updateItem(final Pair<URIImpl, String> pair, boolean empty) {
super.updateItem(pair, empty);
if (!empty && pair != null) {
setText(
pair.getValue()
);
setGraphic(
iconView
);
iconView.setImage(pair.getKey().getImage());
} else {
setText(null);
setGraphic(null);
}
setOnMouseClicked(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
clickedPair.setText(
"Key: " + pair.getKey() + " Value: " + pair.getValue()
);
}
});
}
};
}
});
loadTreeItems(
createPair("http://www.google.com", "google.com", "Google"),
createPair("http://www.microsoft.com", "microsoft.com", "Microsoft"),
createPair("http://www.yahoo.com", "yahoo.com", "Yahoo")
);
}
private Pair<URIImpl, String> createPair(String uri, String domain, String name) {
return new Pair<>(new URIImpl(uri, domain), name);
}
private void loadTreeItems(Pair<URIImpl,String>... rootItems) {
TreeItem<Pair<URIImpl,String>> root = new TreeItem(createPair("N/A", "", "Root Node"));
root.setExpanded(true);
for (Pair<URIImpl, String> pair: rootItems) {
root.getChildren().add(
new TreeItem<>(
pair
)
);
}
treeView.setRoot(root);
}
}
URIImpl.java
import javafx.scene.image.Image;
class URIImpl {
private final String uri;
private final String domain;
private Image image;
public URIImpl(String uri, String domain) {
this.uri = uri;
this.domain = domain;
}
public String getUri() {
return uri;
}
public String getDomain() {
return domain;
}
public Image getImage() {
if (image == null) {
image = new Image("https://plus.google.com/_/favicon?domain=" + getDomain());
}
return image;
}
@Override
public String toString() {
return "URIImpl{" + "uri->" + uri + '}';
}
}
treeInterface.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane id="AnchorPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" xmlns:fx="http://javafx.com/fxml" fx:controller="tests.treesample.TreeController">
<TreeView fx:id="treeView" layoutX="0" layoutY="0" prefHeight="193.0" prefWidth="471.0" />
<Label fx:id="clickedPair" layoutX="0" layoutY="200"/>
</AnchorPane>
Update
This worked just fine. However, the icon image seems to be lost once we setup the CallBack event handlers.
Yeah, looks as though if you create your own cell factory, you also need to set the graphic manually within the cell factory. This is because the default tree item cell factory will grab the graphic from the tree item if one has been set on the tree item, whereas this logic won't be present within your custom cell factory unless you code it there.
I updated the code in the sample solution to show how you can set a graphic on a tree cell generated by a custom cell factory. The updated code gets the graphic image from the model backing the tree cell, but it could equally have retrieved it from the graphic attribute of the tree item instead if that were preferred. To get the graphic from the TreeItem, use the following code as suggested by blacks0ul:
TreeItem<Pair<URIImpl, String>> item = getTreeItem();
if (item != null && item.getGraphic() != null) {
setGraphic(item.getGraphic());
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With