在Eclipse RCP应用程序中使用Databinding(邦定)


1. Eclipse Databinding概述

Eclipse Databinding可以在你的模型和用户界面之间建立一种关联。当任何一方(模型或用户界面)发生变化时,都可以同步到另一方。 另外你可以使用默认的或者是自定义的数据验证器(validator)来验证数据同时可以使用转换器(converter)在模型和界面之间转换数据。 下面我们用一个应用程序来演示Databinding功能。在这个程序中我们在界面中显示一个Person对象,用户可以在界面中修改Person对象的值。 也可以通过一个按钮来触发事件来直接修改模型。任何一端的变化都会同步到另一端。

1.  Domain Model

1.1.  概述

首先创建一个可复用的数据模型。所以我们把数据模型封装到一个独立的插件(plugin)中以方便复用。

 

1.2.  创建插件

创建一个名为“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 中。这样其他插件就可以访问到这个包了。

2. 绑定到SWT控件

2.1.  RCP项目

通过“Hello RCP”模板创建一个名为“de.vogella.databinding.person.swt”的Eclipse RCP项目(具体的可以参考 Create your first Eclipse RCP )。

2.2. 依赖模块

把“de.vogella.databinding.person.model”插件添加到当前项目的依赖中。

2.3.  添加Eclipse Databinding的依赖

把"org.eclipse.core.databinding", "org.eclipse.core.databinding.beans", "org.eclipse.jface.databinding" 和 "org.eclipse.core.databinding.property" 添加到当前项目的依赖中。

2.4.  用户界面

向当前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,将会使用默认的。

2.5.  Run

运行以上程序。每当你在用户界面中修改时,模型将会被同步。如果你直接修改模型时,用户界面将会同时被更新。 上面的代码同时还演示了Eclipse Databinding预定义的转换器和验证器。

Note

默认情况下text控件用 “text ” 属性来邦定到模型的属性。用户也可以指定用其他属性来邦定到模型属性。 例如:用户可以将控件中的 “enabled ” 邦定到一个布尔(boolean)类型的模型属性(模型中的字段)。

 

3.  JFace 邦定

3.1.  概述

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

      还有一部分没有翻译完。后续马上跟上。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值