Most Popular :-) JTree refresh problem

Hello everyone,

I am posting about a JTree refresh problem that seems to be very popular....

My code uses a custom model that implements TreeModel (unfortunately I cannot just inherit from DefaultTreeModel). My custom model also provides the same implementations for the fireTreeNodesXXXX() and fireTreeStructureChanged() methods as javax.swing.tree.DefaultTreeModel.

When nodes are dynamically inserted into the model, I attempt to update the tree UI to reflect the new changes.

All approaches except invoking updateUI() fail!

The problem with using updateUI() is that for starters it is meant to be used to notify the control that the L&F has changed. Using updateUI will surely force the tree to redraw, with serious performance penalties. In addition if you want custom height for each rown in the tree, update UI will force a call to setRowHeight() on the table, effectively effectively resetting the tree to fixed row height rendering!

According to all JTree documentation (and Swing use examples provided by SUN and others), invoking the fireTreeNodeInserted() and fireTreeStructureChange() events should be sufficient since all observers (including the JTree instance) are notified of model changes. I have verified that the JTree is actually notified of the structure change event.

Using calls to fireTreeNodeInserted() and fireTreeStructureChange() after the model has changed, does not update the tree UI for me. I have also tried update(), invalidate(), revalidate(), repaint(), with no effect.

Since many people keep posting about this problem over and over again, either there is a lack of proper documentation on this topic or there are some "special features" in the JTree implementation the public is not aware of.

Perhaps some Guru from SUN would be kind enough to help us in this thread, and provide a clear explanation (perhaps some examples) of how this JTree update should be done, both within a UI thread and from an non-UI worker thread!

As always everyone's contribution is appreciated.

Thanks in advance...

[2124 byte] By [mpetres] at [2007-9-26 2:17:34]
# 1

My two cents is this: the UI delegate for JTree is 'expecting' the model to fire events in the same way that DefaultTreeModel does. I have created quite a few custom models and I have found myself taking great pains to avoid constantly repainting them when data change.

Mitch Goldstein

Author, Hardcore JFC (Cambridge Univ Press)

mdgoldstein@hotmail.com

mitchg at 2007-6-29 9:17:25 > top of Java-index,Desktop,Core GUI APIs...
# 2
What is preventing you from writing a tree model class that inherits from DefaultTreeModel?
DrClap at 2007-6-29 9:17:25 > top of Java-index,Desktop,Core GUI APIs...
# 3
Hello.Are you using JTree for an applet? The reason I'm asking, because I'm writting a tree applet and using JTree and getting an error classnotfound javax.swing.JApplet if I use IE 5.5, but it works fine if I use Netscape 6.0.Thank you, Luiza
lhagan at 2007-6-29 9:17:25 > top of Java-index,Desktop,Core GUI APIs...
# 4

I have the same problem. However, if I add the node while the parent has not been expanded then when I expand the parent, after insertion, my new node is there.

However, if I add a node to the parent after the parent has been expanded the tree only paints the expander icon next to the parent ( that's if the parent was previously a leaf ). Even if I collapse the parent, try to add it, and then re-expand it, the tree still doesn't paint the newly inserted node.

I placed println's to see what the Jtree was calling once I notified it I inserted a new node, and all it seems to be doing is calling getChildCount(). I expected it to call getChild() at some point to paint the newly inserted child, but it doesn't!

Why does this happen when the tree is already expanded? Can some of Sun guru's explain what happens when the tree is already expand and how it paints differently?

thanks

charlie

charlie76 at 2007-6-29 9:17:25 > top of Java-index,Desktop,Core GUI APIs...
# 5

Ok I've solved my problem. What is comes down to is ensuring that your TreePath's are equal. JTree internally caches TreePaths to identify nodes. THey are stored internally in a Hashtable so it is important that your objects that live in the TreePath's implement their own equal and hashCode methods. TreePath's hashCode method delegates to the the last component in the TreePath for it's hashCode().

hope this helps out.

charlie

charlie76 at 2007-6-29 9:17:25 > top of Java-index,Desktop,Core GUI APIs...
# 6

Hello folks,

Thanks for joining in.

The problem I was describing leads to more basic issues. For instance, as long as the custom model inherits from DefaultTreeModel and the nodes for the tree implement the TreeNode, updates in general will work fine, if one lets, the DefaultTreeModel handle the details associated with invoking the fireXXXX() methods.

What we are finding however is that if the tree model does not extend the DefaultTreeModel, and the nodes for the tree do not implement TreeNode, there will be view update problems! No matter what sequence of fireXXX() methods one resorts to, the view is not updated...

Here again, there is no indication from SUN that this should fail. Their examples all use custom models, that extend DefaultTreeModel. The documentation for TreeModel states that TreeModel is "...The interface that defines a suitable data model for the JTree...". It stands to reason that a custom model that only implements TreeModel should suffice... Perhaps, but it doesn't.

It seems that there is some unmentioned little detail (about JTree and its companions) due to which only models extended from DefaultTreeModel using nodes implementing TreeNode are acceptable.

I invite someone from SUN again to join in and comment on this...

mpetres at 2007-6-29 9:17:25 > top of Java-index,Desktop,Core GUI APIs...
# 7

What sort of display update problems are you having? I have implemented my own TreeModel that doesn't extend DefaultTreeModel, and I haven't run into any update issues other than the ones I wrote about above. And, those we're "bugs" in my code, or incorrect assumptions about how TreePath's work. Once I resolved the equals methods of my nodes that were in the TreePath it worked just fine.

How are you creating the TreeModelEvent?

charlie

charlie76 at 2007-6-29 9:17:25 > top of Java-index,Desktop,Core GUI APIs...
# 8
even i agree with charlie76 . same is happening for me also . may be if send a piece of code that will be helpful.
raj_rash36 at 2007-6-29 9:17:25 > top of Java-index,Desktop,Core GUI APIs...
# 9
Are you updating your tree from threads other than the event dispatch thread? If you are, you need to wrap SwingUtilities.invokeLater() around your update code.
chuanhaochiu at 2007-6-29 9:17:25 > top of Java-index,Desktop,Core GUI APIs...
# 10
Try with reload() of treeModel. I am sure it will work.
ashu1_99 at 2007-6-29 9:17:25 > top of Java-index,Desktop,Core GUI APIs...
# 11
It would be really nice if you could make a tree without caring about the model you are using. I invite sun to look at the TreeView control in VB6
spankmehardnow at 2007-6-29 9:17:25 > top of Java-index,Desktop,Core GUI APIs...
# 12

Hi mpetres,

I had the same problem. I created a file browser (similar to Windows Explorer) which originally created representation of my directory structure at run time. This took extreamly long so I decided to dynamically populate the contents of the parent node whenever the user expanded the tree using addTreeWillExpandListener.

Here is a snipet of my code:

File root = new File("c:\\");

rootNode = new DefaultMutableTreeNode(root);

treeModel = new DefaultTreeModel(rootNode);

tree = new JTree(treeModel);

tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);

tree.setEditable(true);

tree.setShowsRootHandles(true);

tree.putClientProperty("JTree.lineStyle", "Angled");

tree.addTreeWillExpandListener(new treeWillExpandListener()

{

public void treeWillExpand(TreeExpansionEvent tee)

{

//Add child nodes here

System.out.println("Tree Will Expand event occured");

}

public void treeWillCollapse(TreeExpansionEvent e)

{

System.out.println("Tree Will Collapse event occured");

}

public void treeExpanded(TreeExpansionEvent e)

{

System.out.println("Tree Expand event occured");

}

public void treeCollapsed(TreeExpansionEvent e)

{

System.out.println("Tree Collapsed occured");

}

});

Hope this helps!!

CBrodus at 2007-6-29 9:17:25 > top of Java-index,Desktop,Core GUI APIs...
# 13

private TreeNode[] treeNodes;

private TreePath treePath;

private DefaultTreeModel treeModel;

public void addNode(Node userNode)

{

treeNodes=treeModel.getPathToRoot((DefaultMutableTreeNode)userNode);

//gives back an array of all Nodes from the root to userNode

treePath=new TreePath(treeNodes);

//constructs a path from the array of Nodes

tree.makeVisible(treePath);

//This is to make the path visible

tree.scrollPathToVisible(treePath);

//to scroll to the userNode

}

weberio at 2007-6-29 9:17:25 > top of Java-index,Desktop,Core GUI APIs...
# 14

Here is my solution to the problem. I found it thanks to you.

I had the same problem with my multi-column quicksort sorting treetable. On every sort all collapsed.

Here is my solution

I created two functions, one to save all paths and one to load all paths.

I put these functions in my TreeTable class but for you guys it will probably be somewhere else.

public Vector savePaths() {

Vector v = new Vector();

for (int i=0; i<getTree().getRowCount(); i++) {

if (getTree().isExpanded(i)) {

v.add(getTree().getPathForRow(i));

}

}

return v;

}

public void loadPaths(Vector v) {

for (int i=0; i><v.size(); i++) {

getTree().expandPath((TreePath) v.get(i));

}

}

Now the trick is to execute savePaths before reload() and loadPaths() after reload().

>

Willllem at 2007-6-29 9:17:25 > top of Java-index,Desktop,Core GUI APIs...
# 15

If you want to maintain the paths after reload, you have to save that, but just normal add/remove of nodes should not require a reload. I didn't read the full thread, and I don't know what you were doing that was wrong, but I've done custom tree models before and never had problems.

The link below, for anyone that wants it, includes my standard AbstractTreeModel, as well as a subclass for TreeNodes and a node class and model for hideable nodes. It doesn't have all the features of the DefaultTreeModel, but has a couple things it doesn't have, such as a means to save the expanded paths and re-expand them on reload, an extra method to implement to allow getPathToRoot() to work, and add/remove/change notification methods that have always worked for me.

http://forum.java.sun.com/thread.jsp?forum=57&thread=563514

bsampieri at 2007-7-1 1:46:52 > top of Java-index,Desktop,Core GUI APIs...
# 16
FINALLY!!! If using the DefaultTreeModel - don't forget to implement hashCode() AND equals() for ANY tree nodes you subclass. I fought this one for about 1 year - thanks to this note, I FINALLY picked up on this rather small detail...thanks to all on this thread!!!
Brian_ingen at 2007-7-1 1:46:52 > top of Java-index,Desktop,Core GUI APIs...