Jul 22 2008

JFace Binding

Published by at 12:55 am under Eclipse,IT,Java

Recently I have started using JFace Binding. Unfortunately there are not many resources available on the web with non-trivial examples.

Let present example of master-detail view: a drop-down list and two text fields which display some details regarding option chosen from the list.

POJO Category. It contains two fields: name and description which will be displayed as details of the object chosen from the drop-down list:

import java.beans.PropertyChangeListener;
import java.io.Serializable;
 
/**
* Domain object representing category
*
*/
public class Category implements Serializable {
 
private static final long serialVersionUID = -5542167952605551865L;
 
private String name;
private String description;
 
/**
* Default constructor
*/
public Category() {
// Do nothing
}
 
/**
* Constructor
*
* @param name
*            name of a category
* @param description
*            description of a category
*/
public Category(String name, String description) {
this.name = name;
this.description = description;
}
 
/**
* @return the name
*/
public String getName() {
return name;
}
 
/**
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}
 
/**
* @return the description
*/
public String getDescription() {
return description;
}
 
/**
* @param description the description to set
*/
public void setDescription(String description) {
this.description = description;
}
}

Class of model that will be used to bind the data to the view:

import java.util.List;
 
/**
* Model for displaying categories
*
*/
public class CategoriesModel extends AbstractModelObject {
private List<Category> categories;
private IDataManager dataManager;
private Category category;
 
/**
* The constructor
*/
public CategoriesModel() {
this.dataManager = Activator.getDataManager();
this.categories = this.dataManager.getAllCategories();
}
 
/**
* @return the category
*/
public Category getCategory() {
return this.category;
}
 
/**
* @param category
*            the category to set
*/
public void setCategory(Category category) {
this.category = category;
}
 
/**
* @return the categories
*/
public List<Category> getCategories() {
return categories;
}
 
/**
* @param categories the categories to set
*/
public void setCategories(List<Category> categories) {
this.categories = categories;
}
}

Class of view (in this case it is Eclipse View containing managed form):

import java.util.logging.Logger;
 
import org.eclipse.core.databinding.DataBindingContext;
import org.eclipse.core.databinding.UpdateValueStrategy;
import org.eclipse.core.databinding.beans.BeansObservables;
import org.eclipse.core.databinding.observable.Realm;
import org.eclipse.core.databinding.observable.list.WritableList;
import org.eclipse.core.databinding.observable.map.IObservableMap;
import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.jface.databinding.swt.SWTObservables;
import org.eclipse.jface.databinding.viewers.ObservableListContentProvider;
import org.eclipse.jface.databinding.viewers.ObservableMapLabelProvider;
import org.eclipse.jface.databinding.viewers.ViewersObservables;
import org.eclipse.jface.viewers.ComboViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.forms.ManagedForm;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.ScrolledForm;
import org.eclipse.ui.part.ViewPart;
 
/**
* Allows to view list of categories and their details
*
*/
public class CategoryView extends ViewPart {
 
private Logger logger = Logger.getLogger(CategoryView.class.getName());
 
private static final String FORM_TITLE = Messages
.getString("category.view.form.title");
 
private FormToolkit formToolkit;
 
private CategoriesModel model;
private Text nameText;
private Text descriptionText;
private ComboViewer categoriesComboViewer;
private DataBindingContext dbc;
 
/**
* {@inheritDoc}
*/
@Override
public void createPartControl(Composite parent) {
model = new CategoriesModel();
 
ManagedForm mForm = new ManagedForm(parent);
ScrolledForm scrolledForm = mForm.getForm();
FormToolkit formToolkit = mForm.getToolkit();
Composite formBody = scrolledForm.getBody();
formToolkit.decorateFormHeading(scrolledForm.getForm());
scrolledForm.setText(FORM_TITLE);
formBody.setLayout(new GridLayout(2, true));
 
categoriesComboViewer = new ComboViewer(formBody, SWT.DROP_DOWN
| SWT.READ_ONLY);
GridData comboGridData = new GridData();
comboGridData.horizontalSpan = 2;
comboGridData.horizontalAlignment = GridData.FILL;
categoriesComboViewer.getCombo().setLayoutData(comboGridData);
 
nameText = formToolkit.createText(formBody, "", SWT.NONE);
GridData nameGridData = new GridData();
nameGridData.horizontalSpan = 2;
nameGridData.horizontalAlignment = GridData.FILL;
nameText.setLayoutData(nameGridData);
 
descriptionText = formToolkit.createText(formBody, "", SWT.MULTI);
GridData textGridData = new GridData();
textGridData.horizontalSpan = 2;
textGridData.horizontalAlignment = GridData.FILL;
textGridData.heightHint = 100;
descriptionText.setLayoutData(textGridData);
 
formToolkit.paintBordersFor(formBody);
bindData();
}
 
/**
* {@inheritDoc}
*/
@Override
public void setFocus() {
// Do nothing
}
 
/**
* This method binds the value from UI layer to the model object
*/
protected void bindData() {
dbc = new DataBindingContext();
 
// Seting content provider for combo viewer
ObservableListContentProvider categoryViewerContentProvider = new ObservableListContentProvider();
categoriesComboViewer.setContentProvider(categoryViewerContentProvider);
IObservableMap[] attributeMaps = BeansObservables.observeMaps(
categoryViewerContentProvider.getKnownElements(),
Category.class, new String[] { "name" });
categoriesComboViewer.setLabelProvider(new ObservableMapLabelProvider(
attributeMaps));
categoriesComboViewer.setInput(new WritableList(model.getCategories(),
Category.class));
 
// Observing changes in selection in combo viewer
IObservableValue selection = ViewersObservables
.observeSingleSelection(categoriesComboViewer);
 
// Observing the properties of the current selection
IObservableValue detailNameObservable = BeansObservables
.observeDetailValue(Realm.getDefault(), selection, "name",
String.class);
 
IObservableValue detailDescriptionObservable = BeansObservables
.observeDetailValue(Realm.getDefault(), selection,
"description", String.class);
 
// Binding the Text widgets to the name and description details
// (selection's properties).
dbc.bindValue(SWTObservables.observeText(nameText, SWT.None),
detailNameObservable, new UpdateValueStrategy(false,
UpdateValueStrategy.POLICY_NEVER), null);
 
dbc.bindValue(SWTObservables.observeText(descriptionText, SWT.None),
detailDescriptionObservable, new UpdateValueStrategy(false,
UpdateValueStrategy.POLICY_NEVER), null);
}
 
/**
* {@inheritDoc}
*/
public void dispose() {
if (formToolkit != null) {
formToolkit.dispose();
}
super.dispose();
}
}

AbstractModelObject which provides methods required by JFace Binding (it’s not checked on compilation level):

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
 
/**
* Provides minimal JavaBeans support for model objects
*
*/
public abstract class AbstractModelObject {
private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(
this);
 
/**
* @param listener
*/
public void addPropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.addPropertyChangeListener(listener);
}
 
/**
* @param propertyName
* @param listener
*/
public void addPropertyChangeListener(String propertyName,
PropertyChangeListener listener) {
propertyChangeSupport.addPropertyChangeListener(propertyName, listener);
}
 
/**
* @param listener
*/
public void removePropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.removePropertyChangeListener(listener);
}
 
/**
* @param propertyName
* @param listener
*/
public void removePropertyChangeListener(String propertyName,
PropertyChangeListener listener) {
propertyChangeSupport.removePropertyChangeListener(propertyName,
listener);
}
 
/**
*
* @param propertyName
* @param oldValue
* @param newValue
*/
protected void firePropertyChange(String propertyName, Object oldValue,
Object newValue) {
propertyChangeSupport.firePropertyChange(propertyName, oldValue,
newValue);
}
}

4 responses so far

4 Responses to “JFace Binding”

  1. Igor Ganapolskyon 08 Jun 2009 at 4:55 am

    This is a great example. However, I’m trying to do something like this with a TableViewer. And I when creating the BindingContext I get a runtime error “Realm cannot be null.” My class extends a ViewPart, so it doesn’t have a main method. Have you seen this error?

  2. Radoslaw Urbason 20 Jun 2009 at 4:48 pm

    @Igor I haven’t used binding with TableViewer so far. But I suggest you check if you are passing Realm instance to all Observables objects.

  3. Jameson 17 Sep 2009 at 8:03 am

    I can’t run this program because of some error happen.
    1. private IDataManager dataManager;

    2.java.lang.IllegalArgumentException: Could not find property with name name in class class testcombo.ViewPart1
    at org.eclipse.core.internal.databinding.beans.BeanPropertyHelper.getPropertyDescriptor(BeanPropertyHelper.java:177)
    at org.eclipse.core.databinding.beans.BeanProperties.value(BeanProperties.java:116)
    at org.eclipse.core.databinding.beans.BeanProperties.value(BeanProperties.java:87)
    at org.eclipse.core.databinding.beans.BeansObservables.observeMap(BeansObservables.java:122)
    at org.eclipse.core.databinding.beans.BeansObservables.observeMaps(BeansObservables.java:254)
    at testcombo.ViewPart1.bindData(ViewPart1.java:102)
    at testcombo.ViewPart1.createPartControl(ViewPart1.java:82)
    at org.eclipse.ui.internal.ViewReference.createPartHelper(ViewReference.java:367)
    at org.eclipse.ui.internal.ViewReference.createPart(ViewReference.java:226)
    at org.eclipse.ui.internal.WorkbenchPartReference.getPart(WorkbenchPartReference.java:595)
    at org.eclipse.ui.internal.PartPane.setVisible(PartPane.java:313)
    at org.eclipse.ui.internal.ViewPane.setVisible(ViewPane.java:529)
    at org.eclipse.ui.internal.presentations.PresentablePart.setVisible(PresentablePart.java:180)
    at org.eclipse.ui.internal.presentations.util.PresentablePartFolder.select(PresentablePartFolder.java:270)
    at org.eclipse.ui.internal.presentations.util.LeftToRightTabOrder.select(LeftToRightTabOrder.java:65)
    at org.eclipse.ui.internal.presentations.util.TabbedStackPresentation.selectPart(TabbedStackPresentation.java:473)
    at org.eclipse.ui.internal.PartStack.refreshPresentationSelection(PartStack.java:1256)
    at org.eclipse.ui.internal.PartStack.setSelection(PartStack.java:1209)
    at org.eclipse.ui.internal.PartStack.showPart(PartStack.java:1608)
    at org.eclipse.ui.internal.PartStack.createControl(PartStack.java:649)
    at org.eclipse.ui.internal.PartStack.createControl(PartStack.java:576)
    at org.eclipse.ui.internal.PartSashContainer.createControl(PartSashContainer.java:568)
    at org.eclipse.ui.internal.PerspectiveHelper.activate(PerspectiveHelper.java:272)
    at org.eclipse.ui.internal.Perspective.onActivate(Perspective.java:981)
    at org.eclipse.ui.internal.WorkbenchPage.onActivate(WorkbenchPage.java:2626)
    at org.eclipse.ui.internal.WorkbenchWindow$27.run(WorkbenchWindow.java:2964)
    at org.eclipse.swt.custom.BusyIndicator.showWhile(BusyIndicator.java:70)
    at org.eclipse.ui.internal.WorkbenchWindow.setActivePage(WorkbenchWindow.java:2945)
    at org.eclipse.ui.internal.WorkbenchWindow.busyOpenPage(WorkbenchWindow.java:760)
    at org.eclipse.ui.internal.Workbench$21.runWithException(Workbench.java:1045)
    at org.eclipse.ui.internal.StartupThreading$StartupRunnable.run(StartupThreading.java:31)
    at org.eclipse.swt.widgets.RunnableLock.run(RunnableLock.java:35)
    at org.eclipse.swt.widgets.Synchronizer.runAsyncMessages(Synchronizer.java:134)
    at org.eclipse.swt.widgets.Display.runAsyncMessages(Display.java:3468)
    at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:3115)
    at org.eclipse.ui.application.WorkbenchAdvisor.openWindows(WorkbenchAdvisor.java:803)
    at org.eclipse.ui.internal.Workbench$28.runWithException(Workbench.java:1384)
    at org.eclipse.ui.internal.StartupThreading$StartupRunnable.run(StartupThreading.java:31)
    at org.eclipse.swt.widgets.Synchronizer.syncExec(Synchronizer.java:179)
    at org.eclipse.ui.internal.UISynchronizer.syncExec(UISynchronizer.java:150)
    at org.eclipse.swt.widgets.Display.syncExec(Display.java:4113)
    at org.eclipse.ui.internal.StartupThreading.runWithoutExceptions(StartupThreading.java:94)
    at org.eclipse.ui.internal.Workbench.init(Workbench.java:1379)
    at org.eclipse.ui.internal.Workbench.runUI(Workbench.java:2335)
    at org.eclipse.ui.internal.Workbench.access$4(Workbench.java:2221)
    at org.eclipse.ui.internal.Workbench$5.run(Workbench.java:500)
    at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:332)
    at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:493)
    at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:149)
    at testcombo.Application.start(Application.java:20)
    at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:194)
    at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:110)
    at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:79)
    at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:368)
    at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:179)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:585)
    at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:559)
    at org.eclipse.equinox.launcher.Main.basicRun(Main.java:514)
    at org.eclipse.equinox.launcher.Main.run(Main.java:1311)
    at org.eclipse.equinox.launcher.Main.main(Main.java:1287)

    Could you explain it to me. Thanks!

  4. Radoslaw Urbason 06 Oct 2009 at 11:54 am

    Do you have public String getName() and void setName(String name) methods?

Trackback URI | Comments RSS

Leave a Reply

You must be logged in to post a comment.