TreeTableColumn.visible: Связанное значение не может быть установлено
Я делаю простое приложение JavaFX. В этом приложении есть древовидная таблица с 2 столбцами и флажок. Если флажок установлен, столбец 2 будет виден, в противном случае не виден. Для этого я привязал видимое свойство столбца таблицы дерева к флажку выбранного свойства. Когда я нажимаю флажок, состояние столбца меняется, но в то же время дает.
Причина: java.lang.RuntimeException: TreeTableColumn.visible: Связанное значение не может быть установлено.
Если я использую двунаправленную привязку, я не получаю эту ошибку. Но мне не нужно двунаправленное связывание. Это ошибка или я неправильно использую bind? Пожалуйста, используйте приведенный ниже код, чтобы воспроизвести эту ошибку. Я использую 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>
0 ответов
Я думаю, что это на самом деле не ошибка. Хотя это определенно таинственное и неожиданное поведение. Частично проблема заключается в том, что конфликт привязки происходит из TableHeaderRow, который создается скином во время отображения.
TableHeaderRow
отвечает за отображение всех заголовков столбцов и включает в себя эту встроенную кнопку для меню, которое по умолчанию представляет собой список выбора радиостанций для отображения / скрытия столбцов:
В результате TableHeaderRow
создает двунаправленную привязку между состоянием выбора этих пунктов меню и видимым свойством каждого столбца.
Это на самом деле можно отменить эту привязку, но я нашел это раздражает, так как TableHeaderRow
является null
до TableView
отображается. Но, адаптируя это решение для доступа к TableHeaderRow, мы можем сделать что-то вроде:
TableHeaderRow headerRow = ((TableViewSkinBase) tableView.getSkin()).getTableHeaderRow();
try {
// get columnPopupMenu field
Field privateContextMenuField = TableHeaderRow.class.getDeclaredField("columnPopupMenu");
// make field public
privateContextMenuField.setAccessible(true);
// get context menu
ContextMenu contextMenu = (ContextMenu) privateContextMenuField.get(headerRow);
for (MenuItem menuItem : contextMenu.getItems()) {
// Assuming these will be CheckMenuItems in the default implementation
BooleanProperty selectedProperty = ((CheckMenuItem) menuItem).selectedProperty();
// In theory these menu items are in parallel with the columns, but I just brute forced it to test
for (TableColumn<?, ?> tableColumn : tableView.getColumns()) {
// Unlink the column's visibility with the menu item
tableColumn.visibleProperty().unbindBidirectional(selectedProperty);
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
// Not strictly necessary but we don't want to be misleading :-p
tableView.setTableMenuButtonVisible(false);
Но вот что важно: вы должны делать это каждый раз, когда изменяется видимость столбца, потому что TableHeaderRow
имеет прослушиватель, который перестраивает меню и повторно связывает свойства, каждый раз, когда отображается столбец.
Конечно, вы можете найти способ отключить этого слушателя... но, очевидно, это уже смешно и, вероятно, не нужно.
Во всяком случае, как вы заметили:
Если я использую двунаправленную привязку, я не получаю эту ошибку. Но мне не нужно двунаправленное связывание.
Я думаю, что второе утверждение не является технически верным: ваш вариант использования не требует двунаправленной привязки, но на самом деле он уместен, поскольку есть другие свойства, уже связанные с видимостью столбца.
Итак, я бы использовал двунаправленную привязку.