In an Eclipse RCP application I was working on recently, I had to implement a TreeViewer with in-place editing feature. It was not easy to find all the information required to implement this, so I thought I would explain it here.
First let’s take a simple example, where hierarchical data is displayed in a tree.
When any item in the tree is double clicked, I want to edit the value in-place.
Here is the code to create this tree, without editing support. I will first create a data model using a Map. To simplify the example, I am assuming only one level of hierarchy.
//Class to represent each node of the tree class TreeItemData { String value; public TreeItemData (String value) { this.value = value; } public String toString() { return value; } } Map<TreeItemData,TreeItemData[]> treeModelData = new HashMap<>(); treeModelData.put(new TreeItemData("parent1"), new TreeItemData[]{ new TreeItemData("child1"), new TreeItemData("child2") });
Then create an instanceof TreeViewer and its content provider.
TreeViewer treeViewer = new TreeViewer(shell); treeViewer.setContentProvider(new ITreeContentProvider() { @Override public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { } @Override public void dispose() { } @Override public boolean hasChildren(Object element) { if (treeModelData.containsKey(element)) return true; return false; } @Override public Object getParent(Object element) { return null; } @Override public Object[] getElements(Object inputElement) { if (inputElement.equals("initial_input")) return treeModelData.keySet().toArray(); return null; } @Override public Object[] getChildren(Object parentElement) { parentElement.toString(); Object value = treeModelData.get(parentElement); if (value != null) return (TreeItemData[])value; return null; } }); //set initial input treeViewer.setInput("initial_input");
The above code will create a TreeViewer with tree nodes, but with no editing support.
To trigger editing in Tree cells on double click, we need to create TreeViewerEditor.
TreeViewerEditor.create(treeViewer, new ColumnViewerEditorActivationStrategy(treeViewer){ protected boolean isEditorActivationEvent(ColumnViewerEditorActivationEvent event) { return event.eventType == ColumnViewerEditorActivationEvent.MOUSE_DOUBLE_CLICK_SELECTION; }
There are two ways to add editing support for cells in the tree. 1)Using setCellEditors method of TreeViewer, and 2)Using ViewerColumn.setEditingSupport API. Eclipse documentation says that the second API is available since Eclipse 3.3 and it is more flexible.
First I will show how to use setCellEditors API
private void enableEditing1(TreeViewer treeViewer) { //You have to create identifier for tree columns treeViewer.setColumnProperties(new String[]{"col1"}); treeViewer.setLabelProvider(new TreeLabelProvider()); //Create text editor treeViewer.setCellEditors(new CellEditor[]{new TextCellEditor(treeViewer.getTree())}); treeViewer.setCellModifier(new ICellModifier() { @Override public void modify(Object element, String property, Object value) { if (element instanceof TreeItem) { //update element and tree model TreeItem treeItem = (TreeItem)element; TreeItemData data = (TreeItemData)treeItem.getData(); data.value = value.toString(); treeItem.setText(value.toString()); } } @Override public Object getValue(Object element, String property) { return element.toString(); } @Override public boolean canModify(Object element, String property) { return true; } }); }
class TreeLabelProvider extends ColumnLabelProvider { public String getText(Object element) { return element.toString(); } }
Alternatively, here is the code to add editing support using ViewerColumn.setEditingSupport API.
private void enableEditing2 (TreeViewer treeViewer) { final TreeViewerColumn column = new TreeViewerColumn(treeViewer, SWT.NONE); column.getColumn().setWidth(150); column.setLabelProvider(new TreeLabelProvider()); final TreeViewer finalTreeViewer = treeViewer; final TextCellEditor cellEditor = new TextCellEditor(treeViewer.getTree()); column.setEditingSupport(new EditingSupport(treeViewer) { @Override protected void setValue(Object element, Object value) { if (element instanceof TreeItemData) { TreeItemData data = (TreeItemData)element; data.value = value.toString(); } finalTreeViewer.update(element, null); } @Override protected Object getValue(Object element) { return element.toString(); } @Override protected TextCellEditor getCellEditor(Object element) { return cellEditor; } @Override protected boolean canEdit(Object element) { return true; } }); //To set width of the column to tree width treeViewer.getControl().addControlListener(new ControlListener() { @Override public void controlResized(ControlEvent e) { column.getColumn().setWidth(((Tree)e.getSource()).getBounds().width); } @Override public void controlMoved(ControlEvent e) { } }); }
Note that you have to specify the width of the table cell column, else it would be set to zero. If you want to set column width equal to the width of the tree then you can do that in the ControlListener, as shown in the above code.
Both above methods will let you edit values in TreeViewer by double clicking on tree items.
The default text box created by TextCellEditor is without borders. You can customize this text box by extending TexCellEditor. The following class adds border to the text box and sets it height to 10 pixels more than the font height.
class MyTextCellEditor extends TextCellEditor { int minHeight = 0; public MyTextCellEditor(Tree tree) { super(tree, SWT.BORDER); Text txt = (Text)getControl(); Font fnt = txt.getFont(); FontData[] fontData = fnt.getFontData(); if (fontData != null && fontData.length > 0) minHeight = fontData[0].getHeight() + 10; } public LayoutData getLayoutData() { LayoutData data = super.getLayoutData(); if (minHeight > 0) data.minimumHeight = minHeight; return data; } }
Now set instance of this class as cell editor in the function enableEditing1 –
treeViewer.setCellEditors(new CellEditor[]{new MyTextCellEditor(treeViewer.getTree())});
And in the function enableEditing2 –
final TextCellEditor cellEditor = new MyTextCellEditor(treeViewer.getTree());
With the above changes, the cell editor is created with border, as shown in the image on the left.
You can find the complete source code of this example here.
-Ram Kulkarni
Hello, I really appreciate your article, it has saved me lot of time, i was having a hard time implementing the the tree viewer, however my problem is still a bit more complex than the topics your article covered. I would like to have multiple columns, each with different data that can be edited independently, however when I try doing this i get a very nasty exception thrown at me, the only column I’m able to add editingSupport to is the first. Any help you can provide me to sole my problem will be greatly appreciated.Once again thank you for the nice article.
If you want multiple columns, you may want to consider TableViewer
Thanks, This helped me a great deal!
Hello! I would really appreciate if you’d explain how to change the initial input data given with viewer.setInput(listOfSomething) into another list listOfSomethingElse. It is in vain to say viewer.setInput(listOfSomethingElse). The TreeView’s content just won’t change. Thanks!
Thanks for a great posting, Saves me a lot of research time.