Jump to: navigation, search

JFace Data Binding/Getting started

This page shows you how to create a simple example application using data binding that you can play with.

Setup

  1. Download and install the Eclipse 3.3 SDK.
  2. Start the Eclipse SDK and select a new workspace directory. Close the Welcome view - the Package Explorer should now be showing but empty.
  3. Create a new plug-in project: Hit Ctrl+3, enter 'npip' (just the letters, not the quotes), hit Return.
  4. Enter a project name, for example 'GettingStarted'.
  5. Keep the default options (Target: Eclipse 3.3, plug-in will make contributions to UI, no RCP application) and click the Finish button.
  6. In the plug-in manifest editor, switch to the 'Dependencies' tab.
  7. Under 'Required Plug-ins', click 'Add...'.
  8. In the dialog, type '*databinding'. (Note the leading wildcard character.)
  9. Multi-select org.eclipse.core.databinding, org.eclipse.core.databinding.beans, and org.eclipse.jface.databinding, and click 'OK'.
  10. Save and then close the plug-in manifest editor.
  11. Create a new Java package (File > New > Package) and pick a name for it, e.g. 'starting'.
  12. Create a new Java class (File > New > Class) in that package, called 'GettingStarted'.

Example Code

Copy the following example code - consisting of three parts - into the new class. Use 'Source > Organize Imports' to get the appropriate import statements (make sure to select the SWT types for Text, Button, and Label, and to select org.eclipse.core.databinding.observable.Realm).

Save the file and then select 'Run > Run As > Java Application'. Enter a numeric value in the text field and observe how the label is updated automatically. Click on the button to double the amount. You can also try entering a non-numeric value.

public class GettingStarted {

	static Model model = new Model();
	
	static void init(Shell shell) {
		Text text = new Text(shell, SWT.BORDER);
		Label label = new Label(shell, SWT.NONE);

		Button button = new Button(shell, SWT.PUSH);
		button.setText("Double!");
		button.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				model.setAmount(model.getAmount() * 2);
			}
		});
		
		DataBindingContext dbc = new DataBindingContext();

		IObservableValue modelObservable = BeansObservables.observeValue(model, "amount");

		dbc.bindValue(SWTObservables.observeText(text, SWT.Modify), modelObservable, null, null);
		dbc.bindValue(SWTObservables.observeText(label), modelObservable, null, null);
		
		GridLayoutFactory.swtDefaults().generateLayout(shell);
	}

The above code assumes that a SWT Shell has already been created, and that there is a model object with an 'amount' property. Both will be implemented in the remaining two code pieces.

A text widget, a label, and a button are created within the shell. When the button is clicked, the model's amount will be doubled, using the getter and setter methods supported by the model object.

A data binding context is created. This is an object that will hold on to the bindings that you create. Bindings can be created between observable objects. In our example, we create one observable for our model object's property, and two observables on the UI side, one for the text, and another one for the label. The two 'null' arguments are for configuring validators or converters; by passing null, we will get default validators and converters. Note that the model property is of a numeric type while the text widget holds strings. Clearly, some kind of conversion is needed here.

	static class Model {
		private PropertyChangeSupport changeSupport = new PropertyChangeSupport(this);
		public void addPropertyChangeListener(String propertyName,
				PropertyChangeListener listener) {
			changeSupport.addPropertyChangeListener(propertyName, listener);
		}
		public void removePropertyChangeListener(String propertyName,
				PropertyChangeListener listener) {
			changeSupport.removePropertyChangeListener(propertyName, listener);
		}
		private int amount = 0;
		public void setAmount(int newAmount) {
			int oldAmount = this.amount;
			this.amount = newAmount;
			changeSupport.firePropertyChange("amount", oldAmount, newAmount);
		}
		public int getAmount() {
			return amount;
		}
	}

This is a pretty basic model class that conforms to the JavaBeans specification by delegating listener management to a PropertyChangeSupport object. For convenience, it is implemented as a static inner class. You can easily add more properties to the model class, but don't forget to implement public getters and setters for them, and to make appropriate calls to changeSupport.firePropertyChange() from all setters.

	public static void main(String[] args) {
		final Display display = new Display();
		Realm.runWithDefault(SWTObservables.getRealm(display), new Runnable() {
			public void run() {
				Shell shell = new Shell(display);
				init(shell);
				shell.pack();
				shell.open();
				while (!shell.isDisposed()) {
					if (!display.readAndDispatch())
						display.sleep();
				}
			}
		});
		display.dispose();
	}
}

This is the standard SWT event loop with one complication - a SWT Realm is created and made the default realm for our application. Think of a Realm as an abstraction of SWT's UI thread. If everything in your application happens in the UI thread, you don't have to deal with Realms in your binding code. For more details on this, see the FAQ or the wiki page that explains in detail what a Realm is. If you are writing a plug-in for the Eclipse Platform, or a RCP application, you don't have to do this setup yourself - as of Eclipse 3.3, it is already part of the initialization code in PlatformUI.createAndRunWorkbench().

Validation Results

To see the results of the default validation, add the following to the GettingStarted class.

		Label errorLabel = new Label(shell, SWT.NONE);
		dbc.bindValue(SWTObservables.observeText(errorLabel),
				new AggregateValidationStatus(dbc.getBindings(),
						AggregateValidationStatus.MAX_SEVERITY), null, null);

This code adds another label for the validation message and binds it to an aggregated status obtained from all the bindings in the data binding context. You can also look at validation results for individual bindings if you keep a reference to the binding object returned from bindValue:

		// updated line follows:
		Binding b = dbc.bindValue(SWTObservables.observeText(text, SWT.Modify), modelObservable, null, null);

		Label individualErrorLabel = new Label(shell, SWT.NONE);
		dbc.bindValue(SWTObservables.observeText(individualErrorLabel), b.getValidationStatus(), null, null);

Note that b.getValidationStatus() returns an IObservableValue, not an IStatus object. It is a live value which can be observed; in this example, we are using it directly in another call to bindValue().

Custom Converters and Validators

To configure your own converters and/or validators instead of the default ones, you would pass an instance of UpdateValueStrategy for each direction (UI>Model, Model>UI) instead of the null arguments to bindValue():

		// this is just an example of configuring an existing validator and converter:
		dbc.bindValue(SWTObservables.observeText(text, SWT.Modify), modelObservable,
			// UI to model:
			new UpdateValueStrategy().setAfterConvertValidator(anIntValidator),
			// model to UI:
			new UpdateValueStrategy().setConverter(anIntToStringConverter));