RSS

Search Engine

Wednesday, June 23, 2010

JFace Databinding

4.1. Overview

JFace provides a flexible way of defining a view, the data and the representation of data separately. The following focuses in using JFace with Eclipse Databinding. For a larger example of JFace please see JFace Tables With Eclipse RCP

The following will demonstrate how list, table and tree viewers can be used with Eclipse Databinding.

Create a new plugin "de.vogella.databinding.model".

4.2. JFace Listviewer

In the databinding for JFace viewers it is possible to distinct between changes in the collection and changes in the individual object. The following focus on the propagation of changes from the collection to the UI, e.g. if an element will be added to the collection or if an element will be removed from the collection this change should be visible in the UI.

JFace databinding provides several classes which allows to bind lists to viewers. ObservableListContentProvider is a content provider which requires a list which implements IObservableCollection. We use WritableList as input.

Create a new Eclipse RCP project "de.vogella.databinding.person.listviewer" use "Hello RCP" as a template (see Create your first Eclipse RCP application for details).

Add again the dependency to your model and the databinding plugins to your plugin project.

Add a view to your application. Create the following class for this view.

    
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 persons = new ArrayList();
// 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 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() {

}

}

In this example the UI will be updated if you remove or add an element to the collection. Run this example and test it.

Using Eclipse Data Binding with Pojos

5.1. Overview

The example before should how you can use databinding to synchronize changes between the UI and the model. If you only need to use to update the model from the UI you can use the class PojoObservables.

5.2. Example

Create a new Eclipse RCP project "de.vogella.databinding.personpojo" use "Hello RCP" as a template (see Create your first Eclipse RCP application for details).

Create the following domain model.

    
package de.vogella.databinding.personpojo.model;

public class PersonPojo {
private String firstName;
private String lastName;
private boolean married;
private String gender;
private Integer age;

public PersonPojo() {
}

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) {
this.firstName = firstName;
}

public void setGender(String gender) {
this.gender = gender;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}

public void setMarried(boolean isMarried) {
this.married = isMarried;
}

public Integer getAge() {
return age;
}

public void setAge(Integer age) {
this.age = age;
}


Add the plugins "org.eclipse.core.databinding", "org.eclipse.core.databinding.beans" and "org.eclipse.jface.databinding"as a dependency to your plugin.

Create a view with the ID "de.vogella.databinding.personpojo.myview" and implement the following code.

    
package de.vogella.databinding.personpojo;

import org.eclipse.core.databinding.DataBindingContext;
import org.eclipse.core.databinding.beans.PojoObservables;
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.GridLayout;
import org.eclipse.swt.widgets.Button;
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.personpojo.model.PersonPojo;

public class MyView extends ViewPart {
public static final String ID = "de.vogella.databinding.personpojo.myview";

private PersonPojo person;
private Text firstName;
private Button married;

public MyView() {
person = new PersonPojo();
person.setFirstName("Joe");
person.setLastName("Jim");
}

@Override
public void createPartControl(Composite parent) {
Layout layout = new GridLayout(2, false);
parent.setLayout(layout);
Label label = new Label(parent, SWT.NONE);
label.setText("FirstName");
firstName = new Text(parent, SWT.BORDER);
Label marriedLabel = new Label(parent, SWT.NONE);
marriedLabel.setText("Maried");
married = new Button(parent, SWT.CHECK);
bindValues();

Button button1 = new Button(parent, SWT.PUSH);
button1.setText("Write model");
button1.addSelectionListener(new SelectionAdapter() {

@Override
public void widgetSelected(SelectionEvent e) {
System.out.println(person.getFirstName());
System.out.println(person.isMarried());
}
});

}

private void bindValues() {
DataBindingContext bindingContext = new DataBindingContext();
bindingContext.bindValue(SWTObservables.observeText(firstName,
SWT.Modify), PojoObservables.observeValue(person, "firstName"),
null, null);

bindingContext.bindValue(SWTObservables.observeSelection(married),
PojoObservables.observeValue(person, "married"), null, null);
}

@Override
public void setFocus() {

}

}

Add your view to the perspective.

Run the example and test it. Each time you change the UI element then model changes automatically.

Customer Validations and ControlDecoration

This chapter will demonstrate the usage of validators and decorators for the controls.

The result will look like the following.

Create a new Eclipse RCP project "de.vogella.databinding.validation" use "Hello RCP" as a template.

In your new project re-create the pojo example. Re-create the dependencies to the required plugins.

Create the following class "StringLongerThenTwo" which implements org.eclipse.core.databinding.validation.IValidator.

   
package de.vogella.databinding.validation.validators;

import org.eclipse.core.databinding.validation.IValidator;
import org.eclipse.core.databinding.validation.ValidationStatus;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.fieldassist.ControlDecoration;

public class StringLongerThenTwo implements IValidator {

private final String message;
private final ControlDecoration controlDecoration;

public StringLongerThenTwo(String message,
ControlDecoration controlDecoration) {
super();
this.message = message;
this.controlDecoration = controlDecoration;
}

@Override
public IStatus validate(Object value) {
if (value instanceof String) {
String s = (String) value;
// We check if the string is longer then 2 signs
if (s.length() > 2) {
controlDecoration.hide();
return Status.OK_STATUS;
} else {
controlDecoration.show();
return ValidationStatus.error(message);
}
} else {
throw new RuntimeException(
"Not supposed to be called for non-strings.");
}
}
}

To use the validator you have to use your own UpdateValueStrategy. We are also adding a label to the UI which displays the status in case something is wrong in this field. We are also registering a change listener to the field "lastName" and changing the background color of the field if the input is not correct. So you see both how to use a decorator and how to change a field based on the values.

The following shows the coding for the view with the ID "de.vogella.databinding.validation.personview".

   
package de.vogella.databinding.validation;

import org.eclipse.core.databinding.AggregateValidationStatus;
import org.eclipse.core.databinding.DataBindingContext;
import org.eclipse.core.databinding.UpdateValueStrategy;
import org.eclipse.core.databinding.beans.PojoObservables;
import org.eclipse.core.databinding.observable.ChangeEvent;
import org.eclipse.core.databinding.observable.IChangeListener;
import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.jface.databinding.swt.SWTObservables;
import org.eclipse.jface.fieldassist.ControlDecoration;
import org.eclipse.jface.fieldassist.FieldDecoration;
import org.eclipse.jface.fieldassist.FieldDecorationRegistry;
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.Composite;
import org.eclipse.swt.widgets.Display;
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.validation.model.PersonPojo;
import de.vogella.databinding.validation.validators.StringLongerThenTwo;

public class PersonViewValidation extends ViewPart {
private static final String MESSAGE = "Name must be longer two letters";
public static final String ID = "de.vogella.databinding.validation.personview";
private PersonPojo person;
private Text firstName;

private Label errorLabel;
private IObservableValue uiElement;
private ControlDecoration firstNameDecorator;
private ControlDecoration lastNameDecorator;
private Text lastName;

public PersonViewValidation() {
}

@Override
public void createPartControl(Composite parent) {
person = new PersonPojo();
person.setFirstName("Lars");
person.setLastName("Vogel");
person.setGender("m");
person.setAge(12);
// Lets put thing to order
Layout layout = new GridLayout(2, false);
parent.setLayout(layout);

// Firstname
Label nameLabel = new Label(parent, SWT.None);
nameLabel.setText("Firstname: ");
firstName = new Text(parent, SWT.BORDER);
// Create the decorator which make the error message look really nice
firstNameDecorator = createDecorator(firstName, MESSAGE);

GridData gridData = new GridData();
gridData.horizontalAlignment = SWT.FILL;
gridData.grabExcessHorizontalSpace = true;
firstName.setLayoutData(gridData);

// Lastname
nameLabel = new Label(parent, SWT.None);
nameLabel.setText("Lastname: ");
lastName = new Text(parent, SWT.BORDER);
// Create the decorator which make the error message look really nice
lastNameDecorator = createDecorator(lastName, MESSAGE);
lastName.setLayoutData(gridData);

Button button1 = new Button(parent, SWT.PUSH);
button1.setText("Write model");
button1.addSelectionListener(new SelectionAdapter() {

@Override
public void widgetSelected(SelectionEvent e) {
System.out.println(person.getFirstName());
System.out.println(person.getLastName());
}
});
// Button should take 2 rows
gridData = new GridData();
gridData.horizontalSpan = 2;
button1.setLayoutData(gridData);

// This label will display all errors of all bindings
Label descAllLabel = new Label(parent, SWT.NONE);
descAllLabel.setText("All Validation Problems:");
errorLabel = new Label(parent, SWT.NONE);
gridData = new GridData();
gridData.horizontalAlignment = SWT.FILL;
gridData.grabExcessHorizontalSpace = true;
gridData.horizontalAlignment = GridData.FILL;
gridData.horizontalSpan = 1;
errorLabel.setLayoutData(gridData);

bindValues();

}

@Override
public void setFocus() {
}

private ControlDecoration createDecorator(Text text, String message) {
ControlDecoration controlDecoration = new ControlDecoration(text,
SWT.LEFT | SWT.TOP);
controlDecoration.setDescriptionText(message);
FieldDecoration fieldDecoration = FieldDecorationRegistry.getDefault()
.getFieldDecoration(FieldDecorationRegistry.DEC_ERROR);
controlDecoration.setImage(fieldDecoration.getImage());
return controlDecoration;
}

private void bindValues() {
// The DataBindingContext object will manage the databindings
DataBindingContext bindingContext = new DataBindingContext();
// First we bind the text field to the model
// Here we define the UpdateValueStrategy
UpdateValueStrategy update = new UpdateValueStrategy();
update.setAfterConvertValidator(new StringLongerThenTwo(MESSAGE,
firstNameDecorator));
// Bind fistName
bindingContext.bindValue(SWTObservables.observeText(firstName,
SWT.Modify), PojoObservables.observeValue(person, "firstName"),
update, null);

// Bind lastName
bindingContext.bindValue(SWTObservables.observeText(lastName,
SWT.Modify), PojoObservables.observeValue(person, "lastName"),
new UpdateValueStrategy()
.setAfterConvertValidator(new StringLongerThenTwo(
MESSAGE, lastNameDecorator)), null);

// We listen to all errors via this binding
uiElement = SWTObservables.observeText(errorLabel);
// This one listenes to all changes
bindingContext.bindValue(uiElement, new AggregateValidationStatus(
bindingContext.getBindings(),
AggregateValidationStatus.MAX_SEVERITY), null, null);

// Lets change the color of the field lastName
uiElement.addChangeListener(new IChangeListener() {
@Override
public void handleChange(ChangeEvent event) {
if (uiElement.getValue().equals("OK")) {
lastName.setBackground(Display.getCurrent().getSystemColor(
SWT.COLOR_WHITE));

} else {
lastName.setBackground(Display.getCurrent().getSystemColor(
SWT.COLOR_RED));
}
}
});
}
}

Eclipse Data Binding with EMF

Create an EMF model "person.genmodel" with the following properties:

  • firstName - String

  • lastName - String

  • gender - String

  • isMarried - boolean

Create a new view "eclipsedatabinding.EMFExample" similar to UIExample and add this view to the perspective.

   
package eclipsedatabinding;

import org.eclipse.core.databinding.DataBindingContext;
import org.eclipse.emf.databinding.EMFObservables;
import org.eclipse.jface.databinding.swt.SWTObservables;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Layout;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.part.ViewPart;

import domainmodelemf.DomainmodelemfFactory;
import domainmodelemf.DomainmodelemfPackage;
import domainmodelemf.IPerson;

public class EMFExample extends ViewPart {
public static final String ID = "eclipsedatabinding.EMFExample";
private IPerson lars;

public EMFExample() {

}

@Override
public void createPartControl(Composite parent) {

// Lets put thing to order
Layout layout = new GridLayout(2, false);
parent.setLayout(layout);

// Initialize the model
DomainmodelemfPackage.eINSTANCE.eClass();
// Retrieve the default factory singleton
DomainmodelemfFactory factory = DomainmodelemfFactory.eINSTANCE;

lars = factory.createIPerson();
lars.setFirstName("Lars");
lars.setLastName("Vogel");
lars.setGender("m");


// New text element
Text firstName = new Text(parent, SWT.NONE);
DataBindingContext bindingContext = new DataBindingContext();
firstName.setText("Hallo");
GridData gridData = new GridData();
gridData.horizontalAlignment = GridData.FILL;
gridData.horizontalSpan = 2;
firstName.setLayoutData(gridData);

bindingContext.bindValue(SWTObservables.observeText(firstName, SWT.Modify), EMFObservables.observeValue(lars,DomainmodelemfPackage.Literals.IPERSON__FIRST_NAME), null, null);



Button button1 = new Button(parent, SWT.PUSH);
button1.setText("Write model");
button1.addMouseListener(new MouseListener(){

@Override
public void mouseDoubleClick(MouseEvent e) {
// TODO Auto-generated method stub

}

@Override
public void mouseDown(MouseEvent e) {
System.out.println(lars.getFirstName());
}

@Override
public void mouseUp(MouseEvent e) {
// TODO Auto-generated method stub

}



});

Button button2 = new Button(parent, SWT.PUSH);
button2.setText("Change model");
button2.addMouseListener(new MouseListener(){

@Override
public void mouseDoubleClick(MouseEvent e) {
// TODO Auto-generated method stub

}

@Override
public void mouseDown(MouseEvent e) {
lars.setFirstName("Lars2");
}

@Override
public void mouseUp(MouseEvent e) {
// TODO Auto-generated method stub

}



});
}

@Override
public void setFocus() {
// TODO Auto-generated method stub

}

}

Thank you

Thank you for practicing with this tutorial.

If you like the information of this article, please help me by donating.

Saturday, June 19, 2010

Integrating external jars / third party libraries

The following gives an example how to convert jars to Eclipse plugins.

1.1. Create a plugin project for your jar

Create a new Plugin project by selection File-> New -> Project...-> Plug-in Development -> "Plug-in from Existing JAR Archives"

Add the jars you want to have in this new plugin. Press next.

Maintain a name and a version for this plugin. Uncheck the flag "Unzip the JAR archive into the project". Press then finish. Unchecking the flag "Unzip the JAR archive into the project" prevents that the class files are extracted from the Jar which is usually not necessary.

You have now bundled your jars into a new plugin. Open the file "MANIFEST.MF" and validate that all required package are exported on the tab "Runtime". All the packages from your jars should be included in the exported packages as OSGi will otherwise present other plugins from accessing them.

1.2. Using the new plugin project

In the plugin project which should use the library, select now select your plugin.xml and the tab dependencies. Under required Plug-ins press add and add your generated plugin as a dependency to your RCP application. This will make the classes from the jar available to your plugin.

Thank you

Thank you for practicing with this tutorial.

I maintain this tutorial in my private time. If you like the information please help me by donating or by recommending this tutorial to other people.