Eclipse JFace TreeViewer tutorial. This tutorial explains the usage of Eclipse JFace TreeViewer. It also shows the usage of a DelegatingStyledCellLabelProvider.

1. Prerequisites

Please see Introduction to JFace for an introduction to the concepts behind this example.

For an example on how to build JFace Tables please see JFace Table Tutorial.

2. JFace TreeViewer

2.1. Using viewers to display a tree

The TreeViewer class provides viewer support for displaying trees. The usage of this class is similar to the TableViewer class. The main difference is that the TreeViewer class requires a structured content provider. Typically your content provider has to implement the ITreeContentProvider interface to be used with your TreeViewer class.

Basically a TreeViewer can be used similar to a TableViewer, which just shows a list of elements by using the following content provider:

public class TreeContentProvider implements ITreeContentProvider {
    @Override
    public boolean hasChildren(Object element) {
        return false;
    }

    @Override
    public Object getParent(Object element) {
        return null;
    }

    @Override
    public Object[] getElements(Object inputElement) {
        return ArrayContentProvider.getInstance().getElements(inputElement);
    }

    @Override
    public Object[] getChildren(Object parentElement) {
        return null;
    }
}

This ITreeContentProvider just delegates to the ArrayContentProvider in its getElements method and the elements have no children.

    @PostConstruct
    public void postConstruct(Composite parent) {
        TreeViewer viewer = new TreeViewer(parent);
        viewer.setContentProvider(new TreeContentProvider());
        viewer.getTree().setHeaderVisible(true);
        viewer.getTree().setLinesVisible(true);

        TreeViewerColumn viewerColumn = new TreeViewerColumn(viewer, SWT.NONE);
        viewerColumn.getColumn().setWidth(300);
        viewerColumn.getColumn().setText("Names");
        viewerColumn.setLabelProvider(new ColumnLabelProvider());

        viewer.setInput(new String[] { "Simon Scholz", "Lars Vogel", "Dirk Fauth", "Wim Jongman", "Tom Schindl" });

        GridLayoutFactory.fillDefaults().generateLayout(parent);
    }

2.2. Selection and double-click listener

JFace allows you to access the SWT controls to define listeners on your viewer. For example you can add a SelectionListener implementation to the SWT control which is wrapped in the JFace object. The following code snippet demonstrates how to expand a tree with a mouse click.

// the viewer field is an already configured TreeViewer
Tree tree = (Tree) viewer.getControl();
tree.addSelectionListener(new SelectionAdapter() {
  @Override
  public void widgetSelected(SelectionEvent e) {
      TreeItem item = (TreeItem) e.item;
        if (item.getItemCount() > 0) {
            item.setExpanded(!item.getExpanded());
            // update the viewer
            viewer.refresh();
        }
    }
});

Viewers allows you to add certain listeners directly to them. The following example shows how to expand an instance of a TreeViewer with a double click.

viewer.addDoubleClickListener(new IDoubleClickListener() {
    @Override
    public void doubleClick(DoubleClickEvent event) {
        TreeViewer viewer = (TreeViewer) event.getViewer();
        IStructuredSelection thisSelection = (IStructuredSelection) event.getSelection();
        Object selectedNode = thisSelection.getFirstElement();
        viewer.setExpandedState(selectedNode,
                !viewer.getExpandedState(selectedNode));
    }
});

2.3. Adjusting tree columns TreeColumns on expand

In case a TreeViewer has multiple columns it does not look good, if the first column, which contains the expandable items is clipped.

ClippedTreeColumn

In order to let a column fit to it’s contents width you can invoke the columns pack method like that.

// the viewer field is an already configured TreeViewer
Tree tree = (Tree) viewer.getControl();

Listener listener = new Listener() {

   @Override
   public void handleEvent(Event event) {
      TreeItem treeItem = (TreeItem) event.item;
      final TreeColumn[] treeColumns = treeItem.getParent().getColumns();
      display.asyncExec(new Runnable() {

         @Override
         public void run() {
            for (TreeColumn treeColumn : treeColumns)
                 treeColumn.pack();
         }
      });
   }
};

tree.addListener(SWT.Expand, listener);

With this code every column of the Tree will have the appropriate width, when a TreeItem is expaned, so that the contents of the column is not clipped.

2.4. Increasing the font size

The font size can be increased by simply setting a Font for the underlying SWT Tree.

    @PostConstruct
    public void postConstruct(Composite parent) {

        ResourceManager resourceManager = new LocalResourceManager(JFaceResources.getResources(), parent);

        TreeViewer viewer = new TreeViewer(parent);
        viewer.setContentProvider(new TreeContentProvider());
        viewer.getTree().setHeaderVisible(true);
        viewer.getTree().setLinesVisible(true);
        viewer.getTree().setFont(resourceManager.createFont(FontDescriptor.createFrom("Arial", 32, SWT.ITALIC)));

        TreeViewerColumn viewerColumn = new TreeViewerColumn(viewer, SWT.NONE);
        viewerColumn.getColumn().setWidth(300);
        viewerColumn.getColumn().setText("Names");
        viewerColumn.setLabelProvider(new ColumnLabelProvider());

        viewer.setInput(new String[] { "Simon Scholz", "Lars Vogel", "Dirk Fauth", "Wim Jongman", "Tom Schindl" });

        GridLayoutFactory.fillDefaults().generateLayout(parent);
    }

3. Optional Exercise: File browser via a TreeViewer

3.1. Create a new application

This exercise is a stand-alone exercise and can be used to repeat the steps of creating an Eclipse 4 application.

Use the Eclipse 4 wizard from File  New  Other…​  Plug-in Project to create a new Eclipse 4 application without sample data called com.example.e4.filebrowser. The exact process is shown in the Eclipse RCP tutorial. Make sure to uncheck "Create sample content" on the last page of the wizard:

e4 application creation page with

3.2. Add an image file

Download or create an icon called folder.png and place it into the "icons" folder of your plug-in.

You can use the following example icon: Folder icon

This icon is taken from the FamFamFam silk icons collection and is licensed under the Creative Commons Attribution 2.5 License.

3.3. Create a part

Add a part stack with a part to your application model and display a TreeViewer in this part.

Implement a class for the ITreeContentProvider interface which allows you to browse the file system. Review the Javadoc of this class to understand the methods of this interface.

Also implement your custom LabelProvider for the tree.

Use viewer.setInput(File.listRoots()); to set the initial input to the viewer.

The following listing contains an example implementation for this exercise. It assumes that you added the "folder.png" icon to the "icons" folder. It also demonstrates the usage of a ViewLabelProvider.

package com.vogella.jface.treeviewer.parts;

import java.io.File;
import java.net.URL;

import javax.annotation.PostConstruct;

import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.Path;
import org.eclipse.e4.ui.di.Focus;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.resource.LocalResourceManager;
import org.eclipse.jface.resource.ResourceManager;
import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider;
import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider.IStyledLabelProvider;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.StyledString;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkUtil;

public class FileBrowserPart {
    private TreeViewer viewer;

    @PostConstruct
    public void createControls(Composite parent) {
        viewer = new TreeViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
        viewer.setContentProvider(new ViewContentProvider());
        viewer.setLabelProvider(new DelegatingStyledCellLabelProvider(
                new ViewLabelProvider(createImageDescriptor())));
        viewer.setInput(File.listRoots());
    }

    private ImageDescriptor createImageDescriptor() {
        Bundle bundle = FrameworkUtil.getBundle(ViewLabelProvider.class);
        URL url = FileLocator.find(bundle, new Path("icons/folder.png"), null);
        return ImageDescriptor.createFromURL(url);
    }

    class ViewContentProvider implements ITreeContentProvider {
        public void inputChanged(Viewer v, Object oldInput, Object newInput) {
        }

        @Override
        public void dispose() {
        }

        @Override
        public Object[] getElements(Object inputElement) {
            return (File[]) inputElement;
        }

        @Override
        public Object[] getChildren(Object parentElement) {
            File file = (File) parentElement;
            return file.listFiles();
        }

        @Override
        public Object getParent(Object element) {
            File file = (File) element;
            return file.getParentFile();
        }

        @Override
        public boolean hasChildren(Object element) {
            File file = (File) element;
            if (file.isDirectory()) {
                return true;
            }
            return false;
        }

    }

    class ViewLabelProvider extends LabelProvider implements IStyledLabelProvider {

        private ImageDescriptor directoryImage;
        private ResourceManager resourceManager;

        public ViewLabelProvider(ImageDescriptor directoryImage) {
            this.directoryImage = directoryImage;
        }

        @Override
        public StyledString getStyledText(Object element) {
            if(element instanceof File) {
                File file = (File) element;
                StyledString styledString = new StyledString(getFileName(file));
                String[] files = file.list();
                if (files != null) {
                    styledString.append(" ( " + files.length + " ) ",
                            StyledString.COUNTER_STYLER);
                }
                return styledString;
            }
            return null;
        }

        @Override
        public Image getImage(Object element) {
            if(element instanceof File) {
                if(((File) element).isDirectory()) {
                    return getResourceManager().createImage(directoryImage);
                }
            }

            return super.getImage(element);
        }

        @Override
        public void dispose() {
            // garbage collect system resources
            if(resourceManager != null) {
                resourceManager.dispose();
                resourceManager = null;
            }
        }

        protected ResourceManager getResourceManager() {
            if(resourceManager == null) {
                resourceManager = new LocalResourceManager(JFaceResources.getResources());
            }
            return resourceManager;
        }

        private String getFileName(File file) {
            String name = file.getName();
            return name.isEmpty() ? file.getPath() : name;
        }
    }

    @Focus
    public void setFocus() {
        viewer.getControl().setFocus();
    }
}

Link from your part in the application model to your new class.

3.4. Validate

Start your new application ensure that you see the content of your file system in your tree.

4. Optional exercise: Add multiple columns to a tree viewer

This exercise assumes that the previous exercise: File browser via a TreeViewer has been done, since this exercise is based on it.

4.1. Using TreeViewerColumns in the file browser

To add multiple columns in a TreeViewer, the TreeViewerColumn class can be used to define columns for the underlying tree.

A TreeViewerColumn itself is a wrapper around the TreeColumn widget from SWT.

// make the table header visible to see the column's text
viewer.getTree().setHeaderVisible(true);

TreeViewerColumn viewerColumn = new TreeViewerColumn(viewer, SWT.NONE);
TreeColumn column = viewerColumn.getColumn();
column.setText("Name");
column.setWidth(300);

Instead of applying a CellLabelProvider for the whole viewer each TreeViewerColumn gets its own CellLabelProvider assigned. This allows that label for each column can be defined separately. To assign a label provider to the tree viewer column the TreeViewerColumn#setLabelProvider method is used.

package com.vogella.jface.treeviewer.parts;

import java.io.File;
import java.net.URL;
import java.text.DateFormat;
import java.util.Date;

import javax.annotation.PostConstruct;

import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.Path;
import org.eclipse.e4.ui.di.Focus;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.resource.LocalResourceManager;
import org.eclipse.jface.resource.ResourceManager;
import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider;
import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider.IStyledLabelProvider;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.StyledString;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.TreeViewerColumn;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.TreeColumn;
import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkUtil;

public class FileBrowserPart {
    private TreeViewer viewer;

    private static final DateFormat dateFormat = DateFormat.getDateInstance();

    @PostConstruct
    public void createControls(Composite parent) {
        viewer = new TreeViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
        viewer.setContentProvider(new ViewContentProvider());
        viewer.getTree().setHeaderVisible(true);

        TreeViewerColumn mainColumn = new TreeViewerColumn(viewer, SWT.NONE);
        mainColumn.getColumn().setText("Name");
        mainColumn.getColumn().setWidth(300);
        mainColumn.setLabelProvider(
                new DelegatingStyledCellLabelProvider(
                        new ViewLabelProvider(createImageDescriptor())));

        TreeViewerColumn modifiedColumn = new TreeViewerColumn(viewer, SWT.NONE);
        modifiedColumn.getColumn().setText("Last Modified");
        modifiedColumn.getColumn().setWidth(100);
        modifiedColumn.getColumn().setAlignment(SWT.RIGHT);
        modifiedColumn
                .setLabelProvider(new DelegatingStyledCellLabelProvider(
                        new FileModifiedLabelProvider(dateFormat)));

        TreeViewerColumn fileSizeColumn = new TreeViewerColumn(viewer, SWT.NONE);
        fileSizeColumn.getColumn().setText("Size");
        fileSizeColumn.getColumn().setWidth(100);
        fileSizeColumn.getColumn().setAlignment(SWT.RIGHT);
        fileSizeColumn.setLabelProvider(new DelegatingStyledCellLabelProvider(
                new FileSizeLabelProvider()));

        viewer.setInput(File.listRoots());
    }

    private ImageDescriptor createImageDescriptor() {
        Bundle bundle = FrameworkUtil.getBundle(ViewLabelProvider.class);
        URL url = FileLocator.find(bundle, new Path("icons/folder.png"), null);
        return ImageDescriptor.createFromURL(url);
    }

    class ViewContentProvider implements ITreeContentProvider {
        public void inputChanged(Viewer v, Object oldInput, Object newInput) {
        }

        @Override
        public void dispose() {
        }

        @Override
        public Object[] getElements(Object inputElement) {
            return (File[]) inputElement;
        }

        @Override
        public Object[] getChildren(Object parentElement) {
            File file = (File) parentElement;
            return file.listFiles();
        }

        @Override
        public Object getParent(Object element) {
            File file = (File) element;
            return file.getParentFile();
        }

        @Override
        public boolean hasChildren(Object element) {
            File file = (File) element;
            if (file.isDirectory()) {
                return true;
            }
            return false;
        }

    }

    class ViewLabelProvider extends LabelProvider implements IStyledLabelProvider {

        private ImageDescriptor directoryImage;
        private ResourceManager resourceManager;

        public ViewLabelProvider(ImageDescriptor directoryImage) {
            this.directoryImage = directoryImage;
        }

        @Override
        public StyledString getStyledText(Object element) {
            if (element instanceof File) {
                File file = (File) element;
                StyledString styledString = new StyledString(getFileName(file));
                String[] files = file.list();
                if (files != null) {
                    styledString.append(" ( " + files.length + " ) ", StyledString.COUNTER_STYLER);
                }
                return styledString;
            }
            return null;
        }

        @Override
        public Image getImage(Object element) {
            if (element instanceof File) {
                if (((File) element).isDirectory()) {
                    return getResourceManager().createImage(directoryImage);
                }
            }

            return super.getImage(element);
        }

        @Override
        public void dispose() {
            // garbage collection system resources
            if (resourceManager != null) {
                resourceManager.dispose();
                resourceManager = null;
            }
        }

        protected ResourceManager getResourceManager() {
            if (resourceManager == null) {
                resourceManager = new LocalResourceManager(JFaceResources.getResources());
            }
            return resourceManager;
        }

        private String getFileName(File file) {
            String name = file.getName();
            return name.isEmpty() ? file.getPath() : name;
        }
    }

    class FileModifiedLabelProvider extends LabelProvider implements IStyledLabelProvider {

        private DateFormat dateLabelFormat;

        public FileModifiedLabelProvider(DateFormat dateFormat) {
            dateLabelFormat = dateFormat;
        }

        @Override
        public StyledString getStyledText(Object element) {
            if (element instanceof File) {
                File file = (File) element;
                long lastModified = file.lastModified();
                return new StyledString(dateLabelFormat.format(new Date(lastModified)));
            }
            return null;
        }
    }

    class FileSizeLabelProvider extends LabelProvider implements IStyledLabelProvider {

        @Override
        public StyledString getStyledText(Object element) {
            if (element instanceof File) {
                File file = (File) element;
                if (file.isDirectory()) {
                    // a directory is just a container and has no size
                    return new StyledString("0");
                }
                return new StyledString(String.valueOf(file.length()));
            }
            return null;
        }
    }

    @Focus
    public void setFocus() {
        viewer.getControl().setFocus();
    }
}

4.2. Validate

Start your new application ensure that you see the content of your file system in your tree.

Multiple Column FileBrowser

5. Searchable TreeViewer

This example shows how to make a JFace TreeViewer searchable. Pressing Ctrl-f opens a search dialog. If a tree item is found it gets selected.

public class SearchDialog extends Dialog {

    public SearchDialog(Shell parent) {
        super(parent);
    }

    private Tree tree;
    private SearchableTreeViewer searchableTreeViewer;
    private Button searchButton;
    private Text searchField;
    private TreeSearchAlgorithm algorithm;

    public SearchDialog(Shell parent, SearchableTreeViewer searchableTreeViewer, TreeSearchAlgorithm algorithm) {
        super(parent);
        this.searchableTreeViewer = searchableTreeViewer;
        this.algorithm = algorithm;
        tree = searchableTreeViewer.getTree();
    }

    @Override
    protected Control createDialogArea(Composite parent) {
        Composite container = (Composite) super.createDialogArea(parent);
        container.setLayout(new FillLayout());
        searchField = new Text(container, SWT.NONE);
        return container;
    }

    @Override
    protected void createButtonsForButtonBar(Composite parent) {
        searchButton = createButton(parent, IDialogConstants.PROCEED_ID, "Search", false);
        searchButton.addSelectionListener(SelectionListener.widgetSelectedAdapter(this::search));
    }

    private void search(SelectionEvent event) {
        TreeItem found = algorithm.search(tree.getItems(), searchField.getText());
        if (found != null) {
            searchableTreeViewer.setSelection(new StructuredSelection(found.getData()), true);
            close();
        } else {
            System.out.println("Nothing Found");
        }
    }
}
public interface TreeSearchAlgorithm {

    public TreeItem search(TreeItem[] treeItems, String searchTerm);
}
public class TreeContentProvider implements ITreeContentProvider {
    @Override
    public boolean hasChildren(Object element) {
        return false;
    }

    @Override
    public Object getParent(Object element) {
        return null;
    }

    @Override
    public Object[] getElements(Object inputElement) {
        return ArrayContentProvider.getInstance().getElements(inputElement);
    }

    @Override
    public Object[] getChildren(Object parentElement) {
        return null;
    }
}
public class SearchableTreeViewer extends TreeViewer {

    private Composite parent;

    public SearchableTreeViewer(Composite parent, TreeSearchAlgorithm algorithm) {
        super(parent);
        this.parent = parent;
        parent.addKeyListener(KeyListener.keyPressedAdapter(event -> {
            if (event.stateMask == SWT.CTRL && event.keyCode == 'f') {
                startSearchDialog(algorithm);
            }
        }));
    }

    private void startSearchDialog(TreeSearchAlgorithm algorithm) {
        SearchDialog searchDialog = new SearchDialog(parent.getShell(), this, algorithm);
        searchDialog.open();
    }
}
public class TreeWindow extends ApplicationWindow {


    public TreeWindow() {
        super(null);
        // Don't return from open() until window closes
        setBlockOnOpen(true);

        // Open the main window
        open();

        // Dispose the display
        Display.getCurrent().dispose();
    }

    @Override
    protected void configureShell(Shell shell) {
        super.configureShell(shell);

        // Set the title bar text and the size
        shell.setText("Searchable Tree");
        shell.setSize(400, 400);
    }

    @Override
    protected Control createContents(Composite parent) {
        Composite composite = new Composite(parent, SWT.NONE);
        composite.setLayout(new FillLayout());
        SearchableTreeViewer treeViewer = new SearchableTreeViewer(composite,
                (treeItems, searchText) -> {
                    for (TreeItem item : treeItems) {
                        if (item.getText().toUpperCase().contains(searchText.toUpperCase())) {
                            return item;
                        };
                    }
                    return null;
                });
        treeViewer.setContentProvider(new TreeContentProvider());
        TreeViewerColumn viewerColumn = new TreeViewerColumn(treeViewer, SWT.NONE);
        treeViewer.getTree().setHeaderVisible(true);
        treeViewer.getTree().setLinesVisible(true);
        viewerColumn.getColumn().setWidth(300);
        viewerColumn.getColumn().setText("Names");
        viewerColumn.setLabelProvider(new ColumnLabelProvider());
        treeViewer.setInput(new String[] { "Simon Scholz", "Lars Vogel", "Dirk Fauth", "Wim Jongman", "Tom Schindl" });
        return composite;
    }

    public static void main(String[] args) {
        new TreeWindow();
    }
}

6. Learn more and get support

This tutorial continues on Eclipse RCP online training or Eclipse IDE extensions with lots of video material, additional exercises and much more content.