Jump to: navigation, search

Difference between revisions of "JFace Data Binding/Getting started"

(New page: == Getting startet == --~~~~ <p> The best you can do at this time, is to check out the examples from CVS for review. Eclipse provides easy CVS access. Just open the CVS Perspective (Window...)
 
m (Missing required plug-in added)
 
(6 intermediate revisions by 2 users not shown)
Line 1: Line 1:
== Getting startet ==
+
This page shows you how to create a simple example application using data binding that you can play with.
--[[User:Frank.schaare.gmail.com|Frank.schaare.gmail.com]] 19:03, 12 October 2007 (EDT)
+
=== Setup ===
<p>
+
# Download and install the Eclipse 3.3 SDK.
The best you can do at this time, is to check out the examples from CVS for review.
+
# Start the Eclipse SDK and select a new workspace directory. Close the Welcome view - the Package Explorer should now be showing but empty.
Eclipse provides easy CVS access. Just open the CVS Perspective (Window | Open Perspective | CVS) and add a new Repository Location.  
+
# Create a new plug-in project: Hit Ctrl+3, enter 'npip' (just the letters, not the quotes), hit Return.
When done, your settings should look like this:
+
# Enter a project name, for example 'GettingStarted'.
</p>
+
# Keep the default options (Target: Eclipse 3.3, plug-in will make contributions to UI, no RCP application) and click the Finish button.
[[Image:EclipseCVSSettings.JPG]]
+
# In the plug-in manifest editor, switch to the 'Dependencies' tab.
<p>Expand the HEAD branch and browse for '''org.eclipse.jface.examples.databinding'''. You can check out the package [RMB: Check out as] into your Workspace as a new Project or into an existing Project.</p>
+
# Under 'Required Plug-ins', click 'Add...'.
<p>When checked out, you'll find the examples under '''org.eclipse.jface.examples.databinding.snippets'''. Most of the examples provide a main method, you can run it as a Java Application to see what happens.</p>
+
# In the dialog, type '*databinding'. (Note the leading wildcard character.)
 +
# Multi-select <b>org.eclipse.core.databinding</b>, <b>org.eclipse.core.databinding.beans</b>, <b>org.eclipse.core.databinding.property</b>, <b>org.eclipse.jface.databinding</b>, and <b>com.ibm.icu</b>, and click 'OK'.
 +
# Save and then close the plug-in manifest editor.
 +
# Create a new Java package (File > New > Package) and pick a name for it, e.g. 'starting'.
 +
# 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).
  
<p>Note for '''RCP''' developers: You don't need to care for the realm Object. Since 3.3, <pre>Workbench#createAndRunWorkbench</pre> does this for you.</p>
+
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.
 +
<pre>
 +
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);
 +
}
 +
</pre>
 +
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.
 +
<pre>
 +
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;
 +
}
 +
}
 +
</pre>
 +
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.
 +
<pre>
 +
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();
 +
}
 +
}
 +
</pre>
 +
This is the standard SWT event loop with one complication - a SWT <i>Realm</i> 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 [[JFace Data Binding/FAQ|FAQ]] or the wiki page that explains in detail what a [[JFace Data Binding/Realm|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 <b>PlatformUI.createAndRunWorkbench()</b>.
 +
 
 +
=== Validation Results ===
 +
To see the results of the default validation, add the following to the GettingStarted class.
 +
<pre>
 +
Label errorLabel = new Label(shell, SWT.NONE);
 +
dbc.bindValue(SWTObservables.observeText(errorLabel),
 +
new AggregateValidationStatus(dbc.getBindings(),
 +
AggregateValidationStatus.MAX_SEVERITY), null, null);
 +
</pre>
 +
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:
 +
<pre>
 +
// 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);
 +
</pre>
 +
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():
 +
<pre>
 +
// 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));
 +
</pre>

Latest revision as of 09:18, 10 February 2010

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, org.eclipse.core.databinding.property, org.eclipse.jface.databinding, and com.ibm.icu, 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));