I'm trying to set up a checkbox tree in JavaFX where the leaves are bound to their parents:
- they are selected when the parent is selected
- cannot be changed individually.
I did it using a binding with the selectedProperty
.
It works just fine, but it throws an exception each time I select or deselect the parent.
Is there an easy way around this? I hate to abandon an approach that works except for those nasty exceptions.
The relevant code is below. Please forgive any java convention errors - I'm still new to the language.
Controller:
package application;
import java.util.ArrayList;
import java.util.List;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.fxml.FXML;
import javafx.scene.control.CheckBoxTreeItem;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TreeView;
import javafx.scene.control.cell.CheckBoxTreeCell;
public class BindingTestController {
@FXML
private TreeView<String> MainTree;
static List<CheckBoxTreeItem<String>> treeItems = new ArrayList<CheckBoxTreeItem<String>>();
CheckBoxTreeItem<String> root = new CheckBoxTreeItem<String>("Root");
CheckBoxTreeItem<String> parent1 = new CheckBoxTreeItem<String>("Parent1");
CheckBoxTreeItem<String> parent2 = new CheckBoxTreeItem<String>("Parent2");
private void AddColumns(CheckBoxTreeItem<String> item){
for (int i = 1 ; i < 5 ; i++){
CheckBoxTreeItem<String> itemColumn = new CheckBoxTreeItem<String>(item.getValue()+"_Column_"+i);
treeItems.add(itemColumn);
item.getChildren().add(itemColumn);
itemColumn.selectedProperty().bind(item.selectedProperty());
}
}
@FXML // This method is called by the FXMLLoader when initialization is complete
private void initialize() {
treeItems.add(root);
treeItems.add(parent1);
root.getChildren().add(parent1);
treeItems.add(parent2);
root.getChildren().add(parent2);
AddColumns(parent1);
AddColumns(parent2);
MainTree.setRoot(root);
MainTree.setShowRoot(true);
root.setExpanded(true);
MainTree.setCellFactory(p-> new CheckBoxTreeCell());
MainTree.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
MainTree.getSelectionModel().selectedItemProperty().addListener(new ChangeListener() {
@Override
public void changed(ObservableValue observable, Object oldValue, Object newValue) {
CheckBoxTreeItem<String> treeItem = (CheckBoxTreeItem)newValue;
}
});
}
}
Here is the FXML:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.TreeView?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.VBox?>
<BorderPane xmlns="http://javafx.com/javafx/8.0.65" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.BindingTestController">
<center>
<VBox prefHeight="1049.0" prefWidth="714.0"BorderPane.alignment="CENTER">
<children>
<TreeView fx:id="MainTree" prefHeight="624.0" prefWidth="714.0"/>
</children>
</VBox>
</center>
</BorderPane>
and here is the main:
package application;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
public class Main extends Application {
@Override
public void start(Stage primaryStage) {
try {
// BorderPane root = new BorderPane();
BorderPane root = (BorderPane) FXMLLoader.load(Main.class.getResource("BindingTest.fxml"));
Scene scene = new Scene(root,400,400);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
Any advice would be appreciated.