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]

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
What is preventing you from writing a tree model class that inherits from DefaultTreeModel?
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
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
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
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...
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
even i agree with charlie76 . same is happening for me also . may be if send a piece of code that will be helpful.
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.
Try with reload() of treeModel. I am sure it will work.
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
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!!
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
}
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().
>
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
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!!!