Skip to main content

Automatic Binding

Posted by evanx on September 1, 2008 at 10:55 AM PDT

In the >Gooey MvnC prequel, we advocate an MVC-type architecture for
programming a Swing UI panel, using convention-over-configuration
to automate event handling, thread switching and beans binding.

In the Gooey Event Proxy prequel,
we introduced a minimalistic helper class towards the above strategy,
illustrating the automatic wiring of events, for starters.

Now we present automatic binding
by convention-over-configuration, in order to bind components in the view
to properties in the presentation model, automatically,
by matching component names
to property names.

style="text-decoration: none;">


Salient Code

Our helper binds components to bean properties as follows.

public class GEventHelper {
    protected Map<Field, Component> components = new HashMap();
    ...
    public void bind(GBean bean) {
        GBeanInfo beanInfo = new GBeanInfo(bean.getClass());
        for (Component component : components.values()) {
            GPropertyInfo propertyInfo =
                    beanInfo.getPropertyInfo(component.getName());
            if (propertyInfo != null) {
                inputComponentAdapterFactory.create(component).bind(
                        new GBeanProperty(bean, propertyInfo));
            }
        }
    }
}

where GBeanInfo and GPropertyInfo are wrappers for java.beans.BeanInfo
and PropertyDescriptor, and GBeanProperty ties in a reference to
a bean instance.

For example, GTextFieldBinding below is used to establish a binding
between a JTextField and a bean property.

public class GTextFieldBinding
        implements ActionListener, FocusListener, PropertyChangeListener {
    JTextField component;
    GBeanProperty property;

    public GTextFieldBinding(JTextField component, GBeanProperty property) {
        this.component = component;
        this.property = property;
        updateComponent();
        property.addPropertyChangeListener(this);
        component.addActionListener(this);
        component.addFocusListener(this);
    }
    ...
}   

where the binding implements various listeners, and registers itself
as a listener on the component's events eg. FocusEvent, and
also as a PropertyChangeListener on the bean property.

In the focusLost() event handler below, we push the edited value into
the bean property.

    @Override
    public void focusLost(FocusEvent event) {
        updateProperty();
    }
   
    public void updateProperty() {
        try {
            property.parse(component.getText());
        } catch (GPropertyException e) {
            property.handleException(e);
        }
    }

In propertyChange() below, we invoke updateComponent()
to pull the value from the bean into the component.

    @Override
    public void propertyChange(PropertyChangeEvent event) {
        if (event.getPropertyName().equals(property.getName())) {
            updateComponent();
        }
    }
   
    public void updateComponent() {
        try {
            component.setText(property.format());
        } catch (GPropertyException e) {
            property.handleException(e);
        }
    }

The full article is reposited
here.

Related Topics >>