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.
DataBindingContext class is no longer used. Instead use the new
Bind class. This code is currently under review in the e4 project (https://git.eclipse.org/r/#/c/15202/). You can get it as follows:
git fetch git://git.eclipse.org/gitroot/e4/org.eclipse.e4.databinding refs/changes/02/15202/3 && git checkout FETCH_HEAD
To bind a model observable (IObservableValue<T> modelObservable) to a target observable (ObservableValue<T> targetObservable), use code as follows:
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:
If you want to validate values going from the target to the model then you can insert a validator (IValidator<T> myValidator) as follows:
Notice how methods are chained together. You start the chain by calling either the static method
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.
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:
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,
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
Integer should, when converting from
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.
Further proposed work
The above features are already implemented and are available for use using the instructions at the top of this page.
Further proposed work is documented here
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.
Bind bindingContext = Bind.createContext();
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();
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:
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.