Back to top

vogella training Training Books

Eclipse JFace Table - Advanced Tutorial

Lars Vogel

Version 3.3

20.02.2013

Revision History
Revision 0.1 01.07.2007 Lars
Vogel
created
Revision 0.2 - 3.3 12.09.2007 - 20.02.2013 Lars
Vogel
bug fixes and enhancements

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.2.


Table of Contents

1. Introduction
2. JFace Table Features
2.1. Editing Support
2.2. Filtering data
2.3. ViewerComparator
2.4. TableColumnLayout
2.5. StyledCellLabelProvider and OwnerDrawLabelProvider
2.6. Show and hide table columns
2.7. Tooltips for Viewers
2.8. Virtual tables with LazyContentProvider
3. Tutorial: Sort Columns
4. Tutorial: Editing the table content
5. Tutorial: Using a Filter
6. Tutorial: Using a Styled Label Providers
7. Commands Prerequisitions
8. Commands
8.1. Add a objects to the table
8.2. Delete objects from your table
8.3. Copy data to the system clipboard
9. Thank you
10. Questions and Discussion
11. Download
12. Links and Literature
12.1. Source Code
12.2. JFace Resources
12.3. vogella Resources

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. Editing Support

To make a JFace TableColumn editable, you need an object of type EditingSupport.

The following shows an example of an EditorSupport 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 de.vogella.jface.tableviewer.model.Person;

public class FirstNameEditingSupport extends EditingSupport {

  private final TableViewer viewer;

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

  @Override
  protected CellEditor getCellEditor(Object element) {
    return new TextCellEditor(viewer.getTable());
  }

  @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 value) {
    ((Person) element).setFirstName(String.valueOf(value));
    viewer.update(element, null);
  }
} 

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

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.

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

Eclipse provides default implementations for typical cases, e.g. TextCellEditor, ColorCellEditor, CheckboxCellEditor, DialogCellEditor and ComboBoxViewerCellEditor.

You can assign the EditorSupport class to your TableColumn via the setEditingSupport() method of your TableViewerColumn.

col.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

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 methods 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) {
    // Search must be a substring of the existing value
    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 on the Viewer changes, or whenever the refresh() method is called on the Viewer.

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

2.3. ViewerComparator

JFace supports sorting of the table 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 columns in the table. This can be done based on a fixed value or on a percentage.

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 tableViewer = 
  new TableViewer(tableComposite, SWT.BORDER|
    SWT.FULL_SELECTION); 

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

// Fixed size
tableColumnLayout.
  setColumnData(viewerNameColumn.getColumn(), 
      new ColumnPixelData(50));

// Relative size
// Last parameter defines if the column is allowed 
// to be resized
tableColumnLayout.
  setColumnData(viewerNameColumn.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 draw via its update() method.

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

column.setLabelProvider(new StyledCellLabelProvider() {
@Override
  public void update(ViewerCell cell) {
    StyledString text = new StyledString();
    StyleRange myStyledRange = new StyleRange(17, 2, null, Display
      .getCurrent().getSystemColor(SWT.COLOR_YELLOW));
    text.append("This is a test", StyledString.DECORATIONS_STYLER);
    text.append(" (" + 15 + ") ", StyledString.DECORATIONS_STYLER);
    cell.setText(text.toString());

    StyleRange[] range = { myStyledRange };
    cell.setStyleRanges(range);
    super.update(cell);
  }
}); 

As a result the number "15" will be highlighted as presented 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.

// ICON is an Image 
col.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. Show and hide table columns

You can add a Menu to your table. This Menu can get a MenuItem for each column and you can use it for example to hide and show columns based on the width setting.

// Define the menu and assign to the table
headerMenu = new Menu(viewer.getTable());
viewer.getTable().setMenu(headerMenu);
// Create your columns....
// ... as usual


// Now add a MenuItem for the colum to the table menu
createMenuItem(headerMenu, column.getColumn());

// The createMenuItem method add per column a 
// new MenuItem 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.addListener(SWT.Selection, new Listener() {
    public void handleEvent(Event event) {
      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. You have to activate tooltips for the Viewer to get this working.

// 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.

col.setLabelProvider(new CellLabelProvider() {
  @Override
  public void update(ViewerCell cell) {
    cell.setText(((Person) cell.getElement()).getLastName());
  }

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

  @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. The following code demonstrates its usage.

private class MyContentProvider implements ILazyContentProvider {
    private TableViewer viewer;
    private YourModel[] elements;

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

    public void dispose() {

    }

    public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
      this.elements = (YourModel[]) 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 and LabelProvider for them
// ..
// .. 

// Now the special settings for the lazy Content provider
v.setContentProvider(new MyContentProvider(v));
v.setUseHashlookup(true);
YourModel[] model = createModel();
v.setInput(model);
// You must explizitly set the items count
v.setItemCount(model.length); 

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 de.vogella.jface.tableviewer.model.Person;

public class FirstNameEditingSupport extends EditingSupport {

  private final TableViewer viewer;

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

  @Override
  protected CellEditor getCellEditor(Object element) {
    return new TextCellEditor(viewer.getTable());
  }

  @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 value) {
    ((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 will add a filter to the table by using the "Search" field. The user can enter search pattern in the Search box and only data which fits to the pattern will be displayed.

Create a new Class "de.vogella.jface.tableviewer.filter.PersonFilter.java"

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) {
    // Search must be a substring of the existing value
    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 field "private PersonFilter filter;" 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 Prerequisitions

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

8. Commands

8.1. Add a 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.NONE);
    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.NONE);
    btnDelete.setText("Delete");

  }

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

When you finished this successful 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 a 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. Thank you

Please help me to support this article:

Flattr this

10. Questions and Discussion

Before posting questions, please see the vogella FAQ. If you have questions or find an error in this article please use the www.vogella.com Google Group. I have created a short list how to create good questions which might also help you.

11. Download

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

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 and compile to JavaScript and HTML

Eclipse RCP Tutorial Create native applications in Java

JUnit Tutorial Test your application

Git Tutorial Put everything you have under distributed version control system