Eclipse Databinding + Validation + Decoration

When it comes to databinding, a very common use case is to decorate form fields with little markers that indicate the state of the control. The following example explains how to do this with Eclipse databinding and JFace. Here is a screenshot:

Validation Decoration

The code to create an error decoration for a control is pretty straight forward:

ControlDecoration controlDecoration = new ControlDecoration(
    control, SWT.LEFT | SWT.TOP);
controlDecoration.setDescriptionText(hoverText);
FieldDecoration fieldDecoration = FieldDecorationRegistry
    .getDefault().getFieldDecoration(
         FieldDecorationRegistry.DEC_ERROR);
controlDecoration.setImage(fieldDecoration.getImage());

After creating the error decoration, you can implement a databindig validator that checks the condition and hides or shows the decoration accordingly:

class StringRequiredValidator implements IValidator {

    private final String errorText;
    private final ControlDecoration controlDecoration;

    public StringRequiredValidator(String errorText,
        ControlDecoration controlDecoration) {
        super();
        this.errorText = errorText;
        this.controlDecoration = controlDecoration;
    }

    public IStatus validate(Object value) {
        if (value instanceof String) {
            String text = (String) value;
            if (text.trim().length() == 0) {
                controlDecoration.show();
                return ValidationStatus
                        .error(errorText);
            }
        }
        controlDecoration.hide();
        return Status.OK_STATUS;
    }
}

Now you can bind a model property to a text field with an UpdateStrategy using your validator:

dataBindingContext.bindValue(
    SWTObservables.observeText(firstNameText, SWT.Modify),
    PojoObservables.observeValue(person, "firstName"),
    new UpdateValueStrategy()
        .setAfterConvertValidator(new StringRequiredValidator(
             "Please enter first name",
              firstNameDecoration)),
    null);

If you want to use (existing) validators without handing the control decoration over, you could create a map for controls and their corresponding decorators, and then use an AggregateValidationStatus to control the decorators:

AggregateValidationStatus aggregateValidationStatus
 = new AggregateValidationStatus(
  dataBindingContext.getBindings(),
     AggregateValidationStatus.MAX_SEVERITY);

  agregateValidationStatus
   .addChangeListener(new IChangeListener() {
    public void handleChange(ChangeEvent event) {
      for (Object o : dataBindingContext.getBindings()) {
        Binding binding = (Binding) o;
        IStatus status = (IStatus) binding
            .getValidationStatus().getValue();
        Control control = null;
        if (binding.getTarget() instanceof ISWTObservable) {
          ISWTObservable swtObservable =
             (ISWTObservable) binding.getTarget();
          control = (Control) swtObservable.getWidget();
        }
        ControlDecoration decoration
          = decoratorMap.get(control);
        if (decoration != null) {
          if (status.isOK()) {
            decoration.hide();
          } else {
            decoration
                .setDescriptionText(status.getMessage());
            decoration.show();
          }
        }
      }
    }
});

You can download an Eclipse project with the running examples here: Binding Decoration Examples. I also implemented this in my RCP demo project MP3 Manager.

Have Fun!

Kai

This Post Has 19 Comments

  1. Peter Friese

    Kai,
    I find it amazing how much code is needed to “just” control this tiny bit of UI… And from looking at the code, you can’t just tell what it really is doing – you have to wade through it to understand what is going on. The intention of the code gets obfuscated by the API. Which, of course, is not your fault 🙂
    I think it’s really sad that modern APIs are not geting more intentional…
    Peter

  2. Kai Tödter

    Hi Peter,

    The crux with these kind of tiny examples is, that the domain model often is so small that it seems to be overhead to use databinding at all. In real word applications I really appreciate the flexibility of Eclipse databinding. It is very easy to create a domain specific framework that does exactly what it is supposed to do. But I would agree with you regarding the API support of such common use cases 🙂

  3. Reiner

    Hi,

    > I really appreciate the flexibility of Eclipse databinding

    It is flexible. But flexibility has a price. It’s called complexity.

    Peter Friese is right. JFace-databinding can be very very complicated and it is ultra-verbose so-to-say. We need more convenience-classes. JFace-databinding should be as easy as possible. Think of Microsoft very easy understandable databindings for .NET or Adobe Flex.

    Databinding will be used by corporate-developers and don’t expect that these “corporate guys” are interested to make week-long crash-courses for JFace-API. They expect something as simple as possible and easy-to-use.

    Please make JFace-databinding easier. Please!

    I have written myself some convenience-classes for TableViewer-binding. And I did it because the complexity of JFace-databinding and the boiler-plate code you need makes your software unmaintainable and very difficult to read. In other words: the complexity of JFace-databinding can kill you! Try to read and understand some code using raw JFace-databinding and be sure to go to hell.

    So please: make JFace-databinding easier. Make it more intuitive. And don’t let corporate-developers become system-specialists.

    By the way: there will be no Swing-databinding in Java7 so the Eclipse-foundation could provide something usable for the Swing-community.

  4. Chris Aniszczyk

    I agree that the databinding API is very verbose and flexible… but that has its advantages. If you’re looking at an abstraction on top of databinding… see what the Riena guys are working on with Ridgets:

    http://wiki.eclipse.org/Riena_Snippets

  5. Gerald Rosenberg

    Agreeing with Peter and Reiner, I think we need to go the ‘other’ way. JFace-DataBinding (and Riena) add complexity to the view. Instead, binding needs to be one-liners for typical uses *and* separate from the view. Here is a basic wrapper I use: http://www.certiv.net/download/Bind.java

    Implementation is just:

    // create the view
    form = new RepositoryForm(parent, SWT.NONE);

    // construct the context
    dbc = new DataBindingContext();

    // build the presentation model containing the data bean
    presModel = new RepositoryPresModel();

    // Create an observable to observe changes in the view model
    IObservableValue repValue = BeansObservables.observeValue(presModel, RepositoryPresModel.REPOSITORY);

    // Bind the fields to the current observable
    Bind.bind(dbc, form.getTextID(), repValue, Repository.ID, Long.class);
    Bind.bind(dbc, form.getTextRepName(), repValue, Repository.REP_NAME);
    Bind.bind(dbc, form.getCBoxActive(), repValue, Repository.ACTIVE);
    Bind.bind(dbc, form.getTextRepSysUrl(), repValue, Repository.REP_SYS_URL);
    Bind.bind(dbc, form.getDate(), repValue, Repository.DATE, Timestamp.class);

    Validation can be added using similar one-liners.

    Flexibility is great, but desired binding behaviors should be consistent within a single application. If controlled variability were needed, it would be better set it as a “policy” on the Bind class rather than polluting the view with lots of intricate binding code on a per field basis.

    Change the JFace-DataBinding API? No!. Add some ‘well-thought-out’ convenience classes to drastically simplify 90% of typical use cases, yes.

  6. Tom Schindl

    There’s Databinding for Swing available from the UFaceKit-Project at google and we are just migrating our code to Eclipse.

  7. Mike Evans

    Another attempt at wrapping the complexities of databinding behind more friendly convenience classes can be found at http://sourceforge.net/projects/jfacefactories/.

    There is some code I put together to support a talk at EclipseCon last April. See the ‘Quick Start’ link on the associated website for a 3 minute overview.

    Note that the code should be treated as ALPHA, though it is based on production code I wrote for a client.

  8. Matt Barry

    Thanks for the blog post. To help out and save some time for the next person who tries to learn from and build the examples, here are the java build path libraries I used for a standalone app:

    com.ibm.icu_*
    org.eclipse.core.commands_*
    org.eclipse.core.databinding_*
    org.eclipse.core.databinding.beans_*
    org.eclipse.core.runtime_*
    org.eclipse.equinox.common_*
    org.eclipse.jface_*
    org.eclipse.jface.databinding_*
    org.eclipse.jface.text_*
    org.eclipse.osgi_*
    swt.jar
    JRE_*

  9. Kai Schlamp

    Hy Kai,

    I am not sure if the code with the AggregateValidationStatus really works in the way it is presented on your blog (on the other hand I didn’t check the whole example code you provided for download).
    The problem is that if the method aggregateValidationStatus.getValue() is not called, the handleChange method of the change listener is only called once and after that no more. If this is done in your full example code, then this is just a hint for some that wonder (like me) why their AggregateValidationStatus does not work as expected.
    See http://www.eclipse.org/newsportal/article.php?id=80171&group=eclipse.platform#80171

  10. Kai Tödter

    Kai (Schlamp),

    thanks for then hint. In the example code it is working but I have some other RCP projects that behave like you discribe (at least with 3.4.2). My hack is to invoke aggregateValidationStatus.getValue() in the handleChange method of the event listener, then the handleChange method is called every time. Actually I found your answer after googling for this kind of problem, so thanks a lot 🙂

  11. Yves YANG

    XWT resolves this problem of complexity in declarative UI and has added resource management, data context management and dynamic UI based on Data Binding.

  12. Ralf Ebert

    Try ControlDecorationSupport (provisional API):

    Binding binding = ctx.bindValue(SWTObservables.observeText(text, SWT.Modify), BeansObservables.observeValue(modelObject, “value”));
    ControlDecorationSupport.create(binding, SWT.RIGHT|SWT.TOP);

  13. Roman Legat

    A problem I see here is that the validators are only called on “set”, that is, when the user enters data. A typical situation would be the insertion of new data, e.g. adding a user. The form is opened, all fields are empty, user presses “Save”. Since he hasn’t entered anything, the validators weren’t called and there is no display of the ControlDecoration.

    What would be a good solution for this? Call the validators manually on save?

  14. Chris

    @Roman Legat
    Your question is from January, some time ago, nevertheless my repsones today:

    Yes, call them manually:
    dataBindingContext.updateModels();
    to trigger all validators known to Binding.

  15. ender

    @Peter Friese
    there is other way to do the same jgoodies libraries, it allows to do the same more simply and using much less code.

  16. Lasantha

    This code is really help me to sort out my requirement. I just need only the first part of code to sort out my problem. No need many code as explain above.

    uiElement: element you want to show the icon and message on hover.
    errorMessage: error you want to display on hover.

    ControlDecoration controlDecoration = new ControlDecoration( uiElement, SWT.LEFT | SWT.TOP);
    controlDecoration.setDescriptionText(errorMessage);
    FieldDecoration fieldDecoration = FieldDecorationRegistry.getDefault().getFieldDecoration(FieldDecorationRegistry.DEC_ERROR);
    controlDecoration.setImage(fieldDecoration.getImage());
    controlDecoration.show();

    Simply what you want to do is execute the above code on a condition.

    Thanks lot to author.

Leave a Reply

I accept the Privacy Policy