Eclipse Databinding可以在你的模型和用户界面之间建立一种关联。当任何一方(模型或用户界面)发生变化时,都可以同步到另一方。 另外你可以使用默认的或者是自定义的数据验证器(validator)来验证数据同时可以使用转换器(converter)在模型和界面之间转换数据。 下面我们用一个应用程序来演示Databinding功能。在这个程序中我们在界面中显示一个Person对象,用户可以在界面中修改Person对象的值。 也可以通过一个按钮来触发事件来直接修改模型。任何一端的变化都会同步到另一端。
创建一个名为“de.vogella.databinding.person.model”的插件项目。不要创建activator,不要选这“This plug-in will make contributions to th UI”,也不要选择“Do you want to create a rich client application”.
点击"Finish". 为了能够在用户界面和模型之间直接使用Databinding,模型必须实现PropertyChangeSupport接口。这样Eclipse Databinding框架就可以监听到在模型中的变化了。 创建一个名为 de.vogella.databinding.person.model 和如下的文件。
package de.vogella.databinding.person.model;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
public class Person implements PropertyChangeListener {
private String firstName;
private String lastName;
private boolean married;
private String gender;
private Integer age;
private Address address;
private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(
this);
public Person() {
}
public void addPropertyChangeListener(String propertyName,
PropertyChangeListener listener) {
propertyChangeSupport.addPropertyChangeListener(propertyName, listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.removePropertyChangeListener(listener);
}
public String getFirstName() {
return firstName;
}
public String getGender() {
return gender;
}
public String getLastName() {
return lastName;
}
public boolean isMarried() {
return married;
}
public void setFirstName(String firstName) {
propertyChangeSupport.firePropertyChange("firstName", this.firstName,
this.firstName = firstName);
}
public void setGender(String gender) {
propertyChangeSupport.firePropertyChange("gender", this.gender,
this.gender = gender);
}
public void setLastName(String lastName) {
propertyChangeSupport.firePropertyChange("lastName", this.lastName,
this.lastName = lastName);
}
public void setMarried(boolean isMarried) {
propertyChangeSupport.firePropertyChange("married", this.married,
this.married = isMarried);
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
propertyChangeSupport.firePropertyChange("age", this.age,
this.age = age);
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
address.addPropertyChangeListener("country", this);
propertyChangeSupport.firePropertyChange("address", this.address,
this.address = address);
}
@Override
public String toString() {
return firstName + " " + lastName;
}
@Override
public void propertyChange(PropertyChangeEvent event) {
propertyChangeSupport.firePropertyChange("address", null, address);
}
}
package de.vogella.databinding.person.model;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
public class Address {
private String street;
private String number;
private String postalCode;
private String city;
private String country;
private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(
this);
public void addPropertyChangeListener(String propertyName,
PropertyChangeListener listener) {
propertyChangeSupport.addPropertyChangeListener(propertyName, listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.removePropertyChangeListener(listener);
}
public Address() {
}
public Address(String postalCode, String city, String country) {
this.postalCode = postalCode;
this.city = city;
this.country = country;
}
public String getStreet() {
return street;
}
public void setStreet(String street) {
propertyChangeSupport.firePropertyChange("street", this.street,
this.street = street);
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
propertyChangeSupport.firePropertyChange("number", this.number,
this.number = number);
}
public String getPostalCode() {
return postalCode;
}
public void setPostalCode(String postalCode) {
propertyChangeSupport.firePropertyChange("postalCode", this.postalCode,
this.postalCode = postalCode);
}
public String getCity() {
return city;
}
public void setCity(String city) {
propertyChangeSupport.firePropertyChange("citry", this.city,
this.city = city);
this.city = city;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
propertyChangeSupport.firePropertyChange("country", this.country,
this.country = country);
}
public String toString() {
String s = "";
s += street != null ? street + " " : "";
s += number != null ? number + " " : "";
s += postalCode != null ? postalCode + " " : "";
s += city != null ? city + " " : "";
s += country != null ? country + " " : "";
return s;
}
}
打开 META-INF 文件夹中的 MANIFEST.MF 文件。选中"Runtime"选项页。把 de.vogella.databinding.person.model 加入到 Exported Packages 中。这样其他插件就可以访问到这个包了。
通过“Hello RCP”模板创建一个名为“de.vogella.databinding.person.swt”的Eclipse RCP项目(具体的可以参考 Create your first Eclipse RCP )。
把"org.eclipse.core.databinding", "org.eclipse.core.databinding.beans", "org.eclipse.jface.databinding" 和 "org.eclipse.core.databinding.property" 添加到当前项目的依赖中。
向当前RCP项目添加一个视图(view),并创建这个视图的实现类 “de.vogella.databinding.person.PersonView ” 把这个视图添加到透视图中去。(可以参考 Eclipse RCP Tutorial ) 实现代码如下所示
package de.vogella.databinding.person.swt;
import org.eclipse.core.databinding.DataBindingContext;
import org.eclipse.core.databinding.beans.BeanProperties;
import org.eclipse.core.databinding.beans.BeansObservables;
import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.jface.databinding.swt.SWTObservables;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Layout;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.part.ViewPart;
import de.vogella.databinding.person.model.Address;
import de.vogella.databinding.person.model.Person;
public class PersonView extends ViewPart {
public static final String ID = "de.vogella.databinding.person.swt.View";
private Person person;
private Text firstName;
private Text ageText;
private Button marriedButton;
private Combo genderCombo;
private Text countryText;
public PersonView() {
}
@Override
public void createPartControl(Composite parent) {
person = new Person();
Address address = new Address();
address.setCountry("Deutschland");
person.setAddress(address);
person.setFirstName("John");
person.setLastName("Doo");
person.setGender("Male");
person.setAge(12);
person.setMarried(true);
// Lets put thing to order
Layout layout = new GridLayout(2, false);
parent.setLayout(layout);
Label firstLabel = new Label(parent, SWT.NONE);
firstLabel.setText("Firstname: ");
firstName = new Text(parent, SWT.BORDER);
GridData gridData = new GridData();
gridData.horizontalAlignment = SWT.FILL;
gridData.grabExcessHorizontalSpace = true;
firstName.setLayoutData(gridData);
Label ageLabel = new Label(parent, SWT.NONE);
ageLabel.setText("Age: ");
ageText = new Text(parent, SWT.BORDER);
gridData = new GridData();
gridData.horizontalAlignment = SWT.FILL;
gridData.grabExcessHorizontalSpace = true;
ageText.setLayoutData(gridData);
Label marriedLabel = new Label(parent, SWT.NONE);
marriedLabel.setText("Married: ");
marriedButton = new Button(parent, SWT.CHECK);
Label genderLabel = new Label(parent, SWT.NONE);
genderLabel.setText("Gender: ");
genderCombo = new Combo(parent, SWT.NONE);
genderCombo.add("Male");
genderCombo.add("Female");
Label countryLabel = new Label(parent, SWT.NONE);
countryLabel.setText("Country");
countryText = new Text(parent, SWT.BORDER);
Button button1 = new Button(parent, SWT.PUSH);
button1.setText("Write model");
button1.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
System.out.println("Firstname: " + person.getFirstName());
System.out.println("Age " + person.getAge());
System.out.println("Married: " + person.isMarried());
System.out.println("Gender: " + person.getGender());
System.out.println("Country: "
+ person.getAddress().getCountry());
}
});
Button button2 = new Button(parent, SWT.PUSH);
button2.setText("Change model");
button2.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
person.setFirstName("Lars");
person.setAge(person.getAge() + 1);
person.setMarried(!person.isMarried());
if (person.getGender().equals("Male")) {
} else {
person.setGender("Male");
}
if (person.getAddress().getCountry().equals("Deutschland")) {
person.getAddress().setCountry("USA");
} else {
person.getAddress().setCountry("Deutschland");
}
}
});
// Now lets do the binding
bindValues();
}
@Override
public void setFocus() {
}
private void bindValues() {
// The DataBindingContext object will manage the databindings
DataBindingContext bindingContext = new DataBindingContext();
IObservableValue uiElement;
IObservableValue modelElement;
// Lets bind it
uiElement = SWTObservables.observeText(firstName, SWT.Modify);
modelElement = BeansObservables.observeValue(person, "firstName");
// The bindValue method call binds the text element with the model
bindingContext.bindValue(uiElement, modelElement, null, null);
uiElement = SWTObservables.observeText(ageText, SWT.Modify);
modelElement = BeansObservables.observeValue(person, "age");
// Remember the binding so that we can listen to validator problems
// See below for usage
bindingContext.bindValue(uiElement, modelElement, null, null);
uiElement = SWTObservables.observeSelection(marriedButton);
modelElement = BeansObservables.observeValue(person, "married");
bindingContext.bindValue(uiElement, modelElement, null, null);
uiElement = SWTObservables.observeSelection(genderCombo);
modelElement = BeansObservables.observeValue(person, "gender");
bindingContext.bindValue(uiElement, modelElement, null, null);
// Address field is bound to the Ui
uiElement = SWTObservables.observeText(countryText, SWT.Modify);
modelElement = BeanProperties.value("address.country").observe(person);
bindingContext.bindValue(uiElement, modelElement, null, null);
}
}
DataBindingContext对象用来管理绑定。通过调用bindValue方法将界面上的text元素和模型绑定。第一个参数是 uiElement他把一个更新策略封装到一个IObservableValue对象中,第二个参数和第一个参数一样唯一不同的是他封装的是一个模型对 象。可以通过最后两个参数把验证器(validator)和转换器(converter)传给DataBindingContext.如果传入的是 null,将会使用默认的。
JFace提供了一个高扩展的数据和表现分离的机制。下面将重点讨论如何在JFace中使用Eclipse Databinding。 关于JFace的例子可以参考 JFace Tables With Eclipse RCP
下面将演示一下在list(ListViewer),table(TableViewer)和tree (TreeViewer)viewer中使用Eclipse Databinding。
添加一个视图。实现类代码如下。
package de.vogella.databinding.person.listviewer;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.databinding.observable.list.WritableList;
import org.eclipse.jface.databinding.viewers.ObservableListContentProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ListViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.part.ViewPart;
import de.vogella.databinding.person.model.Person;
public class View extends ViewPart {
private ListViewer viewer;
private WritableList input;
@Override
public void createPartControl(Composite parent) {
// Just a little bit layout
parent.setLayout(new GridLayout(3, false));
// Define the viewer
viewer = new ListViewer(parent);
viewer.setContentProvider(new ObservableListContentProvider());
List<Person> persons = new ArrayList<Person>();
// Just for testing we create sample data
createExampleData(persons);
input = new WritableList(persons, Person.class);
// Set the writeableList as input for the viewer
viewer.setInput(input);
Button delete = new Button(parent, SWT.PUSH);
delete.setText("Delete");
delete.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
if (!viewer.getSelection().isEmpty()) {
IStructuredSelection selection = (IStructuredSelection) viewer
.getSelection();
Person p = (Person) selection.getFirstElement();
input.remove(p);
}
}
});
Button add = new Button(parent, SWT.PUSH);
add.setText("Add");
add.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
Person p = new Person();
p.setFirstName("Test");
p.setLastName("Test");
input.add(p);
}
});
}
protected void createExampleData(List<Person> persons) {
Person p = new Person();
p.setFirstName("Joe");
p.setLastName("Darcey");
persons.add(p);
p = new Person();
p.setFirstName("Jim");
p.setLastName("Knopf");
persons.add(p);
p = new Person();
p.setFirstName("Jim");
p.setLastName("Bean");
persons.add(p);
}
@Override
public void setFocus() {
}
}
在这个例子中界面将变成如下的样子。当你添加和修改模型的时候用户界面也会跟着一起更新。
注:英文原文为 Eclipse Databinding with Eclipse RCP applications - Tutorial
还有一部分没有翻译完。后续马上跟上。