Binding a Non-Bound Property
Swing and AWT components can be very frustrating because they generally don't bind any properties. As you know, bound properties are extremely useful in VisualAge for Java, especially because of VisualAge's property-to-property connections.
We'd like some way to make properties bound.
Binding a Swing Property
This is actually pretty easy with Swing, as JComponent already tracks
PropertyChangeListeners and has firePropertyChanged() methods
Using JTextField as an example, we'll bind its "text" property:
1: Create class BoundJTextField extending JTextField
NOTE: Make sure you specify that you want to make copies
of the constructors (this is checked by default on the
second page of the "Add class" smartguide)
ALSO: Use the second page of the smartguide to add imports
for:
com.sun.java.swing.* com.sun.java.swing.event.* com.sun.java.swing.text.*
or if you're using Swing >=1.1
javax.swing.* javax.swing.event.* javax.swing.text.*
2: Create method firePropertyChange()
This may seem weird, but you need this to be able to access the firePropertyChange() method in
JComponent from an inner class. I'm not sure whether to think of this as a bug in VAJ's compiler or
not...
protected void firePropertyChange(String propertyName,
Object oldValue,
Object newValue) {
super.firePropertyChange(propertyName, oldValue, newValue);
}
3: Create method createListeners()
This method is used to add some listeners to the document (the model)
of the JTextField. Whenever the model changes, this listener is
informed and will fire a PropertyChangeEvent.
protected void createListeners() {
getDocument().addDocumentListener(new DocumentListener() {
public void insertUpdate(DocumentEvent e) {
firePropertyChange("text", null, getText());
}
public void removeUpdate(DocumentEvent e) {
firePropertyChange("text", null, getText());
}
public void changedUpdate(DocumentEvent e) {
firePropertyChange("text", null, getText());
}
});
}
As far as I can see, because the new DocumentListener isn't a subclass of
JComponent, and isn't in the same package, it cannot directly access
JComponent's firePropertyChange() method...
4: Add a call to createListeners() at the end of each constructor
/**
* BoundJTextField constructor comment.
*/
public BoundJTextField() {
super();
createListeners();
}
/**
* BoundJTextField constructor comment.
* @param columns int
*/
public BoundJTextField(int columns) {
super(columns);
createListeners();
}
/**
* BoundJTextField constructor comment.
* @param doc com.sun.java.swing.text.Document
* @param text java.lang.String
* @param columns int
*/
public BoundJTextField(Document doc, String text, int columns) {
super(doc, text, columns);
createListeners();
}
/**
* BoundJTextField constructor comment.
* @param text java.lang.String
*/
public BoundJTextField(String text) {
super(text);
createListeners();
}
/**
* BoundJTextField constructor comment.
* @param text java.lang.String
* @param columns int
*/
public BoundJTextField(String text, int columns) {
super(text, columns);
createListeners();
}
5: Make the BeanInfo realize it's bound
In order to use this text property as a bound property in the VCE, we need to
explicitly list it as bound in a BeanInfo class. This is necessary
because the BeanInfo for JTextField or JTextComponent (probably
the latter) explicitly lists the text property as not bound, so we need
to override it.
To do this:
- Open
BoundJTextFieldto the BeanInfo editor
- "Generate BeanInfo class" from the features menu
- "Add available features" from the features menu
- Select the "text" property -- note that it's only defined as read/write -- we need to change that!
- Select the text property in the features pane, and change its "bound" status to true.
- Save the beaninfo (you need to right-click somewhere other than the "bound" row to get the popup menu to save)
You now have a bound "text" property.
Other Swing components are similar. Just remember:
- listen for changes to the model of the component
- call
fireProperytChange()with "null" as the oldValue (unless you
have easy access to the old value). Do not pass the same value
for old/new or no event will be fired.