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:
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();
}
}
3. Links and Literature
3.1. vogella Java example code
If you need more assistance we offer Online Training and Onsite training as well as consulting