Jump to: navigation, search

JFace Data Binding/The New Binding API

The existing API for the JFace Data Binding is showing its age. A new API is now available. The same classes for observables, properties, etc are the same. It is only the way the observables are bound together that is different.

The DataBindingContext class is no longer used. Instead use the new Bind class.

Basic Usage

To bind a model observable (IObservableValue<T> modelObservable) to a target observable (ObservableValue<T> targetObservable), use code as follows:

Bind.twoWay(modelObservable).to(targetObservable)

This will set up two way binding between the model and the target, with the initial value from the model being initially set into the target. If you want one way binding from the model to the target then use the following:

Bind.oneWay(modelObservable).to(targetObservable)

If you want to validate values going from the target to the model then you can insert a validator (IValidator<T> myValidator) as follows:

Bind.twoWay(modelObservable).validate(myValidator).to(targetObservable)

Notice how methods are chained together. You start the chain by calling either the static method Bind.oneWay or the static method Bind.twoWay, passing the model observable. These two methods return respectively, an IOneWayBinding or an ITwoWayBinding implementation. Both of these interfaces contain various methods for validations, conversions, and other operations. These methods in general return an IOneWayBinding or an ITwoWayBinding implementation, so you can chain operations together. Finally you call the to method passing the target observable.

Conversions

A value may be converted during data binding by including the convert method in the chain. For example, to bind a model observable (IObservableValue<T1> modelObservable) to a target observable (ObservableValue<T2> targetObservable) using a converter (IBidiConverter<T1,T2> myConverter), use code as follows:

Bind.twoWay(modelObservable).convert(myConverter).to(targetObservable)

For one-way binding, the convert method uses the old IConverter interface which has one method and converts one way. For two-way binding, the convert method uses the new IBidiConverter interface. This interface has two methods, modelToTarget and targetToModel. So, instead of having to supply two converters as with the old DataBindingContext, we supply one converter that can convert both ways.

The converter can also perform validation. Validation done inside a converter should be restricted to validating that the value can be converted. For example, a converter that converts between String and Integer should, when converting from String to Integer, check only that the string value can in fact be converted to an integer.

This keeps the usage simpler because it is not then necessary to separately specify a validator. This is more robust because the user cannot forget to validate a value before converting it. Often the converter would check that a value is valid as it is converting the value, so validation was effectively done twice. Take for example the conversion of a String to a URL. The only easy way of validating that a string is a valid URL is to do the conversion. The URL code will check the URL over the Internet and is quite expensive. We really don't want to have to do that twice.

Bind instances

The above examples have all used static methods in the Bind class. However sometimes we need to group bindings together. For example, the dispose method in the old DataBindingContext class disposes all bindings in the context. To support the same concept, Bind allows the creation of a Bind instance to give context.

For example:

Bind bindingContext = Bind.createContext(); bindingContext.twoWay(modelObservable1).to(targetObservable1); bindingContext.twoWay(modelObservable2).to(targetObservable2); bindingContext.twoWay(modelObservable3).to(targetObservable3);

...

bindingContext.dispose();

Transaction Bind instance

Sometimes you don't want changes to go from the target to the model immediately. For example if the target controls are in a dialog box then you may want the values to be set in the model only if and when the user presses the 'OK' button. Another example would be an editor part where you don't want parts outside the editor to see the changes until the editor has been saved. To do this you create a 'transaction' binding context:

Bind bindingContext = Bind.createTransactionalContext(); bindingContext.twoWay(modelObservable1).to(targetObservable1); bindingContext.twoWay(modelObservable2).to(targetObservable2); bindingContext.twoWay(modelObservable3).to(targetObservable3);

Changes to the model will now be set in the target but changes to the target are not immediately set in the model. When changes need to be set in the model (for example when the user presses the 'OK' button in a dialog or the user saves an editor) then the following code should be called:

bindingContext.commit();

If commit is never called (for example the user pressed the 'Cancel' button) then the model is never updated.

The transactional binding context also has an isDirty method. This method returns an IObservableValue<Boolean> that can be bound to the enablement of the 'OK' button in a dialog or the dirty state of an editor.

@See