JFace Data Binding/Original Design
Data binding allows easy linking of UI elements and models so that users can edit or view the data in the model. Traditionally, this is what you need to do to link, say, a text widget to a String property of a model object:
- Copy the current value of the model property to the text widget.
- Register listeners with the widget and the model object to propagate changes
made on one side to the other side.
The same basic principle applies to binding a list widget's contents to a collection property of a model object.
Using the data binding framework relieves you from writing and registering listeners yourself, and provides a common infrastructure for validation and conversion. It makes it easy to connect data sources to widgets such as text fields, combos, tables and trees, for viewing and editing. In real life scenarios, changes on one side are not always propagated to the other side right away - changes need to be validated, values converted, and different update policies followed. The data binding framework covers these more advanced aspects as well.
- Easy to use
- Make the default cases easy: For example, simple binding of a text widget should be one line of code.
- Should not require new or custom controls, or wrappering of existing controls: Keep the concern of creating the widgets separate. Ideally, the code for creating the widgets stays exactly the same, and only the setting of initial values and the registering of listeners is replaced by calls to the data binding framework.
- Enable lifecycle management: For example, disposing of a SWT composite should de-register all listeners and other helper objects used for binding controls in that composite.
- Keep binding code separate from code that creates the widgets: This lets users create widgets any way they want, including the use of a visual editor.
- Support pluggable validators and converters (but provide defaults): For example, provide default converters (formatters) and validators for Integer and floating point numbers.
- Allow to bind to different aspects of an object or a control: For example, binding to the enablement state of a control instead of its value, or in the case of the spinner widget, binding to the current selection, the min value, and the max value.
- Easily Extensible
- For supporting new or custom controls
- For supporting other data sources
In terms of the data binding framework, binding involves two updatable objects whose state or current value is kept in sync (see Figure 2). The updatable objects are called target and model, respectively. An Updatable object normalize access to a value, and to notifications when that value has changed. Often, but not always, the target updatable will represent a UI widget, and the model updatable will represent a model object or a property of a model object. At binding time, the current state of the model is copied to the target. After that, updates to both the target updatable and the model updatable will be tracked and propagated to the other updatable. The binding activity is to listen for changes on the target and model updatables, and utilize a converter and a validator to propagate these changes across.
For example, to bind a SWT text widget txtName to the "name" property of a model object mPerson, the framework creates an updatable value object (target) for txtName, and an updatable value object (model) for the "name" property of mPerson. In code, it looks like this:
IUpdatable target = dataBindingContext.createUpdatable(txtName); IUpdatable model = dataBindingContext.createUpdatable(new PropertyDescription(mPerson, "name")); dataBindingContext.bind(target, model, null);
Updatable objects are created from description objects using factories. Description objects are interpreted by factories that create updatable objects, the data binding framework does not impose any semantics on them. Implementers of IUpdatableFactory specify for which description objects they can create updatable objects.
The data binding context offers methods for binding on the level of description objects directly, so that the above example boils down to just one line of code:
dataBindingContext.bind(txtName, new PropertyDescription(mPerson, "name"), null);
The third argument of bind() is an optional bind spec object that can specify a custom validator or converter to be used.
Binding is always performed within a data binding context. The data binding context defines a common lifecycle (i.e. disposing) for the updatable objects created within it. Policy decisions can also be made for all bindings within a context, such as whether validation and updating should be performed automatically or not. Finally, you can register factories for creating updatable objects, validators, and converters with a data binding context. This is what makes the data binding framework extensible.
A binding can be created on the (lower) level of updatable objects directly, or on the (higher) level of description objects.