Overview
The following sections provide a concise explanation of how to work with TableViewer, including:
Instantiating a TableViewer
The order of operations for creating a TableViewer is, perhaps, not what you would expect. You can simply instantiate the TableViewer and then its TableViewerColumns and then you could add the columns to the viewer. That would work, but your columns would not fill the entire width of the container. Rather, you would have a table with a blank column to the right.
To avoid the blank column at the end of the TableViewer you need to use TableColumnLayout. Using that layout requires a composite that only holds your TableViewer, so first you create the composite, then you add the layout to the composite, and then you create and add the TableViewer to the composite.
The following code shows how this is done:
//Create the composite Composite composite = new Composite(container, SWT.NONE); composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1)); //Add TableColumnLayout TableColumnLayout layout = new TableColumnLayout(); composite.setLayout(layout); //Instantiate TableViewer TableViewer tableViewer = new TableViewer(composite, SWT.BORDER | SWT.FULL_SELECTION); table = tableViewer.getTable(); table.setHeaderVisible(true); table.setLinesVisible(true); TableViewerColumn tableViewerColumn = new TableViewerColumn(tableViewer, SWT.NONE); TableColumn tblclmnPixel100 = tableViewerColumn.getColumn(); //Specify width using pixels layout.setColumnData(tblclmnPixel100, new ColumnPixelData(100, true, true)); tblclmnPixel100.setText("Pixel 100"); TableViewerColumn tableViewerColumn_1 = new TableViewerColumn(tableViewer, SWT.NONE); TableColumn tblclmnWeight2 = tableViewerColumn_1.getColumn(); //Specify width using weights layout.setColumnData(tblclmnWeight2, new ColumnWeightData(2, ColumnWeightData.MINIMUM_WIDTH, true)); tblclmnWeight2.setText("Weight 2"); TableViewerColumn tableViewerColumn_2 = new TableViewerColumn(tableViewer, SWT.NONE); TableColumn tblclmnWeight4 = tableViewerColumn_2.getColumn(); //Specify width using weights layout.setColumnData(tblclmnWeight4, new ColumnWeightData(4, ColumnWeightData.MINIMUM_WIDTH, true)); tblclmnWeight4.setText("Weight 4"); TableViewerColumn tableViewerColumn_3 = new TableViewerColumn(tableViewer, SWT.NONE); TableColumn tblclmnWeight6 = tableViewerColumn_3.getColumn(); //Specify width using weights layout.setColumnData(tblclmnWeight6, new ColumnWeightData(6, ColumnWeightData.MINIMUM_WIDTH, true)); tblclmnWeight6.setText("Weight 6"); TableViewerColumn tableViewerColumn_4 = new TableViewerColumn(tableViewer, SWT.NONE); TableColumn tblclmnWeight12 = tableViewerColumn_4.getColumn(); //Specify width using weights layout.setColumnData(tblclmnWeight12, new ColumnWeightData(12, ColumnWeightData.MINIMUM_WIDTH, true)); tblclmnWeight12.setText("Weight 12");
Notice how the first column differs from the rest as it uses pixels to define width; the other columns use a weight value and will adjust their width based on the space that is available for the entire TableViewer. The code may seem a bit involved if you are writing the code by hand; however, if you use RCP Developer or SWT Designer from Instantiations, generating the code above will only take a few seconds. The resulting TableViewer will no longer have the blank column to the right:
Populating a table with a ContentProvider and a LabelProvider
There are two ways to populate a TableViewer. You can use a ContentProvider and a LabelProvider or you can use databinding, which I’ll explain in the next section. SWT Designer makes both methods relatively easy to implement, but if a column should display a string that must be built on the fly by combining several input object properties (such as [first name][space][last name]), then your only option will be to use a ContentProvider and a LabelProvider.
Assuming you have the layout ready, ie, you’ve setup the TableViewer with the columns that you need, you will create the ContentProvider and the LabelProvider as inner classes. In this section we assume we will use a workgroup object that has a member set and will display first name, last name, title, and email address. The ContentPorvider will only return an array of objects that will be displayed in each row, so it would be implemented as follows:
private static class ContentProvider implements IStructuredContentProvider {
public Object[] getElements(Object inputElement) {
Workgroup w = (Workgroup) inputElement;
return w.getMemberSet().toArray();
}
public void dispose() {
}
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
}
}
The label provider will return the icons and strings that will be used in each column:
private class TableLabelProvider extends LabelProvider implements ITableLabelProvider {
public Image getColumnImage(Object element, int columnIndex) {
return null;
}
public String getColumnText(Object element, int columnIndex) {
Person p = (Person) element;
String result = "";
switch(columnIndex){
case 0:
result = p.getFirst();
break;
case 1:
result = p.getLast();
break;
case 2:
result = p.getTitle();
break;
case 3:
result = p.getEmail();
break;
default:
//should not reach here
result = "";
}
return result;
}
}
These two providers must be added to the tableViewer as follows:
tableViewer.setLabelProvider(new TableLabelProvider()); tableViewer.setContentProvider(new ContentProvider());
The table will get populated with a call to the tableViewer.setInput method:
public void setWorkgroup(Workgroup workgroup){
this.workgroup = workgroup;
this.tableViewer.setInput(workgroup);
}
As you add and remove members, you will have to update the table programatically as the table will not reflect changes automatically:
private void add(Person person){
workgroup.add(person);
this.tableViewer.add(person);
this.tableViewer.refresh();
}
private void remove(Person person){
workgroup.remove(person);
this.tableViewer.remove(person);
this.tableViewer.refresh();
}
Here’s the complete example.
Populating a table with Databinding
If you are using SWT Designer, the databinding tool would generate the following initDatabinding method. You will note that there is a WritableSet that is constructed with the memberSet and is passed as viewer input. One very important thing to note is that in this case changes to memberSet will not be reflected in the WritableSet, so the term “databinding” is perhaps not entirely true. If you add or delete members from the memberSet, those changes will not be reflected automatically in the tableViewer. You will still have to call tableViewer add or remove methods. However, if the objects in the set provide support for property listeners, changes in these objects will be reflected in the tableviewer automatically.
protected DataBindingContext initDataBindings() {
DataBindingContext bindingContext = new DataBindingContext();
//
ObservableSetContentProvider setContentProvider = new ObservableSetContentProvider();
tableViewer.setContentProvider(setContentProvider);
//
IObservableMap[] observeMaps = PojoObservables.observeMaps(
setContentProvider.getKnownElements(), Person.class,
new String[] { "first", "last", "title", "email" });
tableViewer
.setLabelProvider(new ObservableMapLabelProvider(observeMaps));
//
WritableSet writableSet = new WritableSet(memberSet, Person.class);
tableViewer.setInput(writableSet);
//
return bindingContext;
}
This initDatabindings method is called from the createContents method and the UI class will keep a reference to the DatabindingContext through an instance variable. The DatabindingContext could then be used to update the target tableViewer or the model. Or, if you are re-loading the UI with a new model, you can use the DatabindingContext to call Dispose and then you can recreate the databinding by simply calling initDataBindings().
protected Control createContents(Composite parent) {
Composite container = new Composite(parent, SWT.NONE);
container.setLayout(new GridLayout(1, false));
// Create the composite
// Add TableColumnLayout
// Create tableViewer its columns
initWorkgroup();
m_bindingContext = initDataBindings();
return container;
}
Here’s the complete example.
TableViewer Events
Two events that are commonly tracked on a TableViewer are selection changes and mouse double-clicks on a TableViewer item. In both cases you will use the getSelection() method to determine the current selection. This method will always return a StructuredSelection object, which implements ISelection, and it will never return null. Most of the time you will cast to either StructuredSelection or IStructuredSelection as follows:
IStructuredSelection sel = (IStructuredSelection) tableViewer.getSelection();
Person person = (Person) sel.getFirstElement(); // this can return null
if(person != null){
System.out.println("Selected : "+ person.getFirst() + " " + person.getLast());
// call a method that will do something
// useful with person
}
else{
MessageDialog.openInformation(getShell(), "Message", "Selection cleared!");
}
Selection changes will be monitored through an ISelectionChangedListener which will tipically be implemented as an inner class:
tableViewer.addSelectionChangedListener(new ISelectionChangedListener() {
public void selectionChanged(SelectionChangedEvent event) {
IStructuredSelection selection = (IStructuredSelection) tableViewer.getSelection();
// ... Do something with selection
}
});
Likewise, double-clicks will be monitored through an IDoubleClickListener:
tableViewer.addDoubleClickListener(new IDoubleClickListener() {
public void doubleClick(DoubleClickEvent event) {
IStructuredSelection selection = (IStructuredSelection) event.getSelection();
// Do something with selection here
}
});
It is also common to change the sorting order on a TableViewer when a column header is clicked. This is acheived by adding a selection adapter to a column and changing the TableViewer sorter from within the adapter’s widgetSelected method:
tblclmnFirst.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
tableViewer.setSorter(new FirstSorter());
}
});
The sorter itself is most commonly implemented a private class that extends ViewerSorter:
private static class FirstSorter extends ViewerSorter {
public int compare(Viewer viewer, Object e1, Object e2) {
Person item1 = (Person) e1;
Person item2 = (Person) e2;
String s1 = item1.getFirst();
String s2 = item2.getFirst();
return s1.compareTo(s2);
}
}
Here’s the complete example of TableViewer events.
Trouble-shooting
Here are a few conditions that will cause the TableViewer to not act as you would expect:
Databinding a Set. Window Builder Pro or SWT Designer will generate databinding code that makes use of a WritableSet, such as:
WritableSet writableSet = new WritableSet(codeDescriptor.getDescriptorSet(), Descriptor.class); tableViewer.setInput(writableSet);
The problem with this databinding approach is that it creates a completely new Set. Changes in the original set–changes in your model– are not reflected in the writableSet because it is a completely different set. As a result, the TableViewer will not get updated as you would expect after making a change to your model. The following statement comes from the JavaDoc for the constructor: “Constructs a new instance in the default realm containing the elements of the given collection. Changes to the given collection after calling this method do not affect the contents of the created WritableSet.”
WritableList does not present this update problem because the WritableList wraps the list that you provide in the constructor (which would be a list from your model). So a coding approach that works perfectly fine when you are working with Lists will “fail silently” when you are working with Sets.
So, how do you work around this problem? If you add or remove items from your model set, also add or remove them from the WritableSet. Obtain the writableSet by using the getInput method in the tableViewer. Once you have gained access to the WritableSet, make the additions and removals as you normally would on any set and, finally, call the refresh method on the tableViewer.
WritableSet set = (WritableSet) this.tableViewer.getInput(); set.add(descriptor); this.tableViewer.refresh();
If you are making changes to an item, follow the change with a call to bindingContext.updateTargets().


Thanks a lot for putting this up, I find resources for this to be quite scarce, especially the documentation for WindowBuilder.
Small question: Can the DataBinding stuff only be done with EMF? When I go to the Bindings tab I can find my TableViewer but not the model I created.