Refreshing a Viewer
If reloading the viewer with a fresh copy of the same input object, the viewer will not update as expected when an overriden equals method is driven by a unique identifier. In the following example, equals is determined by checking the location.id. The viewer will not refresh if the current input object equals the new input object and will therefore fail to reflect any changes. To avoid this problem, before reloading the viewer, set the input object to null as follows:
this.tableViewer.setInput(null); this.tableViewer.setInput(this.location);
There are cases in which the above will not work. For instance, the Hibernate PersistentSet does not work well with TableViewer. While researching the cause of failure, I found that the TableViewer treats the PersistentSet as an item in the viewer, and when it fails to find the PersistentSet in the item array, the TableViewer simply does not refresh anything in the table (because the item it intended to refresh–in this case, the PersistentSet– is not found in the viewer’s item array).
The solution involves thinking of the viewer as a widget that does not have native databinding and, as a result, you are responsible for synchronizing the model and the viewer. You will perform synchronization with three viewer methods (rather than the single setInput method mentioned above):
- refresh(item). After modifying one of the underlying items in your model, call TableViewer.refresh(yourItem).
- remove(item). After removing an item from the underlying collection in your model, also remove the item from the viewer by calling TableViewer.remove(yourItem).
- add(item). Call this method after adding the item to the collection in your model.
If you think about it, using these three methods will impose a lighter load on the viewer as opposed to reloading the entire model. So in fact, the correct way to refresh a TableViewer is by using refresh, add, and remove at the item level, instead of refreshing the viewer at the collection level.
NullPointerException from ViewerColumn.java:145
If any of the above update operations triggers a NullPointerException thrown by a refresh() method in ViewerColumn, the obscure reason is that you (or the tool that you are using) created the Viewer, then added a ContentProvider and a LabelProvider and then you (or it) belatedly added the TableColumns. That is the wrong order of operations. Make sure you add the ContentProvider and the LabelProvider after the columns are created. The refresh method in the column viewer fails because a call to ViewerColumn.getLabelProvider() is returning null and that’s what triggers the NullPointerException.
This closes the case of the failed refresh method.
Viewer Selection
To obtain a reference to the object currently selected in the viewer, you need to:
- Add a SelectionChangedListener to the viewer
- Obtain a reference to the Selection casting to StructuredSelection
- Obtain the first element by calling StructuredSelection.getFirstElement(), which will return an object that will need to be cast to the appropriate class.
The following block shows these three steps:
tableViewer.addSelectionChangedListener(new ISelectionChangedListener() {
public void selectionChanged(final SelectionChangedEvent arg0) {
StructuredSelection sel = (StructuredSelection) tableViewer.getSelection();
CustomerLocation location = (CustomerLocation) sel.getFirstElement();
updateSelection(location);
}
});
To clear the current selection in a viewer:
- Create new StructuredSelection
- Use viewer.setSelection() to replace the current selection with the new empty selection
protected void emptyViewerSelection() {
StructuredSelection sel = new StructuredSelection();
this.tableViewer.setSelection(sel);
}