This discussion is archived
4 Replies Latest reply: Dec 13, 2012 1:07 PM by 970765 RSS

TreeView with mixed cell types

970765 Newbie
Currently Being Moderated
I would like to create a TreeView where some cells are CheckBoxTreeCells and
some aren't. My application domain is a medical search engine where we'd like
to have researcher query a dataset by selecting one or more values. Here's a
simple example:
public class CheckBoxTreeSample extends Application {
    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) {
        primaryStage.setTitle("CheckBoxTree Attempt");
        TreeItem<String> rootItem = new TreeItem<>("Dataset root");
        rootItem.setExpanded(true);

        final TreeView tree = new TreeView(rootItem);
        final TreeItem<String> testResultsItem = new TreeItem<>("Test Results");
        rootItem.getChildren().add(testResultsItem);
        testResultsItem.setExpanded(true);

        final CheckBoxTreeItem<String> normalTreeItem = new CheckBoxTreeItem<>("Normal");
        testResultsItem.getChildren().add(normalTreeItem);
        final CheckBoxTreeItem<String> abnormalTreeItem = new CheckBoxTreeItem<>("Abnormal");
        testResultsItem.getChildren().add(abnormalTreeItem);
        final CheckBoxTreeItem<String> preTreeItem = new CheckBoxTreeItem<>("PreCancer");
        testResultsItem.getChildren().add(preTreeItem);
        final CheckBoxTreeItem<String> cancerTreeItem = new CheckBoxTreeItem<>("Cancer");
        testResultsItem.getChildren().add(cancerTreeItem);

        tree.setCellFactory(CheckBoxTreeCell.<String>forTreeView()); 
                       
        tree.setRoot(rootItem);
        tree.setShowRoot(true);

        StackPane root = new StackPane();
        root.getChildren().add(tree);
        primaryStage.setScene(new Scene(root, 300, 250));
        primaryStage.show();
    }
}
So I'd like to have the testResultsItem and the rootItem NOT have checkboxes (which is why they are TreeItems), but have the leaf nodes be independent
CheckBoxTreeItems. I tried this alternate factory, but that only seems to control the Boolean Property:
        tree.setCellFactory(CheckBoxTreeCell.<String>forTreeView(new Callback<TreeItem<String>, ObservableValue<Boolean>>() {
          @Override
          public ObservableValue<Boolean> call (TreeItem<String> item) {
            if (item instanceof CheckBoxTreeItem) {
              return new ReadOnlyBooleanWrapper(Boolean.TRUE);
            } else {
              return null; //Don't display an CheckBox - obviously doesn't work...
            }
          }
        }));
I recently saw this posting by Jonathan Giles:
http://fxexperience.com/2012/05/listview-custom-cell-factories-and-context-menus/
but I'm not sure how that might apply to this case.

Any help in determining how to remove, or at least disable, non-leaf node checkboxes would be greatly
appreciated!
  • 1. Re: TreeView with mixed cell types
    James_D Guru
    Currently Being Moderated
    Something like this:
    public class CheckBoxTreeSample extends Application {
      public static void main(String[] args) {
        launch(args);
      }
    
      @Override
      public void start(Stage primaryStage) {
        primaryStage.setTitle("CheckBoxTree Attempt");
        TreeItem<String> rootItem = new TreeItem<String>("Dataset root");
        rootItem.setExpanded(true);
    
        final TreeView<String> tree = new TreeView<String>(rootItem);
        final TreeItem<String> testResultsItem = new TreeItem<String>(
            "Test Results");
        rootItem.getChildren().add(testResultsItem);
        testResultsItem.setExpanded(true);
    
        final CheckBoxTreeItem<String> normalTreeItem = new CheckBoxTreeItem<String>(
            "Normal");
        testResultsItem.getChildren().add(normalTreeItem);
        final CheckBoxTreeItem<String> abnormalTreeItem = new CheckBoxTreeItem<String>(
            "Abnormal");
        testResultsItem.getChildren().add(abnormalTreeItem);
        final CheckBoxTreeItem<String> preTreeItem = new CheckBoxTreeItem<String>(
            "PreCancer");
        testResultsItem.getChildren().add(preTreeItem);
        final CheckBoxTreeItem<String> cancerTreeItem = new CheckBoxTreeItem<String>(
            "Cancer");
        testResultsItem.getChildren().add(cancerTreeItem);
    
        // tree.setCellFactory(CheckBoxTreeCell.<String>forTreeView());
        tree.setCellFactory(new Callback<TreeView<String>, TreeCell<String>>() {
    
          @Override
          public TreeCell<String> call(TreeView<String> param) {
            return new TreeCell<String>() {
              @Override
              public void updateItem(String item, boolean empty) {
                super.updateItem(item, empty);
                if (empty) {
                  setText(null);
                  setGraphic(null);
                } else {
                  TreeItem<String> treeItem = getTreeItem();
                  if (treeItem instanceof CheckBoxTreeItem) {
                    CheckBoxTreeItem<String> cbTreeItem = (CheckBoxTreeItem<String>) treeItem;
                    setText(item.toString());
                    CheckBox cb = new CheckBox();
                    cb.indeterminateProperty().bindBidirectional(
                        cbTreeItem.indeterminateProperty());
                    cb.selectedProperty().bindBidirectional(
                        cbTreeItem.selectedProperty());
                    setGraphic(cb);
                  } else {
                    setText(item.toString());
                    setGraphic(null);
                  }
                }
              }
            };
          }
        });
    
        tree.setRoot(rootItem);
        tree.setShowRoot(true);
    
        StackPane root = new StackPane();
        root.getChildren().add(tree);
        primaryStage.setScene(new Scene(root, 300, 250));
        primaryStage.show();
      }
    }
    I haven't tested this out much but it seems to work. There might be an easier way but I don't see it.
  • 2. Re: TreeView with mixed cell types
    970765 Newbie
    Currently Being Moderated
    Hi James,
    Thanks for your solution - it seems to work pretty well, but it's taking a bit of time for me to integrate it into my "real" code. Not surprisingly, I have more complex objects than Strings in the Tree. I'm hoping that there isn't a conflict between this local CheckBox and the private CheckBox inside the CheckBoxTreeCell. The bindings should handle that, though.

    I wonder why the "CheckBoxCells" (Table, Tree, ListCheckBoxCells) don't expose the CheckBox as part of the API. Alternatively, they could make the CheckBox protected to allow for extending it. Then again, it is the first pass for these classes, and I haven't created a lot of public APIs. :)

    I'll let you know how it works when I get it integrated into the rest of my code - but it certainly has gotten me going in the right direction.

    Thanks again,
    mike

    Edited by: bopf on Dec 11, 2012 7:38 AM
  • 3. Re: TreeView with mixed cell types
    James_D Guru
    Currently Being Moderated
    bopf wrote:
    Hi James,
    Thanks for your solution - it seems to work pretty well, but it's taking a bit of time for me to integrate it into my "real" code. Not surprisingly, I have more complex objects than Strings in the Tree. I'm hoping that there isn't a conflict between this local CheckBox and the private CheckBox inside the CheckBoxTreeCell. The bindings should handle that, though.
    I didn't use a CheckBoxTreeCell, so no :).

    >
    I wonder why the "CheckBoxCells" (Table, Tree, ListCheckBoxCells) don't expose the CheckBox as part of the API. Alternatively, they could make the CheckBox protected to allow for extending it. Then again, it is the first pass for these classes, and I haven't created a lot of public APIs. :)
    I think the use case for a CheckBoxTreeCell is for TreeViews for which all TreeItems are CheckBoxTreeItems. I'm thinking of something like the import wizards in Eclipse. In this case you don't need to access the CheckBox; it's selected and indeterminate properties are bound directly to the CheckBoxTreeItem's properties. Your use case is slightly more complex as you have mixed cell types in your tree.

    Of course, there may be something I'm missing, but this seems to work.
  • 4. Re: TreeView with mixed cell types
    970765 Newbie
    Currently Being Moderated
    I didn't use a CheckBoxTreeCell, so no . :)
    Duh. I should have caught that - translated CheckBoxTreeItem to CheckBoxTreeCell in my head.
    Of course, there may be something I'm missing, but this seems to work.
    Yep - after creating a whole hierarchy of types to use in the TreeView, I've finally got it working in my "real" code. For now its a sledge-hammer approach (creating the extra hierarchy of domain types), but at least it allowed me to test out your solution. So I've now marked it "correct".

    Thanks again!

Legend

  • Correct Answers - 10 points
  • Helpful Answers - 5 points