I make a simple JavaFX application. In this application there is a treetable with 2 columns and a check box. If check box is selected column 2 will be visible, otherwise not visible. To do this I bound tree table column visible property to checkbox selected property. When I click the check box column state change but at the same time gives.
Caused by: java.lang.RuntimeException: TreeTableColumn.visible : A bound value cannot be set.
If I use bidirectional binding I don't get this error. But I don't need bidirectional binding. Is it a bug or I don't use bind correctly? Please use below code to reproduce this error. I use jdk1.8.0_111.
JavaFXApplication.java
package test;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class JavaFXApplication extends Application {
@Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
FXMLDocumentController.java
package test;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableView;
public class FXMLDocumentController implements Initializable {
@FXML
private TreeTableView<?> table;
@FXML
private CheckBox box;
@FXML
private TreeTableColumn<?, ?> c2;
@Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
c2.visibleProperty().bind(box.selectedProperty());
}
}
FXMLDocument.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.CheckBox?>
<?import javafx.scene.control.TreeTableColumn?>
<?import javafx.scene.control.TreeTableView?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane id="AnchorPane" prefHeight="390.0" prefWidth="452.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.65" fx:controller="javafxapplication2.FXMLDocumentController">
<children>
<TreeTableView fx:id="table" layoutX="2.0" prefHeight="390.0" prefWidth="149.0">
<columns>
<TreeTableColumn prefWidth="75.0" text="C1" />
<TreeTableColumn fx:id="c2" prefWidth="75.0" text="C2" visible="false" />
</columns>
</TreeTableView>
<CheckBox fx:id="box" layoutX="234.0" layoutY="77.0" mnemonicParsing="false" text="CheckBox" />
</children>
</AnchorPane>
I think this actually is not a bug. It's definitely mysterious and unexpected behavior though. Part of the problem is that the binding conflict is coming from TableHeaderRow, which is created by the skin at display time.
The
TableHeaderRowis responsible for rendering all the column headers, and it includes this built-in button for a menu that, by default, is a radio selection list of columns to show/hide:As a result, the
TableHeaderRowcreates a bidirectional binding between these menu entries' selection state and each column's visible property.It actually is possible to undo this binding but I found it annoying since the
TableHeaderRowisnulluntil theTableViewis displayed. But, adapting this solution to access the TableHeaderRow, we can do something like:But here's the kicker: you have to do this every time a column's visibility changes, because the
TableHeaderRowhas a listener that rebuilds the menu, and re-links the properties, every time a column is shown.You could, of course, find a way to disable that listener.. but clearly this is already ridiculous and probably unnecessary.
Anyway, as you noted:
I think the second statement isn't technically true: your use case doesn't require bidirectional binding, but it is actually appropriate here since there are other properties already linked with a column's visibility.
So, I would use bidirectional binding.