Support free tutorials









vogella training Training Books



Eclipse JFace Table - Advanced Tutorial

Lars Vogel

Version 3.3

20.02.2013

Eclipse JFace Table

This tutorial explains advanced usage of the JFace TableViewer including inline table editing, table filtering and sorting, and model / view interaction. StyledLabelProvider are also discussed.

This tutorial is based on Eclipse 4.3.


Table of Contents

1. Introduction
2. JFace Table Features
2.1. Column editing support
2.2. Filtering data
2.3. Sorting data with ViewerComparator
2.4. TableColumnLayout
2.5. StyledCellLabelProvider and OwnerDrawLabelProvider
2.6. Table column menu and hiding columns
2.7. Tooltips for viewers
2.8. Virtual tables with LazyContentProvider
2.9. Alternative table implementations
3. Tutorial: Sort Columns
4. Tutorial: Editing the table content
5. Tutorial: Using a Filter
6. Tutorial: Using a Styled Label Providers
7. Commands Prerequisite
8. Commands
8.1. Add objects to the table
8.2. Delete objects from your table
8.3. Copy data to the system clipboard
9. Learn more about Eclipse 4 RCP development
10. Support this website
10.1. Thank you
10.2. Questions and Discussion
11. Download
12. Links and Literature
12.1. Source Code
12.2. JFace Resources
12.3. vogella Resources

Get the book Eclipse RCP book

1. Introduction

Eclipse JFace provides functionality to build tables. The following tutorial is based on the JFace Table Tutorial. Please finish that tutorial before continuing with this one.

This tutorial demonstrates how to sort based on table columns, how to make table columns editable and how to filter the displayed data.

We will also learn how to use the StyledCellLabelProvider class to influence the display of data in the table. The usage of commands and how to hide and sort table columns is demonstrated.

2. JFace Table Features

2.1. Column editing support

To make a column in a table editable, you need an object of type EditingSupport.

The following code shows an example of an EditingSupport implementation.

package de.vogella.jface.tableviewer.edit;

import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.EditingSupport;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TextCellEditor;

import com.vogella.eclipse.viewer.Person;

public class FirstNameEditingSupport extends EditingSupport {

  private final TableViewer viewer;
  private final CellEditor editor;

  public FirstNameEditingSupport(TableViewer viewer) {
    super(viewer);
    this.viewer = viewer;
    this.editor = new TextCellEditor(viewer.getTable());
  }

  @Override
  protected CellEditor getCellEditor(Object element) {
    return editor;
  }

  @Override
  protected boolean canEdit(Object element) {
    return true;
  }

  @Override
  protected Object getValue(Object element) {
    return ((Person) element).getFirstName();
  }

  @Override
  protected void setValue(Object element, Object userInputValue) {
    ((Person) element).setFirstName(String.valueOf(value));
    viewer.update(element, null);
  }
} 

The EditingSupport implementation defines how the content can be changed. The getCellEditor() method returns an object of type CellEditor. This object creates the controls to change the data.

The canEdit() method defines if the cell can be edited. The getValue() method receives the current object and returns the value which should be edited.

The method setValue() in EditingSupport receives the changed value based on the user input. In this method you assign the value to your data object.

JFace provides the following default implementations for cell editors:

  • TextCellEditor

  • ColorCellEditor

  • CheckboxCellEditor

  • DialogCellEditor

  • ComboBoxViewerCellEditor

You can assign the instance of EditingSupport to your TableColumn via the setEditingSupport() method of your TableViewerColumn object.

colFirstName.setEditingSupport(new FirstNameEditingSupport(viewer)); 

From an application design perspective, editing within a table can be cumbersome for the user. If the end user has to edit a lot of data, you should also offer a dialog, wizard or part to edit the data.

2.2. Filtering data

A JFace viewer supports filtering of data via the setFilters() or addFilter() methods. These methods expect ViewerFilter objects as arguments.

For each registered ViewerFilter object the select() method is called. The method returns true if the data should be shown and false if it should be filtered.

package de.vogella.jface.tableviewer.filter;

import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerFilter;

import de.vogella.jface.tableviewer.model.Person;

public class PersonFilter extends ViewerFilter {

  private String searchString;

  public void setSearchText(String s) {
    // ensure that the value can be used for matching 
    this.searchString = ".*" + s + ".*";
  }

  @Override
  public boolean select(Viewer viewer, 
      Object parentElement, 
      Object element) {
    if (searchString == null || searchString.length() == 0) {
      return true;
    }
    Person p = (Person) element;
    if (p.getFirstName().matches(searchString)) {
      return true;
    }
    if (p.getLastName().matches(searchString)) {
      return true;
    }

    return false;
  }
} 

All filters are checked whenever the input of the viewer changes, or whenever its refresh() method is called.

If more than one filter is defined for a viewer, all filters must return true to display the data.

2.3. Sorting data with ViewerComparator

JFace supports sorting of the viewer content via the setComparator() method on the viewer object. This method expects a ViewerComparator object. By default, it will sort based on the toString() method of the objects in the viewer.

// sort according to due date
viewer.setComparator(new ViewerComparator() {
  public int compare(Viewer viewer, Object e1, Object e2) {
    Todo t1 = (Todo) e1;
    Todo t2 = (Todo) e2;
    return t1.getDueDate().compareTo(t2.getDueDate());
  };
}); 

2.4. TableColumnLayout

With the TableColumnLayout class you can define the width of the columns in the table. This can be done based on a fixed or percentage value.

Using TableColumnLayout requires a Composite which only contains the table widget. This Composite gets the TableColumnLayout assigned.

Composite tableComposite = new Composite(parent, SWT.NONE);
TableColumnLayout tableColumnLayout = new TableColumnLayout();
tableComposite.setLayout(tableColumnLayout);
TableViewer viewer = 
  new TableViewer(tableComposite, 
      SWT.MULTI | 
      SWT.H_SCROLL | 
      SWT.V_SCROLL | 
      SWT.FULL_SELECTION | 
      SWT.BORDER); 

The TableColumnLayout requires that you define a fixed or relative size for all columns.

// fixed size
tableColumnLayout.
  setColumnData(colFirstName.getColumn(), 
      new ColumnPixelData(50));

// alternatively use relative size
// last parameter defines if the column is allowed 
// to be resized
tableColumnLayout.
  setColumnData(colFirstName.getColumn(), 
      new ColumnWeightData(20, 200, true)); 

2.5. StyledCellLabelProvider and OwnerDrawLabelProvider

It is possible to use a StyledCellLabelProvider for a very flexible styling of your text. StyledCellLabelProvider extends CellLabelProvider and allows you to style the text which is displayed in the cell.

The following example shows how to use a StyledCellLabelProvider. In this example a portion of a pre-defined text is highlighted.

// define column
TableViewerColumn colTesting = new TableViewerColumn(viewer, SWT.NONE);
colTesting.getColumn().setText("Testing");
colTesting.getColumn().setWidth(200);


// set label provider
colTesting.setLabelProvider(new StyledCellLabelProvider() {
  @Override
  public void update(ViewerCell cell) {
    cell.setText("This is a test (15)");
    StyleRange myStyledRange = 
        new StyleRange(16, 2, null, 
            Display.getCurrent().getSystemColor(SWT.COLOR_YELLOW));
    StyleRange[] range = { myStyledRange };
    cell.setStyleRanges(range);
    super.update(cell);
    }
}); 

As a result the number "15" will be highlighted as depicted in the following screenshot.

Screenshot of a styledLabelProvider

The OwnerDrawLabelProvider class is a label provider that handles custom draws.

The following example draws a text and an image into the cell.

TableViewerColumn colHelloAndIcon = new TableViewerColumn(viewer, SWT.NONE);
colHelloAndIcon.getColumn().setText("Column 1");
colHelloAndIcon.getColumn().setWidth(200);


// ICON is an Image 
colHelloAndIcon.setLabelProvider(new OwnerDrawLabelProvider() {
      @Override
      protected void measure(Event event, Object element) {
        Rectangle rectangle = ICON.getBounds();
        event.
        setBounds(new Rectangle(event.x, 
            event.y, 
            rectangle.width + 200 , 
            rectangle.height));
      }

      @Override
      protected void paint(Event event, Object element) {
        Rectangle bounds = event.getBounds();
        event.gc.drawText("Hello", bounds.x, bounds.y);
        Point point = event.gc.stringExtent("Hello");
        event.gc.drawImage(ICON, bounds.x + 5 + point.x, bounds.y);
      }
      
    }); 

OwnerDrawLabelProvider example

2.6. Table column menu and hiding columns

You can add a menu to your table. This menu can get a menu entry for each column. This allow you to add arbitrary popup actions to your columns.

You can use it, for example, to hide and show columns based on the width setting as demonstrated in the following code snippet.

// define the menu and assign to the table
contextMenu = new Menu(viewer.getTable());
viewer.getTable().setMenu(contextMenu);

// create your columns as usual...

// afterwards add a MenuItem for each column to the table menu
for (TableColumn tableColumn : viewer.getTable().getColumns()){
  createMenuItem(contextMenu, tableColumn);
}

// the createMenuItem() method adds a MenuItem
// per column to the menu

private void createMenuItem(Menu parent, final TableColumn column) {
  final MenuItem itemName = new MenuItem(parent, SWT.CHECK);
  itemName.setText(column.getText());
  itemName.setSelection(column.getResizable());
  itemName.addSelectionListener(new SelectionAdapter() {
      @Override
      public void widgetSelected(SelectionEvent e) {
      if (itemName.getSelection()) {
        column.setWidth(150);
        column.setResizable(true);
      } else {
        column.setWidth(0);
        column.setResizable(false);
      }
    }
  });
} 

2.7. Tooltips for viewers

You can use tooltips for the cells of the viewer. To achieve this, you have to activate the tooltips for a viewer.

// activate the tooltip support for the viewer
ColumnViewerToolTipSupport.enableFor(viewer, ToolTip.NO_RECREATE); 

In your CellLabelProvider you specify the related methods for displaying the tooltip.

colFirstName.setLabelProvider(new ColumnLabelProvider() {
  @Override
  public String getText(Object element) {
    Person p = (Person) element;
    return p.getFirstName();
  }

  @Override
  public String getToolTipText(Object element) {
    return "Tooltip (" + ((Person)element).getLastName() + ")";
  }

  @Override
  public Point getToolTipShift(Object object) {
    return new Point(5, 5);
  }

  @Override
  public int getToolTipDisplayDelayTime(Object object) {
    return 100; // msec
  }

  @Override
  public int getToolTipTimeDisplayed(Object object) {
    return 5000; // msec
  }

}); 

2.8. Virtual tables with LazyContentProvider

If you have a huge number of lines which you want to display in the table, you can use a LazyContentProvider. This provider allows you to fetch the data when they are needed instead of loading everything into memory. As a result of the lazy loading you gain a better memory footprint and improve the performance for a large set of data. The following code demonstrates its usage.

private class MyLazyContentProvider implements ILazyContentProvider {
  private TableViewer viewer;
  private Person[] elements;

  public MyLazyContentProvider(TableViewer viewer) {
    this.viewer = viewer;
  }

  public void dispose() {
  }

  public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
    this.elements = (Person[]) newInput;
  }

  public void updateElement(int index) {
    viewer.replace(elements[index], index);
  }
} 

// create your table with the virtual flag
final TableViewer v = new TableViewer(shell, SWT.VIRTUAL);

// create TableColumns with label providers as before....
// ...

v.setContentProvider(new MyLazyContentProvider(v));

// special settings for the lazy content provider
v.setUseHashlookup(true);

// create the model and set it as input
Person[] model = createModel();
v.setInput(model);
// you must explicitly set the items count
v.setItemCount(model.length); 

2.9. Alternative table implementations

It is possible to use other table implementations. Most notable is the NatTable implementation which is a flexible and powerful framework for creating tables, grids and trees that show great performance for a huge number of rows and columns.

3. Tutorial: Sort Columns

The following example will allow to sort the table based on different columns, therefore our ViewerComparator will have the option to set the sort column.

Re-using the project de.vogella.jface.tableviewer from JFace Table Tutorial create the Class de.vogella.jface.tableviewer.sorter.MyViewerComparator.java

package de.vogella.jface.tableviewer.sorter;

import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerComparator;
import org.eclipse.swt.SWT;

import de.vogella.jface.tableviewer.model.Person;

public class MyViewerComparator extends ViewerComparator {
  private int propertyIndex;
  private static final int DESCENDING = 1;
  private int direction = DESCENDING;

  public MyViewerComparator() {
    this.propertyIndex = 0;
    direction = DESCENDING;
  }

  public int getDirection() {
    return direction == 1 ? SWT.DOWN : SWT.UP;
  }

  public void setColumn(int column) {
    if (column == this.propertyIndex) {
      // Same column as last sort; toggle the direction
      direction = 1 - direction;
    } else {
      // New column; do an ascending sort
      this.propertyIndex = column;
      direction = DESCENDING;
    }
  }

  @Override
  public int compare(Viewer viewer, Object e1, Object e2) {
    Person p1 = (Person) e1;
    Person p2 = (Person) e2;
    int rc = 0;
    switch (propertyIndex) {
    case 0:
      rc = p1.getFirstName().compareTo(p2.getFirstName());
      break;
    case 1:
      rc = p1.getLastName().compareTo(p2.getLastName());
      break;
    case 2:
      rc = p1.getGender().compareTo(p2.getGender());
      break;
    case 3:
      if (p1.isMarried() == p2.isMarried()) {
        rc = 0;
      } else
        rc = (p1.isMarried() ? 1 : -1);
      break;
    default:
      rc = 0;
    }
    // If descending order, flip the direction
    if (direction == DESCENDING) {
      rc = -rc;
    }
    return rc;
  }

} 

In View change the method createTableViewerColumn and create a new method getSelectionAdapter(). This method will add a listener to the columns. If one of the columns is selected, the sorting column in ViewerComparator will be set.

package de.vogella.jface.tableviewer;

import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ColumnLabelProvider;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.part.ViewPart;

import de.vogella.jface.tableviewer.model.ModelProvider;
import de.vogella.jface.tableviewer.model.Person;
import de.vogella.jface.tableviewer.sorter.MyViewerComparator;

public class View extends ViewPart {
  public static final String ID = "de.vogella.jface.tableviewer.view";
  private MyViewerComparator comparator;

  private TableViewer viewer;
  // We use icons
  private static final Image CHECKED = Activator.getImageDescriptor("icons/checked.gif").createImage();
  private static final Image UNCHECKED = Activator.getImageDescriptor("icons/unchecked.gif").createImage();

  public void createPartControl(Composite parent) {
    GridLayout layout = new GridLayout(2, false);
    parent.setLayout(layout);
    Label searchLabel = new Label(parent, SWT.NONE);
    searchLabel.setText("Search: ");
    final Text searchText = new Text(parent, SWT.BORDER | SWT.SEARCH);
    searchText.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL
        | GridData.HORIZONTAL_ALIGN_FILL));
    createViewer(parent);
    // Set the sorter for the table
    comparator = new MyViewerComparator();
    viewer.setComparator(comparator);

  }

  private void createViewer(Composite parent) {
    viewer = new TableViewer(parent, SWT.MULTI | SWT.H_SCROLL
        | SWT.V_SCROLL | SWT.FULL_SELECTION | SWT.BORDER);
    createColumns(parent, viewer);
    final Table table = viewer.getTable();
    table.setHeaderVisible(true);
    table.setLinesVisible(true);

    viewer.setContentProvider(new ArrayContentProvider());
    // Get the content for the viewer, setInput will call getElements in the
    // contentProvider
    viewer.setInput(ModelProvider.INSTANCE.getPersons());
    // make the selection available to other views
    getSite().setSelectionProvider(viewer);

    // Layout the viewer
    GridData gridData = new GridData();
    gridData.verticalAlignment = GridData.FILL;
    gridData.horizontalSpan = 2;
    gridData.grabExcessHorizontalSpace = true;
    gridData.grabExcessVerticalSpace = true;
    gridData.horizontalAlignment = GridData.FILL;
    viewer.getControl().setLayoutData(gridData);
  }

  public TableViewer getViewer() {
    return viewer;
  }

  // This will create the columns for the table
  private void createColumns(final Composite parent, final TableViewer viewer) {
    String[] titles = { "First name", "Last name", "Gender", "Married" };
    int[] bounds = { 100, 100, 100, 100 };

    // First column is for the first name
    TableViewerColumn col = createTableViewerColumn(titles[0], bounds[0], 0);
    col.setLabelProvider(new ColumnLabelProvider() {
      @Override
      public String getText(Object element) {
        Person p = (Person) element;
        return p.getFirstName();
      }
    });

    // Second column is for the last name
    col = createTableViewerColumn(titles[1], bounds[1], 1);
    col.setLabelProvider(new ColumnLabelProvider() {
      @Override
      public String getText(Object element) {
        Person p = (Person) element;
        return p.getLastName();
      }
    });

    // now the gender
    col = createTableViewerColumn(titles[2], bounds[2], 2);
    col.setLabelProvider(new ColumnLabelProvider() {
      @Override
      public String getText(Object element) {
        Person p = (Person) element;
        return p.getGender();
      }
    });

    // now the status married
    col = createTableViewerColumn(titles[3], bounds[3], 3);
    col.setLabelProvider(new ColumnLabelProvider() {
      @Override
      public String getText(Object element) {
        return null;
      }

      @Override
      public Image getImage(Object element) {
        if (((Person) element).isMarried()) {
          return CHECKED;
        } else {
          return UNCHECKED;
        }
      }
    });

  }

  private TableViewerColumn createTableViewerColumn(String title, int bound,
      final int colNumber) {
    final TableViewerColumn viewerColumn = new TableViewerColumn(viewer,
        SWT.NONE);
    final TableColumn column = viewerColumn.getColumn();
    column.setText(title);
    column.setWidth(bound);
    column.setResizable(true);
    column.setMoveable(true);
    column.addSelectionListener(getSelectionAdapter(column, colNumber));
    return viewerColumn;
  }

  private SelectionAdapter getSelectionAdapter(final TableColumn column,
      final int index) {
    SelectionAdapter selectionAdapter = new SelectionAdapter() {
      @Override
      public void widgetSelected(SelectionEvent e) {
        comparator.setColumn(index);
        int dir = comparator.getDirection();
        viewer.getTable().setSortDirection(dir);
        viewer.getTable().setSortColumn(column);
        viewer.refresh();
      }
    };
    return selectionAdapter;
  }

  
/** * Passing the focus request to the viewer's control. */
public void setFocus() { viewer.getControl().setFocus(); } }

If you run the example and click on a column header, the table should be sorted according to the content of this column. There will also appear a sort-direction in the top of the column.

4. Tutorial: Editing the table content

Create the following new classes in the new de.vogella.jface.tableviewer.edit package.

package de.vogella.jface.tableviewer.edit;

import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.EditingSupport;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TextCellEditor;

import com.vogella.eclipse.viewer.Person;

public class FirstNameEditingSupport extends EditingSupport {

  private final TableViewer viewer;
  private final CellEditor editor;

  public FirstNameEditingSupport(TableViewer viewer) {
    super(viewer);
    this.viewer = viewer;
    this.editor = new TextCellEditor(viewer.getTable());
  }

  @Override
  protected CellEditor getCellEditor(Object element) {
    return editor;
  }

  @Override
  protected boolean canEdit(Object element) {
    return true;
  }

  @Override
  protected Object getValue(Object element) {
    return ((Person) element).getFirstName();
  }

  @Override
  protected void setValue(Object element, Object userInputValue) {
    ((Person) element).setFirstName(String.valueOf(value));
    viewer.update(element, null);
  }
} 

package de.vogella.jface.tableviewer.edit;

import org.eclipse.jface.viewers.TableViewer;

import de.vogella.jface.tableviewer.model.Person;

public class LastNameEditingSupport extends FirstNameEditingSupport {

  private final TableViewer viewer;

  public LastNameEditingSupport(TableViewer viewer) {
    super(viewer);
    this.viewer = viewer;
  }

  @Override
  protected Object getValue(Object element) {
    return ((Person) element).getLastName();
  }

  @Override
  protected void setValue(Object element, Object value) {
    ((Person) element).setLastName(String.valueOf(value));
    viewer.update(element, null);
  }
} 

package de.vogella.jface.tableviewer.edit;

import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.ComboBoxCellEditor;
import org.eclipse.jface.viewers.EditingSupport;
import org.eclipse.jface.viewers.TableViewer;

import de.vogella.jface.tableviewer.model.Person;

public class GenderEditingSupport extends EditingSupport {

  private final TableViewer viewer;

  public GenderEditingSupport(TableViewer viewer) {
    super(viewer);
    this.viewer = viewer;
  }

  @Override
  protected CellEditor getCellEditor(Object element) {
    String[] gender = new String[2];
    gender[0] = "male";
    gender[1] = "female";

    return new ComboBoxCellEditor(viewer.getTable(), gender);
  }

  @Override
  protected boolean canEdit(Object element) {
    return true;
  }

  @Override
  protected Object getValue(Object element) {
    Person person = (Person) element;
    if (person.getGender().equals("male")) {
      return 0;
    }
    return 1;

  }

  @Override
  protected void setValue(Object element, Object value) {
    Person pers = (Person) element;
    if (((Integer) value) == 0) {
      pers.setGender("male");
    } else {
      pers.setGender("female");
    }
    viewer.update(element, null);
  }
} 

package de.vogella.jface.tableviewer.edit;

import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.CheckboxCellEditor;
import org.eclipse.jface.viewers.EditingSupport;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.swt.SWT;

import de.vogella.jface.tableviewer.model.Person;

public class MarriedEditingSupport extends EditingSupport {

  private final TableViewer viewer;

  public MarriedEditingSupport(TableViewer viewer) {
    super(viewer);
    this.viewer = viewer;
  }

  @Override
  protected CellEditor getCellEditor(Object element) {
    return new CheckboxCellEditor(null, SWT.CHECK | SWT.READ_ONLY);

  }

  @Override
  protected boolean canEdit(Object element) {
    return true;
  }

  @Override
  protected Object getValue(Object element) {
    Person person = (Person) element;
    return person.isMarried();

  }

  @Override
  protected void setValue(Object element, Object value) {
    Person pers = (Person) element;
    pers.setMarried((Boolean) value);
    viewer.update(element, null);
  }
} 

Now assign EditorSupport objects to your TableColumnViewers in your View class. Replace the existing method createColumns() with the following one and adjust your imports.

// This will create the columns for the table
  private void createColumns(final Composite parent, final TableViewer viewer) {
    String[] titles = { "First name", "Last name", "Gender", "Married" };
    int[] bounds = { 100, 100, 100, 100 };

    // First column is for the first name
    TableViewerColumn col = createTableViewerColumn(titles[0], bounds[0], 0);
    col.setLabelProvider(new CellLabelProvider() {
      @Override
      public void update(ViewerCell cell) {
        cell.setText(((Person) cell.getElement()).getFirstName());
      }
    });
    col.setEditingSupport(new FirstNameEditingSupport(viewer));

    // Second column is for the last name
    col = createTableViewerColumn(titles[1], bounds[1], 1);
    col.setLabelProvider(new CellLabelProvider() {
      @Override
      public void update(ViewerCell cell) {
        cell.setText(((Person) cell.getElement()).getLastName());
      }
    });
    col.setEditingSupport(new LastNameEditingSupport(viewer));

    // now the gender
    col = createTableViewerColumn(titles[2], bounds[2], 2);
    col.setLabelProvider(new ColumnLabelProvider() {
      @Override
      public String getText(Object element) {
        Person p = (Person) element;
        return p.getGender();
      }
    });
    col.setEditingSupport(new GenderEditingSupport(viewer));

    // now the status married
    col = createTableViewerColumn(titles[3], bounds[3], 3);
    col.setLabelProvider(new ColumnLabelProvider() {
      @Override
      public String getText(Object element) {
        return null;
      }

      @Override
      public Image getImage(Object element) {
        if (((Person) element).isMarried()) {
          return CHECKED;
        } else {
          return UNCHECKED;
        }
      }
    });
    col.setEditingSupport(new MarriedEditingSupport(viewer));

  } 

Run your application. You should now be able to modify the content of the JFace table.

5. Tutorial: Using a Filter

The following adds a filter to the table based on the Search Text widget. The user can enter search pattern in this text field and data which fits to the entered pattern will be displayed.

Create the following new class.

package de.vogella.jface.tableviewer.filter;

import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerFilter;

import de.vogella.jface.tableviewer.model.Person;

public class PersonFilter extends ViewerFilter {

  private String searchString;

  public void setSearchText(String s) {
    // ensure that the value can be used for matching 
    this.searchString = ".*" + s + ".*";
  }

  @Override
  public boolean select(Viewer viewer, 
      Object parentElement, 
      Object element) {
    if (searchString == null || searchString.length() == 0) {
      return true;
    }
    Person p = (Person) element;
    if (p.getFirstName().matches(searchString)) {
      return true;
    }
    if (p.getLastName().matches(searchString)) {
      return true;
    }

    return false;
  }
} 

Add to your search text field a keyListener which updates the filter and the viewer. You need also to define a new private PersonFilter filter; field and change the createPartControl() method.

private PersonFilter filter;
  //// .... more fields 
  
  public void createPartControl(Composite parent) {
    GridLayout layout = new GridLayout(2, false);
    parent.setLayout(layout);
    Label searchLabel = new Label(parent, SWT.NONE);
    searchLabel.setText("Search: ");
    final Text searchText = new Text(parent, SWT.BORDER | SWT.SEARCH);
    searchText.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL
        | GridData.HORIZONTAL_ALIGN_FILL));
    createViewer(parent);
    // Set the sorter for the table
    comparator = new MyViewerComparator();
    viewer.setComparator(comparator);

    // New to support the search
    searchText.addKeyListener(new KeyAdapter() {
      public void keyReleased(KeyEvent ke) {
        filter.setSearchText(searchText.getText());
        viewer.refresh();
      }

    });
    filter = new PersonFilter();
    viewer.addFilter(filter);
  } 

Run the example, filtering should work.

6. Tutorial: Using a Styled Label Providers

First create the following helper class which will determine which occurrence of the search string is in the column.

package de.vogella.jface.tableviewer.util;

import java.util.ArrayList;
import java.util.List;

public class SearchUtil {
  
/** * Searches "searchTerm" in "content" and returns an array of int pairs * (index, length) for each occurrence. The search is case-sensitive. The * consecutive occurrences are merged together.<code> Examples: content = "123123x123" searchTerm = "1" --> [0, 1, 3, 1, 7, 1] content = "123123x123" searchTerm = "123" --> [0, 6, 7, 3] </code> * * @param searchTerm * can be null or empty. int[0] is returned in this case! * @param content * a not-null string (can be empty!) * @return an array of int pairs (index, length) */
public static int[] getSearchTermOccurrences(final String searchTerm, final String content) { if (searchTerm == null || searchTerm.length() == 0) { return new int[0]; } if (content == null) { throw new IllegalArgumentException("content is null"); } final List<Integer> list = new ArrayList<Integer>(); int searchTermLength = searchTerm.length(); int index; int fromIndex = 0; int lastIndex = -1; int lastLength = 0; while (true) { index = content.indexOf(searchTerm, fromIndex); if (index == -1) { // no occurrence of "searchTerm" in "content" starting from // index "fromIndex" if (lastIndex != -1) { // but there was a previous occurrence list.add(Integer.valueOf(lastIndex)); list.add(Integer.valueOf(lastLength)); } break; } if (lastIndex == -1) { // the first occurrence of "searchTerm" in "content" lastIndex = index; lastLength = searchTermLength; } else { if (lastIndex + lastLength == index) { // the current occurrence is right after the previous // occurrence lastLength += searchTermLength; } else { // there is at least one character between the current // occurrence and the previous one list.add(Integer.valueOf(lastIndex)); list.add(Integer.valueOf(lastLength)); lastIndex = index; lastLength = searchTermLength; } } fromIndex = index + searchTermLength; } final int n = list.size(); final int[] result = new int[n]; for (int i = 0; i != n; i++) { result[i] = list.get(i); } return result; } }

Change the class View to the following. We will only use StyledCellLabelProvider for the firstName. Most of the code changes are in method createColumns() plus a view variable definitions.

package de.vogella.jface.tableviewer;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.CellLabelProvider;
import org.eclipse.jface.viewers.ColumnLabelProvider;
import org.eclipse.jface.viewers.StyledCellLabelProvider;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.part.ViewPart;

import de.vogella.jface.tableviewer.edit.FirstNameEditingSupport;
import de.vogella.jface.tableviewer.edit.GenderEditingSupport;
import de.vogella.jface.tableviewer.edit.LastNameEditingSupport;
import de.vogella.jface.tableviewer.edit.MarriedEditingSupport;
import de.vogella.jface.tableviewer.filter.PersonFilter;
import de.vogella.jface.tableviewer.model.ModelProvider;
import de.vogella.jface.tableviewer.model.Person;
import de.vogella.jface.tableviewer.sorter.MyViewerComparator;
import de.vogella.jface.tableviewer.util.SearchUtil;

public class View extends ViewPart {
  public static final String ID = "de.vogella.jface.tableviewer.view";
  private MyViewerComparator comparator;

  private TableViewer viewer;
  private PersonFilter filter;
  // We use icons
  private static final Image CHECKED = Activator.getImageDescriptor("icons/checked.gif").createImage();
  private static final Image UNCHECKED = Activator.getImageDescriptor("icons/unchecked.gif").createImage();
  private Text searchText;
  private static Color colorYellow = Display.getCurrent().getSystemColor(SWT.COLOR_YELLOW);

  public void createPartControl(Composite parent) {
    GridLayout layout = new GridLayout(2, false);
    parent.setLayout(layout);
    Label searchLabel = new Label(parent, SWT.NONE);
    searchLabel.setText("Search: ");
    searchText = new Text(parent, SWT.BORDER | SWT.SEARCH);
    searchText.setLayoutData(new GridData(GridData.GRAB_HORIZONTAL
        | GridData.HORIZONTAL_ALIGN_FILL));
    createViewer(parent);
    // Set the sorter for the table
    comparator = new MyViewerComparator();
    viewer.setComparator(comparator);

    // New to support the search
    searchText.addKeyListener(new KeyAdapter() {
      public void keyReleased(KeyEvent ke) {
        filter.setSearchText(searchText.getText());
        viewer.refresh();
      }

    });
    filter = new PersonFilter();
    viewer.addFilter(filter);
  }

  private void createViewer(Composite parent) {
    viewer = new TableViewer(parent, SWT.MULTI | SWT.H_SCROLL
        | SWT.V_SCROLL | SWT.FULL_SELECTION | SWT.BORDER);
    createColumns(parent, viewer);
    final Table table = viewer.getTable();
    table.setHeaderVisible(true);
    table.setLinesVisible(true);

    viewer.setContentProvider(new ArrayContentProvider());
    // Get the content for the viewer, setInput will call getElements in the
    // contentProvider
    viewer.setInput(ModelProvider.INSTANCE.getPersons());
    // make the selection available to other views
    getSite().setSelectionProvider(viewer);

    // Layout the viewer
    GridData gridData = new GridData();
    gridData.verticalAlignment = GridData.FILL;
    gridData.horizontalSpan = 2;
    gridData.grabExcessHorizontalSpace = true;
    gridData.grabExcessVerticalSpace = true;
    gridData.horizontalAlignment = GridData.FILL;
    viewer.getControl().setLayoutData(gridData);
  }

  public TableViewer getViewer() {
    return viewer;
  }

  // This will create the columns for the table
  private void createColumns(final Composite parent, final TableViewer viewer) {
    String[] titles = { "First name", "Last name", "Gender", "Married" };
    int[] bounds = { 100, 100, 100, 100 };

    // First column is for the first name
    TableViewerColumn col = createTableViewerColumn(titles[0], bounds[0], 0);
    col.setLabelProvider(new StyledCellLabelProvider() {
      @Override
      public void update(ViewerCell cell) {
        String search = searchText.getText();
        Person person = (Person) cell.getElement();
        String cellText = person.getFirstName();
        cell.setText(cellText);
        if (search != null && search.length() > 0) {
          int intRangesCorrectSize[] = SearchUtil
              .getSearchTermOccurrences(search, cellText);
          List<StyleRange> styleRange = new ArrayList<StyleRange>();
          for (int i = 0; i < intRangesCorrectSize.length / 2; i++) {
            int start = intRangesCorrectSize[i];
            int length = intRangesCorrectSize[++i];
            StyleRange myStyledRange = new StyleRange(start,
                length, null, colorYellow);

            styleRange.add(myStyledRange);
          }
          cell.setStyleRanges(styleRange
              .toArray(new StyleRange[styleRange.size()]));
        } else {
          cell.setStyleRanges(null);
        }

        super.update(cell);

      }
    });
    col.setEditingSupport(new FirstNameEditingSupport(viewer));

    // Second column is for the last name
    col = createTableViewerColumn(titles[1], bounds[1], 1);
    col.setLabelProvider(new CellLabelProvider() {
      @Override
      public void update(ViewerCell cell) {
        cell.setText(((Person) cell.getElement()).getLastName());
      }
    });
    col.setEditingSupport(new LastNameEditingSupport(viewer));

    // now the gender
    col = createTableViewerColumn(titles[2], bounds[2], 2);
    col.setLabelProvider(new ColumnLabelProvider() {
      @Override
      public String getText(Object element) {
        Person p = (Person) element;
        return p.getGender();
      }
    });
    col.setEditingSupport(new GenderEditingSupport(viewer));

    // now the status married
    col = createTableViewerColumn(titles[3], bounds[3], 3);
    col.setLabelProvider(new ColumnLabelProvider() {
      @Override
      public String getText(Object element) {
        return null;
      }

      @Override
      public Image getImage(Object element) {
        if (((Person) element).isMarried()) {
          return CHECKED;
        } else {
          return UNCHECKED;
        }
      }
    });
    col.setEditingSupport(new MarriedEditingSupport(viewer));

  }

  private TableViewerColumn createTableViewerColumn(String title, int bound,
      final int colNumber) {
    final TableViewerColumn viewerColumn = new TableViewerColumn(viewer,
        SWT.NONE);
    final TableColumn column = viewerColumn.getColumn();
    column.setText(title);
    column.setWidth(bound);
    column.setResizable(true);
    column.setMoveable(true);
    column.addSelectionListener(getSelectionAdapter(column, colNumber));
    return viewerColumn;
  }

  private SelectionAdapter getSelectionAdapter(final TableColumn column,
      final int index) {
    SelectionAdapter selectionAdapter = new SelectionAdapter() {
      @Override
      public void widgetSelected(SelectionEvent e) {
        comparator.setColumn(index);
        int dir = comparator.getDirection();
        viewer.getTable().setSortDirection(dir);
        viewer.refresh();
      }
    };
    return selectionAdapter;
  }

  
/** * Passing the focus request to the viewer's control. */
public void setFocus() { viewer.getControl().setFocus(); } }

Run the example. If you search, now the selected content should get highlighted should work.

7. Commands Prerequisite

If you are not familiar with Eclipse Commands, please have a look at the following tutorial: Eclipse Commands Tutorial

8. Commands

8.1. Add objects to the table

This chapter shows how to add persons to the TableViewer.

You have to make the selection of the Viewer available and also provide public method to refresh the data of to the Viewer so that other components can trigger the refresh process.

public void createPartControl(Composite parent) {
  //.... existing coding
  // make selection available via the SelectionProvider
  getSite().setSelectionProvider(viewer);
}

//Used to update the viewer from outsite
public void refresh() {
  viewer.refresh();
} 

Create a Dialog to enter the data for the additional person. Create the de.vogella.jface.tableviewer.dialogs package and the following AddPersonDialog class.

package de.vogella.jface.tableviewer.dialogs;

import org.eclipse.jface.dialogs.IMessageProvider;
import org.eclipse.jface.dialogs.TitleAreaDialog;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;

import de.vogella.jface.tableviewer.model.Person;

public class AddPersonDialog extends TitleAreaDialog {

  private Text text1;
  private Text text2;
  private Person person;
  private Button button1;
  private Combo combo1;

  public Person getPerson() {
    return person;
  }

  public AddPersonDialog(Shell parentShell) {
    super(parentShell);
  }

  @Override
  protected Control createContents(Composite parent) {
    Control contents = super.createContents(parent);
    setTitle("Add a new Person");
    setMessage("Please enter the data of the new person",
        IMessageProvider.INFORMATION);
    return contents;
  }

  @Override
  protected Control createDialogArea(Composite parent) {
    GridLayout layout = new GridLayout();
    layout.numColumns = 2;
    parent.setLayout(layout);
    Label label1 = new Label(parent, SWT.NONE);
    label1.setText("First Name");
    text1 = new Text(parent, SWT.BORDER);
    Label label2 = new Label(parent, SWT.NONE);
    label2.setText("Last Name");
    text2 = new Text(parent, SWT.BORDER);
    Label label3 = new Label(parent, SWT.NONE);
    label3.setText("Gender");
    GridData gd = new GridData(GridData.HORIZONTAL_ALIGN_END);
    gd.horizontalSpan = 2;
    combo1 = new Combo(parent, SWT.READ_ONLY);
    combo1.add("male");
    combo1.add("female");
    button1 = new Button(parent, SWT.CHECK);
    button1.setText("Is married?");
    button1.setLayoutData(gd);
    return parent;

  }

  @Override
  protected void createButtonsForButtonBar(Composite parent) {
    ((GridLayout) parent.getLayout()).numColumns++;

    Button button = new Button(parent, SWT.PUSH);
    button.setText("OK");
    button.setFont(JFaceResources.getDialogFont());
    button.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(SelectionEvent e) {
        if (text1.getText().length() != 0
            && text2.getText().length() != 0
            && combo1.getItem(combo1.getSelectionIndex()).length() != 0) {
          person = new Person(text1.getText(), text2.getText(),
              combo1.getItem(combo1.getSelectionIndex()), button1
                  .getSelection());
          close();

        } else {
          setErrorMessage("Please enter all data");
        }
      }
    });
  }
} 

Create a new View called ChangeView to your application and add it to your perspective.

This View contains a Button which allow to start the Dialog, adds the new Person to the data and refreshes the Viewer.

package de.vogella.jface.tableviewer;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.part.ViewPart;

import de.vogella.jface.tableviewer.dialogs.AddPersonDialog;
import de.vogella.jface.tableviewer.model.ModelProvider;

public class ChangeView extends ViewPart {
  public static final String ID = "de.vogella.jface.tableviewer.changeview";
  private Button btnNewButton;

  @Override
  public void createPartControl(Composite parent) {
    parent.setLayout(new GridLayout(1, false));

    btnNewButton = new Button(parent, SWT.PUSH);
    btnNewButton.addSelectionListener(new SelectionAdapter() {
      @Override
      public void widgetSelected(SelectionEvent e) {

        ModelProvider persons = ModelProvider.INSTANCE;
        AddPersonDialog dialog = new AddPersonDialog(getViewSite()
            .getShell());
        dialog.open();
        if (dialog.getPerson() != null) {
          persons.getPersons().add(dialog.getPerson());
          View part = (View) getViewSite().getPage()
              .findView(View.ID);
          // Updating the display in the view
          part.refresh();
        }
      }
    });
    btnNewButton.setText("Add");

    Button btnDelete = new Button(parent, SWT.PUSH);
    btnDelete.setText("Delete");

  }

  @Override
  public void setFocus() {
    btnNewButton.setFocus();
  }
} 

When you finished this successfully, you are able to add new persons to your TableViewer via clicking on the Button.

8.2. Delete objects from your table

We will implement, that the current selected persons can be deleted from the list.

Create the command de.vogella.jface.tableviewer.commands.DeletePerson with the default handler de.vogella.jface.tableviewer.commands.DeletePerson. Add the command to your menu.

Implement class de.vogella.jface.tableviewer.commands.DeletePerson.

package de.vogella.jface.tableviewer.commands;

import java.util.Iterator;
import java.util.List;

import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.handlers.HandlerUtil;

import de.vogella.jface.tableviewer.View;
import de.vogella.jface.tableviewer.model.ModelProvider;
import de.vogella.jface.tableviewer.model.Person;

public class DeletePerson extends AbstractHandler {
  @SuppressWarnings("unchecked")
  @Override
  public Object execute(ExecutionEvent event) throws ExecutionException {
    IWorkbenchWindow window = HandlerUtil.getActiveWorkbenchWindow(event);
    IWorkbenchPage page = window.getActivePage();
    View view = (View) page.findView(View.ID);
    ISelection selection = view.getSite().getSelectionProvider()
        .getSelection();

    if (selection != null && selection instanceof IStructuredSelection) {
      List<Person> persons = ModelProvider.INSTANCE.getPersons();
      IStructuredSelection sel = (IStructuredSelection) selection;

      for (Iterator<Person> iterator = sel.iterator(); iterator.hasNext();) {
        Person person = iterator.next();
        persons.remove(person);
      }
      view.getViewer().refresh();
    }
    return null;
  }
} 

Please try to delete entries of the table by selection entries and executing Delete Action.

8.3. Copy data to the system clipboard

This chapter shows how to copy the table data to the system clipboard via a command.

Create the command de.vogella.jface.tableviewer.commands.CopyPersonClipboard with the default handler de.vogella.jface.tableviewer.commands.CopyPersonClipboard. Add the command to the menu.

package de.vogella.jface.tableviewer.commands;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.swt.dnd.Clipboard;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IViewPart;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;

import de.vogella.jface.tableviewer.View;
import de.vogella.jface.tableviewer.model.Person;

public class CopyPersonClipboard extends AbstractHandler {

  @Override
  public Object execute(ExecutionEvent event) throws ExecutionException {

    IWorkbenchWindow window = PlatformUI.getWorkbench()
        .getActiveWorkbenchWindow();
    IWorkbenchPage page = window.getActivePage();
    IViewPart view = page.findView(View.ID);
    Clipboard cb = new Clipboard(Display.getDefault());
    ISelection selection = view.getSite().getSelectionProvider()
        .getSelection();
    List<Person> personList = new ArrayList<Person>();
    if (selection != null && selection instanceof IStructuredSelection) {
      IStructuredSelection sel = (IStructuredSelection) selection;
      for (Iterator<Person> iterator = sel.iterator(); iterator.hasNext();) {
        Person person = iterator.next();
        personList.add(person);
      }
    }
    StringBuilder sb = new StringBuilder();
    for (Person person : personList) {
      sb.append(personToString(person));
    }
    TextTransfer textTransfer = TextTransfer.getInstance();
    cb.setContents(new Object[] { sb.toString() },
        new Transfer[] { textTransfer });

    return null;
  }

  private String personToString(Person person) {
    return person.getFirstName() + "\t" + person.getLastName() + "\t"
        + person.getGender() + "\t" + person.isMarried()
        + System.getProperty("line.separator");
  }

} 

Run your application, select a few persons, run your command and paste the result in a text editor, e.g. notepad.

9. Learn more about Eclipse 4 RCP development

I hope you enjoyed this tutorial. You find this tutorial and much more information also in the Eclipse 4 RCP book from this author.

10. Support this website

This tutorial is Open Content under the CC BY-NC-SA 3.0 DE license. Source code in this tutorial is distributed under the Eclipse Public License. See the vogella License page for details on the terms of reuse.

Writing and updating these tutorials is a lot of work. If this free community service was helpful, you can support the cause by giving a tip as well as reporting typos and factual errors.

10.1. Thank you

Please consider a contribution if this article helped you. It will help to maintain our content and our Open Source activities.

10.2. Questions and Discussion

If you find errors in this tutorial, please notify me (see the top of the page). Please note that due to the high volume of feedback I receive, I cannot answer questions to your implementation. Ensure you have read the vogella FAQ as I don't respond to questions already answered there.

11. Download

The checkbox pictures for the JFace Labelprovider: http://www.vogella.com/tutorials/EclipseJFaceTable/download/checkedpics.zip

12. Links and Literature

12.1. Source Code

Source Code of Examples

12.2. JFace Resources

http://www.eclipse.org/articles/Article-Table-viewer/table_viewer.html Building and delivering a table editor with SWT/JFace

http://wiki.eclipse.org/index.php/JFaceSnippets JFace snippets, e.g. small code examples

12.3. vogella Resources

vogella Training Android and Eclipse Training from the vogella team

Android Tutorial Introduction to Android Programming

GWT Tutorial Program in Java, compile to JavaScript and HTML

Eclipse RCP Tutorial Create native applications in Java

JUnit Tutorial Test your application

Git Tutorial Put all your files in a distributed version control system