JavaFX CheckboxTreeItem:不可选择的叶子

时间:2016-07-08 14:15:13

标签: java javafx binding checkboxtree

我正在尝试在JavaFX中设置一个复选框树,其中树叶与父母绑定:

  • 选择父级时选择它们
  • 无法单独更改。

我是使用与selectedProperty的绑定来完成的。

它工作正常,但每次选择或取消选择父级时都会抛出异常。

有一个简单的方法吗?我讨厌放弃一种有效的方法,除了那些令人讨厌的例外情况。

相关代码如下。请原谅任何java约定错误 - 我还是该语言的新手。

控制器:

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;

            }
        });


}


}

以下是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>

,这是主要的:

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);
    }
}

任何建议都将不胜感激。

1 个答案:

答案 0 :(得分:2)

问题来自这一行

itemColumn.selectedProperty().bind(item.selectedProperty());

要了解为什么在选择的节点中出现异常,您应该查看CheckBoxTreeItem的{​​{3}}:

  

CheckBoxTreeItem可以是独立的或依赖的。 默认情况下,   CheckBoxTreeItem实例是依赖的,这意味着任何更改   到TreeItem的选择状态将对父级和   子CheckBoxTreeItem实例。如果CheckBoxTreeItem设置为   独立,这意味着对CheckBoxTreeItem的任何更改   不会直接影响父母和子女的状况   CheckBoxTreeItem实例。

这意味着,当您选择/取消选择具有子项的树中的节点时,它将选择/取消选择其所有子项默认(并设置已选中,未经检查,其父母的不确定状态,但这在您的情况下并不重要)。这是您需要的功能之一,您可以开箱即用。

现在您可以理解为什么绑定存在问题: 您将子节点的selectedProperty()绑定到父节点的selectedProperty(),然后:

  • 如果您检查其中一个叶节点,您将收到异常,因为该值已绑定,并且无法设置绑定值。

  • 如果您检查其中一个父节点,它将尝试选择其所有子节点(因为依赖),那么您将再次获得异常。

<强>解决方案

您有两个要求:

  • 选择所有子节点,当选择父节点时,已经完成

  • 不允许手动选择子节点

对于第二个要求,您可以禁用这些项目的复选框。为此,您可以使用TreeView的{​​{3}}方法:

MainTree.setCellFactory((item) -> {
    return new CheckBoxTreeCell<String>(){

        @Override
        public void updateItem(String item, boolean empty) {
            super.updateItem(item, empty);

            if(item != null) {
                this.disableProperty().unbind();
                CheckBoxTreeItem<String> value = (CheckBoxTreeItem<String>) treeItemProperty().getValue();
                this.disableProperty().bind(value.leafProperty());
            }
        }
    };
});

此代码段会将显示的disableProperty个对象的CheckBoxTreeCell绑定到相应CheckBoxTreeItem的{​​{3}}。

  

public final ReadOnlyBooleanProperty leafProperty()

     

表示TreeItem叶属性,如果TreeItem为true   没有孩子。

这会导致树中所有没有子节点的节点都被禁用。

independentProperty

要使其正常工作,您应该从代码中删除此行:

itemColumn.selectedProperty().bind(item.selectedProperty());

你应该替换这一行

MainTree.setCellFactory(p-> new CheckBoxTreeCell()); 

上面张贴的代码。