简体   繁体   English

JavaFX CheckboxTreeItem:不可选择的叶子

[英]JavaFX CheckboxTreeItem: unselectable leaves

I'm trying to set up a checkbox tree in JavaFX where the leaves are bound to their parents: 我试图在JavaFX中建立一个复选框树,其中叶子绑定到它们的父母:

  • they are selected when the parent is selected 当父母被选中时,他们被选中
  • cannot be changed individually. 不能单独更改。

I did it using a binding with the selectedProperty . 我使用与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. 请原谅任何Java约定错误-我仍然是该语言的新手。

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: 这是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. 任何意见,将不胜感激。

The problem is coming from this line 问题来自这条线

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

To understand why do you get an exception whatever node you select, you should take a look on the independentProperty of CheckBoxTreeItem : 要理解为什么你会得到一个异常的任何节点选择,你应该拿上一看independentPropertyCheckBoxTreeItem

A CheckBoxTreeItem can be independent or dependent. CheckBoxTreeItem可以是独立的或从属的。 By default, CheckBoxTreeItem instances are dependent, which means that any changes to the selection state of a TreeItem will have an impact on parent and children CheckBoxTreeItem instances. 默认情况下,CheckBoxTreeItem实例是依赖的,这意味着对TreeItem的选择状态所做的任何更改都会对父级和子级CheckBoxTreeItem实例产生影响。 If a CheckBoxTreeItem is set to be independent, this means that any changes to that CheckBoxTreeItem will not directly impact the state of parent and children CheckBoxTreeItem instances. 如果将CheckBoxTreeItem设置为独立,则意味着对该CheckBoxTreeItem所做的任何更改都不会直接影响父级和子级CheckBoxTreeItem实例的状态。

This means, when you select/deselect a node in the tree, which has children, it will select/deselect all of it's children by default (and also sets the checked, unchecked, indeterminate state of its parent, but this is not important in your case). 这意味着,当您选择/取消选择树中具有子节点的节点时,默认情况下它将选择/取消选择其所有子节点(并且还设置了其父节点的选中,未选中,不确定状态,但这在您的情况)。 This is one of the functionality what you need and you get it out of the box. 这是您需要的功能之一,您可以立即使用它。

Now you can understand why your binding is problematic: You bind the selectedProperty() of the child nodes to the selectedProperty() of the parent node, then: 现在,你可以理解为什么你的结合是有问题的:你的绑定selectedProperty()子节点的selectedProperty()的父节点,则:

  • if you check one of the leaf nodes, you will get an exception, because the value is bound, and bound values cannot be set. 如果您检查叶节点之一,则会得到异常,因为该值是绑定的,并且绑定的值无法设置。

  • if you check one of the parent nodes, it will try to select all of its children (because of being dependent), then you will get an exception again. 如果您检查父节点之一,它将尝试选择其所有子节点(由于被依赖),那么您将再次获得异常。

Solution

You have two requirements: 您有两个要求:

  • select all of the children nodes, when the parent is selected, which is already done 选择所有父节点后,选择所有子节点,这已经完成

  • do not allow to select child nodes manually 不允许手动选择子节点

For the second requirement, you can disable the checkbox of these items. 对于第二个要求,您可以禁用这些项目的复选框。 For this you can use the setCellFactory method of your TreeView : 为此,您可以使用TreeViewsetCellFactory方法:

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

This snippet will bind the disableProperty of the displayed CheckBoxTreeCell objects to the leafProperty of the corresponding CheckBoxTreeItem . 该代码段将结合disableProperty所显示的CheckBoxTreeCell对象到leafProperty相应的CheckBoxTreeItem

public final ReadOnlyBooleanProperty leafProperty() 公共最终ReadOnlyBooleanProperty leafProperty()

Represents the TreeItem leaf property, which is true if the TreeItem has no children. 表示TreeItem叶子属性,如果TreeItem没有子级,则为true。

Which results in a tree where all the nodes that has no children are disabled. 这将导致一棵树,其中所有没有子节点的节点都被禁用。

在此处输入图片说明

To make this working you should remove this line from your code: 为了使它起作用,您应该从代码中删除以下行:

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

and you should replace this line 你应该替换这行

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

with the code posted above. 上面发布的代码。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM