Maintaining expanded/collapsed state in Eclipse TreeViewer

JFace TreeViewer is a very useful UI control for displaying hierarchical data. It is used extensively in Eclipse, for example in Project/Package/Navigator view, Outline view, Debug Variables view etc. Eclipse .org hosts a nice article on TreeViewer – How to use the JFace Tree Viewer.

To specify data and its hierarchy in the TreeViewer, you implement ITreeContentProvider interface. This interface has methods like getElements, getChildren which you need to override to provide content and structure to the TreeViewer. You would typically return array of your model objects from getElements or getChildren methods. And since it is a tree view, you can expand or collapse the nodes. The model classes for an example in the above article could be Category (book, game etc.), Book and Game. Category can have Books or Games. You might load this information from a database and pass it to the content provider object. To refresh information in the TreeViewer, you would call one of the variants of refresh() function.

If you are not recreating model objects on every refresh, then the tree maintains state (collapsed/expanded) of each node. However if you are constructing model objects again, then the TreeViewer does not maintain state of nodes. It would collapse all the nodes. This would happen even if data is not changed. For example consider Category class as follows –

public class Category
{
    private String name;
    private Object[] children; //books or games

    //...
}

and you have a category in the TreeView with name=”Books” and a few child nodes. If you refresh the tree by creating new category object with the same information, then the node for this category would be collapsed in the tree, even if the previous state of the node was expanded.

This is because the updateChildren method of AbstractTreeViewClass first check the state of each node in the tree and stores model objects (data) of all expanded nodes in a Map. Then it iterates through new model objects and checks if new object exists in the map. Eclipse/JFace uses CustomHashTable for this purpose. If it finds the object in the expanded map, then it expands the new node. It checks if given (model) object exists in the map by checking two things – 1. hash code 2. calling equals method. So even if data in two model objects are same, they would not be same for the CustomHashTable because their hash code may be different and equals method would return false (default implementation of equals compares reference/address of objects).

Therefore if you want to maintain state of nodes in the TreeViewer, your model object should override hashCode() and equals() method. The hashCode method should return the same value for same data and equals should return true. In the above example, we could modify Category class as follows to maintain its state in the TreeViewer –

public class Category
{
    private String name;
    private Object[] children; //books or games

    public int hashCode()
    {
        return name.hashCode();
    }

    public boolean equals (Object obj)
    {
        if (obj instanceof Category == false)
            return false;

        return name.equals(((Category)obj).name);
    }

    //...
}

-Ram Kulkarni

7 Replies to “Maintaining expanded/collapsed state in Eclipse TreeViewer”

    1. Hi Raj,

      I am really note sure how to help you. Based on my experience of TreeViewer I can only say that it should work if you make sure equals and hashCode functions return the same result for objects with same data i.e. of obj1 and obj2 two have same data then equals must return true and the same value must be returned by hashCode function.

  1. Dear Ram,

    Please I downloaded your source code cbg.article.treeviewer.zip and run the rcp application but tree viewer as shown in your article like that not visible and filters also not visible please.Eclipse juno,rcp,swt etc..
    Please kindly I need to implement same thing like that in my eclipse rcp based applications.

    1. That article is not written by me. I just posted a link to that article because it has useful information on TreeViewer. The source code you downloaded is also part of that article.
      My blog post was specifically about keeping expanded state of notes when refreshing tree with same/new data.

    2. Try:

      Object[] expandedElements = viewer.getExpandedElements();
      TreePath[] expandedTreePaths = viewer.getExpandedTreePaths();

      viewer.setInput(projects);

      viewer.setExpandedElements(expandedElements);
      viewer.setExpandedTreePaths(expandedTreePaths);

Leave a Reply

Social