Databind and validate a TableViewer?
Asked Answered
C

3

5

I use the org.eclipse.core.databinding framework to bind some Text fields in an SWT application. I add an update strategy to validate the data and to set the value on the model only when the user click on the save button:

    UpdateValueStrategy toModel = new UpdateValueStrategy(UpdateValueStrategy.POLICY_CONVERT);
    if (validator != null) {
        toModel.setAfterGetValidator(validator);
    }

    UpdateValueStrategy fromModel = new UpdateValueStrategy(UpdateValueStrategy.POLICY_UPDATE);

    binding = bindingContext.bindValue(SWTObservables.observeText(this, SWT.Modify),
                    BeansObservables.observeValue(pVO, propertyName), toModel, fromModel);

This piece of code works really well.

But how can I do the same on a TableViewer?

I want it to work so that when I add something in the IHM, the model stay unchanged until I call getBindingContext().updateModels();

Continuum answered 29/7, 2010 at 15:18 Comment(0)
R
7

You do not need use the JFace Databinding Framework in TableViewer. Manipulation the structured data is simpler then SWT controls, such TableViewer, ListViewer and TreeViewer. You can use those viewer in the same way:

  • create viewer
  • set content provider
  • set label provider (suggested)
  • set filter (optional)
  • set sorter (optional)

After the viewer created, just invoke viewer.setInput(data) to put all the things to your viewer.

There are a list of model:

TableViewer tableViewer = new TableViewer(parent); 

Table table = tableViewer.getTable(); 
table.setHeaderVisible(true);      
table.setLinesVisible(true);`

for (int i = 0; i < COLUMN_NAMES.length; i++) {
    TableColumn tableColumn = new TableColumn(table, SWT.LEFT);
    tableColumn.setText(COLUMN_NAMES[i]);
    tableColumn.setWidth(COLUMN_WIDTHS[i]);
}

tableViewer.setContentProvider(new ModelContentProvider());
tableViewer.setLabelProvider(new ModelLabelProvider());
tableViewer.setInput(models);

The magic happens in the content provider:

class ModelContentProvider implements IStructuredContentProvider {

    @SuppressWarnings("unchecked")
    @Override
    public Object[] getElements(Object inputElement) {
        // The inputElement comes from view.setInput()
        if (inputElement instanceof List) {
            List models = (List) inputElement;
            return models.toArray();
        }
        return new Object[0];
    }

/* ... other methods */

}

Each model will become a TableItem and the model in the TableItem(item.getData()).

However, a table composed by many columns, you need the LabelProvider to help you mapping the property of model to the TableItem:

class ModelLabelProvider extends LabelProvider implements
        ITableLabelProvider {

    @Override
    public Image getColumnImage(Object element, int columnIndex) {
        // no image to show
        return null;
    }

    @Override
    public String getColumnText(Object element, int columnIndex) {
        // each element comes from the ContentProvider.getElements(Object)
        if (!(element instanceof Model)) {
            return "";
        }
        Model model = (Model) element;
        switch (columnIndex) {
        case 0:
            return model.getFoo();
        case 1:
            return model.getBar();
        default:
            break;
        }
        return "";
    }
}

The propagation of models to viewer is easy. If you will propagate viewer to the binded model, using the CellEditor is simple as well. To use CellEditor, you need set the column properties, cell editors and cell modifier to TableViewer:

tableViewer.setColumnProperties(COLUMNS_PROPERTIES);
tableViewer.setCellEditors(new CellEditor[] {
        new TextCellEditor(table), new TextCellEditor(table) });
tableViewer.setCellModifier(new ModelCellModifier(tableViewer));

The CellModifier likes this:

class ModelCellModifier implements ICellModifier {
    TableViewer viewer;

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

    @Override
    public boolean canModify(Object element, String property) {
        // property is defined by viewer.setColumnProperties()
        // allow the FOO column can be modified.
        return "foo_prop".equals(property);
    }

    @Override
    public Object getValue(Object element, String property) {
        if ("foo_prop".equals(property)) {
            return ((Model) element).getFoo();
        }
        if ("bar_prop".equals(property)) {
            return ((Model) element).getBar();
        }
        return "";
    }

    @Override
    public void modify(Object element, String property, Object value) {
        if ("foo_prop".equals(property)) {
            TableItem item = (TableItem) element;
            ((Model) item.getData()).setFoo("" + value);

            // refresh the viewer to show the changes to our user.
            viewer.refresh();
        }
    }
}

Everything is simple but there are many steps to make all together.

Rotenone answered 13/8, 2010 at 7:15 Comment(2)
...but why would you want to give up on databinding and return to writing lots of boilerplate code?Presidentship
Small comment: tableViewer.setColumnProperties(COLUMNS_PROPERTIES); means that in COLUMN_PROPERTIES you have to give all column names - or just use COLUMN_NAMES instead.Passionless
C
2

Use ViewerSupport:

TableViewer tableViewer = ...
IObservableList tableElements = ...
IValueProperty[] columnProperties = ...
ViewerSupport.bind(tableViewer, tableElements, columnProperties);
Coates answered 9/12, 2010 at 23:55 Comment(1)
This seems like a good solution. But is there some way to use validators and converters with a table viewer with this?Sanction
H
0

i agree with qualidafial.

Snippet017TableViewerWithDerivedColumns from the jface.databinding snippets is a full example of this.

Harvell answered 5/1, 2011 at 7:11 Comment(1)
The snippet doesn't seem to be available any more. Here is an alternative: wiki.eclipse.org/JFace_Data_Binding/Snippets#ViewersHoneysuckle

© 2022 - 2024 — McMap. All rights reserved.