Home Tutorials Training Consulting Books Company Contact us


Get more...

This tutorial explains the editing support in NatTable. After this tutorial you should be able to configure the edit behavior in NatTable. This tutorial is based on Nebula NatTable 2.0.

1. Exercise: Add simple editing support to a NatTable

In this exercise editing support is implemented in a NatTable.

1.1. Creating a part with a NatTable

Add a new class NatTableEditorExample to your Natable example. Also add this as new part to your application.

For the sake of practicing you could try to create such an NatTable on your own, by using the PersonService, a ReflectiveColumnPropertyAccessor, a ListDataProvider, a header data provider, e.g., a PersonHeaderDataProvider, and the DefaultGridLayer.

package com.vogella.nattable.parts;

import java.util.List;

import jakarta.annotation.PostConstruct;

import org.eclipse.nebula.widgets.nattable.NatTable;
import org.eclipse.nebula.widgets.nattable.config.AbstractRegistryConfiguration;
import org.eclipse.nebula.widgets.nattable.config.DefaultNatTableStyleConfiguration;
import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry;
import org.eclipse.nebula.widgets.nattable.config.IEditableRule;
import org.eclipse.nebula.widgets.nattable.data.ListDataProvider;
import org.eclipse.nebula.widgets.nattable.data.ReflectiveColumnPropertyAccessor;
import org.eclipse.nebula.widgets.nattable.edit.EditConfigAttributes;
import org.eclipse.nebula.widgets.nattable.grid.layer.DefaultGridLayer;
import org.eclipse.swt.widgets.Composite;

import com.vogella.tasks.model.Task;
import com.vogella.tasks.model.TaskService;

public class NatTableEditorExample {

    @PostConstruct
    public void postConstruct(Composite parent, TaskService taskService) {
        List<Task> tasks = taskService.getAll();
        ReflectiveColumnPropertyAccessor<Task> columnPropertyAccessor = new TaskColumnPropertyAccessor();
        ListDataProvider<Task> dataProvider = new ListDataProvider<>(tasks, columnPropertyAccessor);

        PersonHeaderDataProvider headerDataProvider = new PersonHeaderDataProvider();

        DefaultGridLayer gridLayer = new DefaultGridLayer(dataProvider, headerDataProvider);


        NatTable natTable = new NatTable(parent, gridLayer, false);
        natTable.addConfiguration(new DefaultNatTableStyleConfiguration());
        natTable.addConfiguration(new AbstractRegistryConfiguration() {

            @Override
            public void configureRegistry(IConfigRegistry configRegistry) {
                configRegistry.registerConfigAttribute(EditConfigAttributes.CELL_EDITABLE_RULE,
                        IEditableRule.ALWAYS_EDITABLE);
            }
        });

        natTable.configure();
    }

}

For simple text editing purposes only a EditConfigAttributes.CELL_EDITABLE_RULE config attribute has to be registered to the IConfigRegistry.

package com.vogella.nattable.parts.edit;

import java.util.List;

import jakarta.annotation.PostConstruct;

import org.eclipse.nebula.widgets.nattable.NatTable;
import org.eclipse.nebula.widgets.nattable.config.AbstractRegistryConfiguration;
import org.eclipse.nebula.widgets.nattable.config.DefaultNatTableStyleConfiguration;
import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry;
import org.eclipse.nebula.widgets.nattable.config.IEditableRule;
import org.eclipse.nebula.widgets.nattable.data.ListDataProvider;
import org.eclipse.nebula.widgets.nattable.data.ReflectiveColumnPropertyAccessor;
import org.eclipse.nebula.widgets.nattable.edit.EditConfigAttributes;
import org.eclipse.nebula.widgets.nattable.grid.layer.DefaultGridLayer;
import org.eclipse.swt.widgets.Composite;

import com.vogella.model.person.Person;
import com.vogella.model.person.PersonService;
import com.vogella.nattable.data.PersonHeaderDataProvider;

public class SimpleEditor {

    @PostConstruct
    public void postConstruct(Composite parent, PersonService personService) {
        List<Person> persons = personService.getPersons(10);
        ReflectiveColumnPropertyAccessor<Person> columnPropertyAccessor = new ReflectiveColumnPropertyAccessor<>(
                "firstName", "lastName", "gender", "married", "birthday");
        ListDataProvider<Person> dataProvider = new ListDataProvider<>(persons, columnPropertyAccessor);

        PersonHeaderDataProvider headerDataProvider = new PersonHeaderDataProvider();

        DefaultGridLayer gridLayer = new DefaultGridLayer(dataProvider, headerDataProvider);

        NatTable natTable = new NatTable(parent, gridLayer, false);
        natTable.addConfiguration(new DefaultNatTableStyleConfiguration());
        natTable.addConfiguration(new AbstractRegistryConfiguration() {

            @Override
            public void configureRegistry(IConfigRegistry configRegistry) {
                configRegistry.registerConfigAttribute(EditConfigAttributes.CELL_EDITABLE_RULE,
                        IEditableRule.ALWAYS_EDITABLE);
            }
        });

        natTable.configure();
    }

}

It uses a TaskHeaderDataProvider similar to this.

package com.vogella.nattable.parts;

import org.eclipse.nebula.widgets.nattable.data.IDataProvider;

public class TaskHeaderDataProvider implements IDataProvider {

    @Override
    public Object getDataValue(int columnIndex, int rowIndex) {
        switch (columnIndex) {

        case 0:
            return "ID";
        case 1:
            return "Summary";
        case 2:
            return "Description";
        case 3:
            return "Done";
        case 4:
            return "Due Date";
        }
        return "";
    }
    
    @Override
    public int getColumnCount() {
        return 5;
    }
    
    @Override
    public int getRowCount() {
        return 1;
    }

    @Override
    public void setDataValue(int columnIndex, int rowIndex, Object newValue) {
        throw new UnsupportedOperationException("Setting data values to the header is not supported");
        
    }

}

1.2. Validate

When running the application the part with editing support should look similar to this:

simple editor

2. Exercise: Add more advanced editing support to a NatTable

In this exercise editing support of the table is enhanced to use different cell editors for each cell.

2.1. Creating a part with a NatTable

Copy the NatTableEditorExample to a new class called NatTableAdvancedEditorExample. Add this as a new part called to the application model.

You can reuse the code from the NatTableEditorExample of a former exercise.

For the NatTableEditorExample an anonymous inner class of an AbstractRegistryConfiguration was used to apply basic editing capabilities.

Now that the configuration becomes more complex it is better to encapsulate it in another class:

package com.vogella.nattable.parts;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Locale;

import org.eclipse.nebula.widgets.nattable.config.AbstractRegistryConfiguration;
import org.eclipse.nebula.widgets.nattable.config.CellConfigAttributes;
import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry;
import org.eclipse.nebula.widgets.nattable.config.IEditableRule;
import org.eclipse.nebula.widgets.nattable.data.convert.DefaultBooleanDisplayConverter;
import org.eclipse.nebula.widgets.nattable.data.convert.DefaultDateDisplayConverter;
import org.eclipse.nebula.widgets.nattable.edit.EditConfigAttributes;
import org.eclipse.nebula.widgets.nattable.edit.editor.CheckBoxCellEditor;
import org.eclipse.nebula.widgets.nattable.edit.editor.ComboBoxCellEditor;
import org.eclipse.nebula.widgets.nattable.edit.editor.DateCellEditor;
import org.eclipse.nebula.widgets.nattable.edit.editor.TextCellEditor;
import org.eclipse.nebula.widgets.nattable.layer.cell.ColumnLabelAccumulator;
import org.eclipse.nebula.widgets.nattable.painter.cell.CheckBoxPainter;
import org.eclipse.nebula.widgets.nattable.painter.cell.ComboBoxPainter;
import org.eclipse.nebula.widgets.nattable.style.CellStyleAttributes;
import org.eclipse.nebula.widgets.nattable.style.DisplayMode;
import org.eclipse.nebula.widgets.nattable.style.HorizontalAlignmentEnum;
import org.eclipse.nebula.widgets.nattable.style.Style;

public class EditConfiguration extends AbstractRegistryConfiguration {

    @Override
    public void configureRegistry(IConfigRegistry configRegistry) {
        configRegistry.registerConfigAttribute(EditConfigAttributes.CELL_EDITABLE_RULE, IEditableRule.ALWAYS_EDITABLE);

        registerEditors(configRegistry);
    }

    private void registerEditors(IConfigRegistry configRegistry) {
        registerIdEditor(configRegistry, 0);
        registerLastNameEditor(configRegistry, 1);
        registerGenderEditor(configRegistry, 2);
        registerMarriedEditor(configRegistry, 3);
        registerBirthdayEditor(configRegistry, 4);
    }

    private void registerIdEditor(IConfigRegistry configRegistry, int columnIndex) {
        // register a TextCellEditor for column two that commits on key up/down
        // moves the selection after commit by enter

        Style cellStyle = new Style();
        cellStyle.setAttributeValue(CellStyleAttributes.HORIZONTAL_ALIGNMENT, HorizontalAlignmentEnum.RIGHT);
        configRegistry.registerConfigAttribute(CellConfigAttributes.CELL_STYLE, cellStyle, DisplayMode.NORMAL,
                ColumnLabelAccumulator.COLUMN_LABEL_PREFIX + columnIndex);

        configRegistry.registerConfigAttribute(EditConfigAttributes.CELL_EDITOR, new TextCellEditor(true, true),
                DisplayMode.NORMAL, ColumnLabelAccumulator.COLUMN_LABEL_PREFIX + columnIndex);

        // configure to open the adjacent editor after commit
        configRegistry.registerConfigAttribute(EditConfigAttributes.OPEN_ADJACENT_EDITOR, Boolean.TRUE,
                DisplayMode.EDIT, ColumnLabelAccumulator.COLUMN_LABEL_PREFIX + columnIndex);
    }
    private void registerLastNameEditor(IConfigRegistry configRegistry, int columnIndex) {
        // register a TextCellEditor for column two that commits on key up/down
        // moves the selection after commit by enter

        Style cellStyle = new Style();
        cellStyle.setAttributeValue(CellStyleAttributes.HORIZONTAL_ALIGNMENT, HorizontalAlignmentEnum.LEFT);
        configRegistry.registerConfigAttribute(CellConfigAttributes.CELL_STYLE, cellStyle, DisplayMode.NORMAL,
                ColumnLabelAccumulator.COLUMN_LABEL_PREFIX + columnIndex);

        configRegistry.registerConfigAttribute(EditConfigAttributes.CELL_EDITOR, new TextCellEditor(true, true),
                DisplayMode.NORMAL, ColumnLabelAccumulator.COLUMN_LABEL_PREFIX + columnIndex);

        // configure to open the adjacent editor after commit
        configRegistry.registerConfigAttribute(EditConfigAttributes.OPEN_ADJACENT_EDITOR, Boolean.TRUE,
                DisplayMode.EDIT, ColumnLabelAccumulator.COLUMN_LABEL_PREFIX + columnIndex);
    }

    private void registerGenderEditor(IConfigRegistry configRegistry, int columnIndex) {
        ComboBoxCellEditor comboBoxCellEditor = new ComboBoxCellEditor(Arrays.asList("Done", "Not Done"));
        configRegistry.registerConfigAttribute(EditConfigAttributes.CELL_EDITOR, comboBoxCellEditor, DisplayMode.EDIT,
                ColumnLabelAccumulator.COLUMN_LABEL_PREFIX + columnIndex);

        configRegistry.registerConfigAttribute(CellConfigAttributes.CELL_PAINTER, new ComboBoxPainter(),
                DisplayMode.NORMAL, ColumnLabelAccumulator.COLUMN_LABEL_PREFIX + columnIndex);
    }

    private void registerMarriedEditor(IConfigRegistry configRegistry, int columnIndex) {
        configRegistry.registerConfigAttribute(EditConfigAttributes.CELL_EDITOR, new CheckBoxCellEditor(),
                DisplayMode.EDIT, ColumnLabelAccumulator.COLUMN_LABEL_PREFIX + columnIndex);

        // The CheckBoxCellEditor can also be visualized like a check button
        configRegistry.registerConfigAttribute(CellConfigAttributes.CELL_PAINTER, new CheckBoxPainter(),
                DisplayMode.NORMAL, ColumnLabelAccumulator.COLUMN_LABEL_PREFIX + columnIndex);

        // using a CheckBoxCellEditor also needs a Boolean conversion to work
        // correctly
        configRegistry.registerConfigAttribute(CellConfigAttributes.DISPLAY_CONVERTER,
                new DefaultBooleanDisplayConverter(), DisplayMode.NORMAL,
                ColumnLabelAccumulator.COLUMN_LABEL_PREFIX + columnIndex);
    }

    private void registerBirthdayEditor(IConfigRegistry configRegistry, int columnIndex) {
        DateCellEditor dateCellEditor = new DateCellEditor();
        configRegistry.registerConfigAttribute(EditConfigAttributes.CELL_EDITOR, dateCellEditor, DisplayMode.EDIT,
                ColumnLabelAccumulator.COLUMN_LABEL_PREFIX + columnIndex);

        DateFormat formatter = DateFormat.getDateInstance(DateFormat.MEDIUM, Locale.getDefault());
        String pattern = ((SimpleDateFormat) formatter).toPattern();

        // using a DateCellEditor also needs a Date conversion to work correctly
        configRegistry.registerConfigAttribute(CellConfigAttributes.DISPLAY_CONVERTER,
                new DefaultDateDisplayConverter(pattern), DisplayMode.NORMAL,
                ColumnLabelAccumulator.COLUMN_LABEL_PREFIX + columnIndex);
    }

}

In the EditConfiguration class different cell editors are applied and configured for the different columns.

In the AdvancedEditor implementation this EditConfiguration has to be added to the NatTable configuration.

natTable.addConfiguration(new EditConfiguration());

And since the columns in the EditConfiguration class are references by labels like this ColumnLabelAccumulator.COLUMN_LABEL_PREFIX + columnIndex a ColumnLabelAccumulator has to be applied to the body data layer.

DefaultGridLayer gridLayer = new DefaultGridLayer(dataProvider, headerDataProvider);

ColumnLabelAccumulator columnLabelAccumulator = new ColumnLabelAccumulator(dataProvider);
((DataLayer) gridLayer.getBodyDataLayer()).setConfigLabelAccumulator(columnLabelAccumulator);

So the final AdvancedEditor class should look similar to this:

package com.vogella.nattable.parts;

import java.util.List;

import jakarta.annotation.PostConstruct;

import org.eclipse.nebula.widgets.nattable.NatTable;
import org.eclipse.nebula.widgets.nattable.config.DefaultNatTableStyleConfiguration;
import org.eclipse.nebula.widgets.nattable.data.IColumnPropertyAccessor;
import org.eclipse.nebula.widgets.nattable.data.ListDataProvider;
import org.eclipse.nebula.widgets.nattable.grid.layer.DefaultGridLayer;
import org.eclipse.nebula.widgets.nattable.layer.DataLayer;
import org.eclipse.nebula.widgets.nattable.layer.cell.ColumnLabelAccumulator;
import org.eclipse.swt.widgets.Composite;

import com.vogella.tasks.model.Task;
import com.vogella.tasks.model.TaskService;

public class NatTableAdvancedExample {

    @PostConstruct
    public void postConstruct(Composite parent, TaskService taskService) {
        List<Task> tasks = taskService.getAll();
        IColumnPropertyAccessor<Task> columnPropertyAccessor = new TaskColumnPropertyAccessor();
        ListDataProvider<Task> dataProvider = new ListDataProvider<>(tasks, columnPropertyAccessor);

        TaskHeaderDataProvider headerDataProvider = new TaskHeaderDataProvider();

        DefaultGridLayer gridLayer = new DefaultGridLayer(dataProvider, headerDataProvider);

        ColumnLabelAccumulator columnLabelAccumulator = new ColumnLabelAccumulator(dataProvider);
        ((DataLayer) gridLayer.getBodyDataLayer()).setConfigLabelAccumulator(columnLabelAccumulator);

        NatTable natTable = new NatTable(parent, gridLayer, false);
        natTable.addConfiguration(new DefaultNatTableStyleConfiguration());
        natTable.addConfiguration(new EditConfiguration());

        natTable.configure();
    }

}

2.2. Validate

When running the application the Advanced Editor part should look similar to this:

advanced editor