Controlling UI Components using JGoodies Binding

In Binding with JGoodies Binding, the binding library from JGoodies was introduced with a relatively simple example. The binding framework can also be used to automatically enable or disable user interface components depending on some condition.

A model is used to notify when the component should be enabled or disabled. This is a relatively simple model. It only needs to notify the library when the component should be enabled or disabled. I’ve called the model Allowance, as it is designed to notify when a feature or function is allowed to be used or performed rather than when something should be enabled or disabled. The code for the model is presented here:

 1 public class Allowance extends Model {
 2
 3   private static final long 
 4     serialVersionUID = 1L;
 5   
 6   public static final String
 7     PROPERTY_ALLOWANCE = "allowed";
 8
 9   private boolean allow = false;
10   
11   public void setAllowed(boolean nowAllowed) 
12   {
13     boolean oldAllowed = allow;
14     allow = nowAllowed;
15     firePropertyChange(PROPERTY_ALLOWANCE,
16       oldAllowed, allow);
17   }
18
19   public boolean isAllowed()
20   {
21     return allow;
22   }
23 }

Next, the Allowance model needs to be bound to the UI component. In the previously mentioned tutorial, an editor for a book's subject and title was created and bound to a model for a book. Let's say that the title field will now be enabled only when a subject is selected and disabled at all other times. Models allows listeners to be attached to receive notification of changes to specific properties. Interested listeners must implement the PropertyChangeListener interface. Here is the corresponding block of code to implement this behaviour:

final Allowance allowed = new Allowance();
PropertyChangeListener allowanceUpdate =
  new PropertyChangeListener() {

    @Override
    public void propertyChange(PropertyChangeEvent evt) {
      if (!evt.getNewValue().equals("none")) {
        allowed.setAllowed(true);
      } else {
        allowed.setAllowed(false);
      }
    }
  };

The listener simply receives an event that contains information on the source of the event (i.e. the object with the changed property) and the old and new values of the property.

To be of any use, the listener has to be attached to our model. There are a few options on how this could be done. The listener can be directly attached to our model or indirectly via the same presentation model used in creating the editor. Generally, I recommend going through presentation model, as that would allow you to change the instance being edited much more easily. The listener requires knowledge of changes to the subject only. The binding library allows listeners to be notified of only changes to the subject. To attach the listener:

presentation.getModel(Book.PROPERTY_TOPIC).
  addPropertyChangeListener(allowanceUpdate);

The call to getModel() will retrieve the value model for the subject property of the book model. When you want to change the instance of the model that is being edited, you can use the presentation model's setBean() to load the new instance. If the listener had been attached to the our object, we would have to move the listener to the new instance ourselves. This is why I prefer going through the presentation model.

Finally, the allowance model needs to somehow be connected to the UI component. The line to do this:

PropertyConnector connection =
  PropertyConnector.connect(nameField, "enable",
    allowed, Allowance.PROPERTY_ALLOWANCE);
allowed.setAllowed(false);
connection.updateProperty1();

The PropertyConnector will automatically listen for changes to the Allowance and update the "enable" property of the component. Note that the "enable" property could have also been substituted for "visible" to have it change its visibility or &quoy;editable" to change whether it is editable or not. Using any of these properties will effectively control whether the user is able to edit the contents of the text field. There are also a few other properties that can be controlled in this way, such as "focusable" and "opaque". Files from this tutorial are downloadable from here

While in the example an Allowance model was created for the purpose of modelling when a component can be used or not, it should be noted that the binding library provides ComponentValueModel to control three of the properties – visible, editable and enabled. The model provides three separate methods for controlling the properties separately – setEditable, setEnabled and setVisible. Instead of using PropertyConnector to bind the model to the UI component, you should using Binding.addComponentPropertyHandler instead.

Advertisements

Binding with JGoodies Binding

The binding framework allows you to write classes that represents model completely independent of the user interface. The classes are bound to the user interface and are updated as the user interface enters information. The frame is based on the presentation pattern, described by Martin Fowler.

The framework already provides a Model class. Classes containing attributes that are editable on the user interface would be a subclass of this. It adds functionality to notify of events. By default, the frame assumes that the used naming conventions for the properties, accessors and mutator methods follow the same pattern as suggested in the Java beans specification. For the models that I create, I provide the property as a public constant within the model. The mutator code also need to call the model’s firePropertyChange method to notify listeners of a change to the property. For example, a model for a book could be the following:


public class Book extends Model 
{
    

    /**
     * References to the properties for the model.
     */
    public static final String PROPERTY_TITLE = "title";
    
    

    /**
     * Keeps track of the title of the book. 
     */
    private String title = " ";

    

    /**
     * Changes the title of the book.
     * 
     * @param newTitle
     *             The new title for the book.
     */
    public void setTitle(String newTitle) 
    {
        String oldTitle = title;
        title = newTitle;
        firePropertyChange(PROPERTY_TITLE, oldTitle, 
                newTitle);
    }
    
    /**
     * @return The title of the book.
     */
    public String getTitle() 
    {
        return title;
    }
    
    
}

For creating the user interface components that will be editing the model, the PresentationModel is created for the object. The PresentationModel provides a representation of the user interface. The framework also supplies a BasicComponentFactory that you can use to create the components. The factory will also bind the component to the appropriate attribute. The methods that create text fields require a value model as one of its paramters. The value model is what is actually updated when the user makes changes in the component and it should be retrieved from the PresentationModel using its getModel method. For example, to create a text field for entering the title of a book:

        
        final Book subject = new Book();
                
        final PresentationModel<Book> presentation =
            new PresentationModel<Book>(subject);
        
        JTextField nameField = BasicComponentFactory.
            createTextField(presentation.getModel(
                Book.PROPERTY_TITLE), false);
        

The third parameter, which is set to false will cause the bounded instance to be updated as the user makes changes in the text field. Setting it to true will result in the changes being made only when the field loses focus instead.

In the case of list selection fields, such as a JList and JComboBox, the required model is a SelectionInList. SelectionInList provides the functionality for tracking changes in the selection. Setting up a SelectionInList might require a few different calls.

    
    public static final String TOPICS[] = 
    {"science", "history", "arts"};
    
        SelectionInList<String> topicSelect =
            new SelectionInList<String>(TOPICS);

        /**
         * Set the presentation model as the model that
         * holds the selected value.
         */
        topicSelect.setSelectionHolder(
            presentation.getModel(
                Book.PROPERTY_TOPIC));
        
        JComboBox topicBox = BasicComponentFactory.
            createComboBox(topicSelect);
        

The SelectionInList class has a few different constructors that allow you to specify the set of possible options as a list or an array. The call to setSelectionHolder will make updates to the presention model as the selection of the value is changed.

The BasicComponentFactory can also create JLabel that displays the current value of an attribute in the model. The label gets updated as the value of the attribute changes.

        
        JLabel titleLabel = BasicComponentFactory.
            createLabel(presentation.getModel(
                Book.PROPERTY_TITLE));
        
        JLabel topicLabel = BasicComponentFactory.
            createLabel(presentation.getModel(
                Book.PROPERTY_TOPIC));
        

Since the title property has been bound to the title text field, the titleLabel will be automatically updated when the user makes changes in the field. Similarly, topic property has been bound such that when the user changes its selection, it the topicLabel will be updated. If you wish to try out the examples from here, you can get the source code here.