Andre Dietisheim [http://community.jboss.org/people/adietish] modified the 
document:

"Eclipse wizards with jface databinding in Deltacloud Tools (1)"

To view the document, visit: http://community.jboss.org/docs/DOC-15964

--------------------------------------------------------------
h1. Less code
If you use jface databinding to code your swt views in Eclipse you'll get 
spared from writing listeners and updating code by hand. JFace databinding 
offers very nice abstractions and automatisms that offer funcitonalities for 
the vast majority of the tasks where you have to listen for user input and 
update a view accordingly.

h1. Premise
If you implement a complex and highly dynamic UI in Eclipse you'll have to code 
many listener that wait for user actions. Those listeners mostly do nothing 
spectacular but update widgets and models in reaction to user inputs. You end 
up with a lot of repetitive boilerplate code. UI frameworks in the non-java 
land (ex. http://qt.nokia.com/products/  Trolltechs QT) always had approaches 
that were far more elegant than what we usually did in Swing and SWT. Those so 
called binding frameworks slowly made it into  http://jgoodies.com/ Swing and  
http://wiki.eclipse.org/index.php/JFace_Data_Binding Eclipse. Nowadays Eclipse 
offers a very mature (developed since 2006) framework that spares you from 
boilerplate and leads to far more concise and a less verbose implementations: 
Jface Databinding!

h1. Usecase
In Deltacloud tools, we had to implement a wizard page that allows a user to 
create a connection to a Deltacloud server:

The user has to supply a name, an url and the credentials for it.


 
http://community.jboss.org/servlet/JiveServlet/showImage/102-15964-65-10644/wizardpage.png
  
http://community.jboss.org/servlet/JiveServlet/downloadImage/102-15964-65-10644/450-337/wizardpage.png
 


Sounds pretty simple at first sight. Looking closer at it you'll discover few 
buttons and labels, that shall get updated upon user inputs. We need to inform 
the user in a intuitive way and mark form fields by error decorations. The 
wizard page shall also show a global  error message that reflects the errors in 
a human readable manner.


 
http://community.jboss.org/servlet/JiveServlet/showImage/102-15964-65-10645/wizardpage-decorations.png
  
http://community.jboss.org/servlet/JiveServlet/downloadImage/102-15964-65-10645/450-337/wizardpage-decorations.png
 

h1.  
We choose to use jface databinding and not to stick to the traditional, tedious 
approach. I'll discuss our implementation in a simplified way. You may have a 
look at the complete implementation at  
http://anonsvn.jboss.org/repos/jbosstools/trunk/deltacloud/plugins/org.jboss.tools.deltacloud.ui/src/org/jboss/tools/internal/deltacloud/ui/wizards/CloudConnectionPage.java
 CloudConnectionPage.java 
h1. 
h1. Let us get to the (little) code
h2. The user must provide a name for the connection
We stick to a fairly simple use case and discuss all steps that we have to 
take: The use musn't leave the name blank. He has name the connection.
The picture above shows the various ways we implement to inform the user. As 
long as no name is provided:

* We decorate the name filed with a error icon
* We provide a meaningful error message in the title area of our page
* We disable the finish-button
h1. ** 
h2. 
h2. We need a text widget for the name
To start, we need a text field to hold the user inputs. The classic approach 
would be to create a SWT text wiget and attach a ModifyLlistener to it. We 
would implement this listener by validating the user input. Jface databinding 
spares us from writing this listener:

Text nameText = new Text(parent, SWT.BORDER | SWT.SINGLE);
IObservableValue observableText = 
WidgetProperties.text(SWT.Modify).observe(nameText);

JFace databinding operates on *Observables*. Observables provide a standardized 
observer pattern used across jface databinding. No matter what you observe, a 
widget, a bean, etc. you always listen to changes that occour in a jface 
observable.

 
http://community.jboss.org/servlet/JiveServlet/showImage/102-15964-65-10646/observables.png
  
http://community.jboss.org/servlet/JiveServlet/downloadImage/102-15964-65-10646/450-363/observables.png
 


The code shown above attaches a ModifyListener to the text field and wraps it 
in an *Observable*. We still have no validation. We'll get to this in a few 
steps.
h2. 
h2. We need a model to store the name
Jface databinding is a framework that is built upon the principle of 
model-view-controller (*MVC*).  It connects a view to a model and transfers the 
user inputs to the model. It of course also operates the other way round and 
updates the view if new values are set to the model.
We'll now create a simple bean that holds the name of our connection:

public class CloudConnectionModel extends ObservablePojo { 

     public static final String PROPERTY_NAME = "name"; 

     private String name; 

     public CloudConnectionModel(String name) {
          this.name = name;     
     }     
     
     public String getName() {
          return name;     
     } 
     
     public void setName(String name) {
            getPropertyChangeSupport().
               firePropertyChange(PROPERTY_NAME, this.name, this.name = 
name);     
     }
}

Our model is a simple bean and provides getters and setter for the name 
property. It uses PropertyChangeSupport to inform observers about changes to 
the name of our connection. PropertyChangeSupport, an observer pattern shipped 
with the jdk, allows us to get informed of changes. Since jface uses 
*Observables*, we have to wrap our name property to conform to the *Observable* 
interface:

IObservableValue observableName = 
    BeanProperties.value(CloudConnectionModel.class, 
CloudConnectionModel.PROPERTY_NAME)
     .observe(connectionModel),

h2. 
h2. Databinding, transfer the user input to our model!
Now that we have observables on both ends (model and widget), we can tell 
databinding to connect both ends. JFace databinding will then propagate user 
inputs to our model. It will also transfer changes in the model to the widget. 
A so called *Binding* may be created on behalf of the *DataBindingContext:*

DataBindingContext dbc = new DataBindingContext();

dbc.bindValue(
     WidgetProperties.text(SWT.Modify).observe(nameText),
     BeanProperties.value(CloudConnectionModel.class, 
CloudConnectionModel.PROPERTY_NAME)
          .observe(connectionModel));

h2. But hey, refuse invalid names
We now told databinding to transfer the user input to our model. What we still 
miss is the ability to constrain user inputs.


 
http://community.jboss.org/servlet/JiveServlet/showImage/102-15964-65-10647/convert-validate.png
  
http://community.jboss.org/servlet/JiveServlet/downloadImage/102-15964-65-10647/450-189/convert-validate.png
 


Jface databinding offers the ability to convert and validate values while they 
are transferred from one end to the other. We can tell databinding to validate 
the name before it sets the user input to the model. Databinding uses 
*IValidator* instances for that sake:

public class MandatoryStringValidator implements IValidator {
     
     private String errorMessage;
     
     public MandatoryStringValidator(String errorMessage) {          
          this.errorMessage = errorMessage;     
     }
     
     /**      
     *       
     * validates the given string. Validation passes only if the given value 
is      
     * not <tt>null</tt> and it's length's larger than 0      
     *       
     */     
     public IStatus validate(Object value) {
          if (!((value instanceof String) && ((String) value).length() > 0)) {
               return ValidationStatus.error(errorMessage);
          }
          return ValidationStatus.ok();     
     }
}

The validator shown above validates ok if the user provides any name. It fails 
if no name is set to the text widget.
To instruct jface databinding to use our validator, we have to wire it into a 
so called *UpdateValueStrategy*.  As we saw above, databinding transfers values 
both ways:  from the view to the model and from the model to the view. A 
binding therefore has 2 update strategies. A first one that gets applied when 
values are transfered from the view to the model and another one that is 
applied when transferring from the model to the view. We only want to validate 
names that the user provides. We therefore keep the default (*null*) strategy 
for updates that occur in the model:

Binding nameTextBinding = 
     dbc.bindValue(
          WidgetProperties.text(SWT.Modify).observe(nameText),
          BeanProperties
               .value(CloudConnectionModel.class, 
CloudConnectionModel.PROPERTY_NAME)
               .observe(connectionModel),
          new UpdateValueStrategy().setBeforeSetValidator(
               new MandatoryStringValidator("You must name the connection")),
          null);

h2. Show me the errors

We now may display the result of the validation. We want the text field to be 
decorated:
h3. 
Field decoration

 
http://community.jboss.org/servlet/JiveServlet/showImage/102-15964-65-10648/name-decoration.png
  
http://community.jboss.org/servlet/JiveServlet/downloadImage/102-15964-65-10648/300-65/name-decoration.png
 

ControlDecorationSupport.create(nameTextBinding, SWT.LEFT | SWT.TOP);


h3. Wizard page error
We also want to get informed in the title area of our wizard page.

 
http://community.jboss.org/servlet/JiveServlet/showImage/102-15964-65-10649/page-error.png
  
http://community.jboss.org/servlet/JiveServlet/downloadImage/102-15964-65-10649/267-93/page-error.png
 

A single call to jface databinding achieves this for you:

WizardPageSupport.create(this, dbc);



h3. Finish button enablement
There's a single missing piece: We also want to disable the finish button as 
soon as validation fails:
 
http://community.jboss.org/servlet/JiveServlet/showImage/102-15964-65-10538/enable-disable-finish.png
  
http://community.jboss.org/servlet/JiveServlet/downloadImage/102-15964-65-10538/250-50/enable-disable-finish.png
 


In the classic approach, one would have to implement 
*WizardPage#isPageComplete*.

public abstract class WizardPage extends DialogPage implements IWizardPage {
    /**
     * The <code>WizardPage</code> implementation of this <code>IWizard</code> 
method
     * returns the value of an internal state variable set by
     * <code>setPageComplete</code>. Subclasses may extend.
     */
    public boolean isPageComplete() {

And here's what you have to do using jface databinding:

h4. Nothing, not a single line of additional code.


*WizardPageSupport#create* displays the validation messages in the title area 
but also connects the validation state to the page completion state.

h1. All the code you need

To sum up, the whole databinding code looks like the following:

DataBindingContext dbc = new DataBindingContext(); 
WizardPageSupport.create(this, dbc);

Text nameText = new Text(parent, SWT.BORDER | SWT.SINGLE);
          
Binding nameTextBinding = 
     dbc.bindValue(
          WidgetProperties.text(SWT.Modify).observe(nameText),
          BeanProperties
               .value(CloudConnectionModel.class, 
CloudConnectionModel.PROPERTY_NAME)
               .observe(connectionModel),
          new UpdateValueStrategy().setBeforeSetValidator(
               new MandatoryStringValidator("You must name the connection")),
          null
     );
ControlDecorationSupport.create(nameTextBinding, SWT.LEFT | SWT.TOP);

h1. Conclusion
JFace databinding brings a fresh breath into UI programming compared to what 
was state of the art when swing was introduced. It lessens boilerplate 
considerably and brings well adapted paradigms to what's needed when building 
UIs with java. Your code gets more concise, maintainable and your work gets 
more effective.
JFace databinding exists for 5 years now and a large userbase made it stable 
and mature over the years. There's really no reason not to use it. You wont 
regret it.
--------------------------------------------------------------

Comment by going to Community
[http://community.jboss.org/docs/DOC-15964]

Create a new document in JBoss Tools at Community
[http://community.jboss.org/choose-container!input.jspa?contentType=102&containerType=14&container=2128]
_______________________________________________
jboss-user mailing list
jboss-user@lists.jboss.org
https://lists.jboss.org/mailman/listinfo/jboss-user

Reply via email to