Quantcast
Viewing latest article 1
Browse Latest Browse All 7

Building tables made easy: TableViewerBuilder

In my last RCP training the participants complained a bit about the JFace TableViewer API. I have to agree, tables are the bread and butter of many RCP business applications and binding tables to your data model is a somewhat cumbersome process that usually yields a lot of boilerplate code.

This was eased a bit with the latest additions to JFace Data Binding, which allow you to use data binding to bind the columns of TableViewers. Have a look at the JFace data binding snippets Snippet017TableViewerWithDerivedColumns and Snippet032TableViewerColumnEditing to find out how that works. JFace Data Binding is perfect if the binding between the table UI and the tabel model data is bi-directional.

But many table bindings are a passive thing, they display and modify data in a model object but the model object doesn’t change by itself and you wouldn’t expect the table to refresh automatically if the model changes. This use case is usually implemented using TableViewer and custom CellLabelProviders.

I just finished a builder class TableViewerBuilder, which makes it easy to setup TableViewers. Features of TableViewerBuilder are:

  • Clear separation between data value (like a Date object), formatted value (like a Date object formatted as String) and cell formatting (like dates in the past are colored blue).
  • Binding the columns using nested property Strings like company.country.name instead of writing LabelProviders.
  • Sorting based on data values.
  • Convenient and straightforward builder API.

Example

Let’s say we want to build such a table:

Image may be NSFW.
Clik here to view.
Table

If you want to see a full example right away, here you go: Snippet01TableViewerBuilder

TableViewerBuilder

At first you create a TableViewerBuilder. This instantly creates a Table widget and a TableViewer for you. The given parentComposite needs to be empty, because TableColumnLayout is used internally. For example:

TableViewerBuildert=newTableViewerBuilder(parent);

Columns

You can create columns by calling createColumn on the TableViewerBuilder object. This returns a ColumnBuilder that can be used to configure the table column. When you have finished configuring the column, you have to call build() on the ColumnBuilder to create the actual column:

TableViewerBuildert=newTableViewerBuilder(parent);t.createColumn("City").build();t.createColumn("Population").build();t.createColumn("Area").build();t.createColumn("People/km²").build();t.createColumn("Founding date").build();t.createColumn("Neighbor city").build();

This is the result:

Image may be NSFW.
Clik here to view.

Setting the input data

To set the data to be shown in the table, you can get the TableViewer from the TableViewerBuilder and set a ContentProvider and Input object:

t.getTableViewer().setContentProvider(someContentProvider);t.getTableViewer().setInput(someInput);

If you want to use a Collection as model for your table, you can also use setInput on the TableViewerBuilder with a Collection - this automatically sets an ArrayContentProvider for you:

t.setInput(someCityCollection);

Binding columns

In this example someCityCollection is a list of City objects. The columns of the table can be bound to a property of these objects using bindToProperty:

ColumnBuildercity=t.createColumn("City");city.bindToProperty("name");city.build();ColumnBuilderpopulation=t.createColumn("Population");population.bindToProperty("stats.population");population.build();ColumnBuilderarea=// ...ColumnBuilderdensity=// ...ColumnBuilderfoundingDate=// ...ColumnBuilderneighborCity=// ...

You can also bind a column to an arbitrary value using bindToValue. In the example this helps with the “People/km²” column which is calculated from two other values:

ColumnBuilderdensity=t.createColumn("People/km²");density.bindToValue(newBaseValue<City>(){@OverridepublicObjectget(Citycity){returncity.getStats().getPopulation()/city.getStats().getAreaKm2();}});density.build();

This is the result. Please note that the user can already sort the table by clicking on the table headers:

Image may be NSFW.
Clik here to view.

Formatting values

In this example, the number and date values need to be formatted. You can set a formatter on the column objects for this:

population.format(Formatter.forInt(newDecimalFormat("#,##0")));area.format(Formatter.forDouble(newDecimalFormat("0.00 km²")));density.format(Formatter.forDouble(newDecimalFormat("0")));foundingDate.format(Formatter.forDate(SimpleDateFormat.getDateInstance(SimpleDateFormat.MEDIUM)));

Formatter is a factory class for commonly used formatters (mainly based on java.text.Format). You can always implement the IValueFormatter interface yourself. Please note that formatting has no influence on the sort order, because the comparator responsible for the table sorting uses the raw data values, not the formatted values.

Formatting cells

Sometimes you want to format the cell besides the textual value, for example to customize colors or to set images. You can do that by configuring a cell formatter for the table column:

population.format(newICellFormatter(){publicvoidformatCell(ViewerCellcell,Objectvalue){intpopulation=(Integer)value;intcolor=(population>5000000)?SWT.COLOR_RED:SWT.COLOR_BLACK;cell.setForeground(cell.getControl().getDisplay().getSystemColor(color));}});

If your column is not text based (for example a column with images that are owner-drawn), you can use a custom CellLabelProvider instead of a value and a value formatter:

someColumn.setCustomLabelProvider(newCellLabelProvider(){/* ... */});

The result so far:

Image may be NSFW.
Clik here to view.

Column width and align

Use setPercentWidth, setPixelWidth and align to format the cell width and alignment:

city.setPercentWidth(60);foundingDate.setPixelWidth(100);foundingDate.alignCenter();area.alignRight();

Sorting

You can set the default sort column by calling useAsDefaultSortColumn:

city.useAsDefaultSortColumn();

If you want to sort the column by a value that is different from the original value, you can set a custom value using sortBy:

city.sortBy(newPropertyValue("stats.otherValue"));

Cell editing

You can make columns editable using makeEditable. By default, you get a text cell editor with no formatting applied:

city.makeEditable()

For editing formatted values, you need to specify a formatter which is also responsible to parse the value back from the String:

population.makeEditable(Formatter.forInt());area.makeEditable(Formatter.forDouble(newDecimalFormat("0.00")));foundingDate.makeEditable(Formatter.forDate(SimpleDateFormat.getDateInstance(SimpleDateFormat.MEDIUM)));

You can also set your own cell editor, for example:

ComboBoxViewerCellEditorcityComboEditor=newComboBoxViewerCellEditor(t.getTable(),SWT.READ_ONLY);cityComboEditor.setContenProvider(newArrayContentProvider());cityComboEditor.setLabelProvider(newLabelProvider());cityComboEditor.setInput(RandomData.CITIES);neighborCity.makeEditable(cityComboEditor);

Result:

Image may be NSFW.
Clik here to view.

Download

I hope this helps with building JFace TableViewers. Feedback and contributions are very appreciated: eMail.

You can see the full example code here: Snippet01TableViewerBuilder.

TableViewerBuilder is part of my de.ralfebert.rcputils plug-in which can be downloaded as source project from github: de.ralfebert.rcputils


Viewing latest article 1
Browse Latest Browse All 7

Trending Articles