3

I am using a DefaultTreeModel populated with an override of DefaultMutableTreeNode which supports optionally changing the display string of a node in a tree. As shown in the code below, in my form I populate the tree with new nodes by creating them in a separate class and then passing them in via a wrapper class for my main data type. The procedure there is to create a new overridden DefaultMutableTreeNode, add children to it (each AccessPoint is represented by a node with several child nodes), then store it for later use in the UI.

The first time I add a node this way, it works beautifully. Any subsequent node added with the following code is in fact stored in the DefaultTreeModel, but the JTree is not being updated with the new nodes.

Why is it that the JTree doesn't get populated after the first child is added?

private void populateAccessPointTreeModel(AccessPointDataWrapper wrapper) {
    //the pre-created DefaultMutableTreeNode subclass instance is
    // stored in the wrapper
    DefaultMutableTreeNode accessPointNode =
            wrapper.getAccessPointTreeNode();
    //this line updates the accessPointTree with the new node (I've looked at the
    // value in debug mode, and it does in fact add the node
    ((DefaultMutableTreeNode) accessPointTree.getRoot()).add(accessPointNode);
    //unrelated logic happens down here...
}

I can include the code where I create the node if necessary, but I don't think it is the issue.

Caleb Hearth
  • 3,315
  • 5
  • 30
  • 44

2 Answers2

7

The problem is that DefaultMutableTreeNode does not inform the DefaultTreeModel that its children were updated. To do this you'll either want to call the appropriate method in the table model (nodesChanged or similar) or (preferably) use the DefaultTreeModel.insertNodesInto method.

DefaultTreeModel model = (DefaultTreeModel)accessPointTree.getModel();
DefaultMutableTreeNode root = model.getRoot();
model.insertNodeInto(accessPointNode, root, root.getChildCount());
Kainsin
  • 447
  • 2
  • 8
  • +1, updates to Swing comopnents should always be done by using the model. – camickr Jul 26 '11 at 14:53
  • That makes sense. I had previously been updating through the DefaultTreeModel which I have saved in the UI class, I just ended up using the add on DefaultMutableTreeNode because it was shorter. Thanks. – Caleb Hearth Jul 26 '11 at 15:31
  • I believe that root.getChildCount() will throw out of bounds exceptions (by believe, I mean I know from experience) on insertNodeInto. Exception in thread "Algorithm Thread" java.lang.ArrayIndexOutOfBoundsException: 1 > 0 at java.util.Vector.insertElementAt(Vector.java:571) at javax.swing.tree.DefaultMutableTreeNode.insert(DefaultMutableTreeNode.java:195) at javax.swing.tree.DefaultTreeModel.insertNodeInto(DefaultTreeModel.java:234) – Caleb Hearth Jul 26 '11 at 15:34
  • Make sure that you're using the actual count and not count+1 or anything. `Vector.insertElementAt` allows an index in the range of 0..size inclusive. – Kainsin Jul 26 '11 at 16:11
0

Quite likely you are having some threading issue. Your JTree is updated is some thread, but the important copy of the JTree, the one that is displayed in the Swing event dispatch thread (EDT), knows nothing of those changes.

If this is the case, you have to update the JTree in the Swing EDT using:

SwingUtilities.invokeLater(new Runnable() {
  @Override public void run() { ... update jTree here }
});

I don't know about JTree... maybe it's the TreeModel you have to update in the Swing EDT.

toto2
  • 5,306
  • 21
  • 24