22.5. Binding Properties
By using a combination of events and adapters, we can connect beans in many interesting ways. We can even "bind" two beans together so that if a property changes in the first bean, the corresponding property is automatically changed in the second bean. In this scenario, the beans don't necessarily have to be of the same type, but, to make sense, the properties do.
Close the Molecule file and start a new one. Grab two NumericField beans from the palette, drop them in the workspace, and select one of them, as shown in Figure 22-7. You'll probably want to set the AbsoluteLayout again. You can also adjust the width of the fields by dragging them at the sides. You'll notice that a NumericField has many of the standard properties of a Swing component. If you look in the Other Properties section of the Properties pane, you can find an integer property called value that represents the numeric value of the field. You can set it there or enter a number directly into the field when you run the program. NumericField rejects nonnumeric text.
Figure 22-7. Binding properties
Let's bind the value property of one of the fields to the other. Activate the Connection Wizard to create a connection between the two fields. Click first on numericField1 and then on numericField2 so that numericField1 is the source. In the wizard, choose the propertyChange( ) event of the source field. This is the listener method for PropertyChangeEvent, a generic event sent by beans when one of their properties changes. When a bean fires property-change events in response to changes in a particular property, that property is said to be "bound." This means that it is possible to bind the property to another bean through the generic mechanism. In this case, the value property of our NumericField beans is a bound property, so whenever it changes, a PropertyChangeEvent is fired.
Choose Next, and select the value property as the target for numericField2. Click Next again, and select the Property radio button on the Parameters screen. Click the "..." editor button to pop up a Select Property dialog. Select the source numeric field (probably named numericField1, if that is your source button) from the pull-down menu, and then choose the value property. Click Ok and Finish to complete the hookup.
Run the application, and try entering values in the first field (numericField1). The second field should change each time. The second bean's value property has been bound to the first.
Try binding the value property in the other direction as well so that you can change the value in either bean, and the changes are propagated in both directions. (Some simple logic in the beans prevents infinite loops from happening here.)
NetBeans has again generated an adapter for us. This time the adapter listens for PropertyChangeEvents and invokes the setValue( ) method of our target field. We haven't done anything earth-shaking. The PropertyChangeEvent does carry some extra informationthe old and new values of the propertybut we're not using them here. And with the Connection Wizard, you can use any event source as the impetus to set a property on your target bean. Finally, as we've seen, the property can derive its value from any other bean in the layout. The flexibility of the Connection Wizard is, to some extent, masking the purpose of the events, but that's okay. If we are interested in the specific property that changed, or if we want to apply logic about the value, we can fill in the generated method with our own code.
Many Swing components have bound properties; they are usually documented in the Javadoc for the class.
22.5.1. Constraining Properties
In the previous section, we discussed how beans fire PropertyChangeEvents to notify other beans (and adapters) that a property has changed. In that scenario, the object that receives the event is simply a passive listener as far as the event's source is concerned. JavaBeans also supports constrained properties, in which the event listener gets to say whether it will allow a bean to change the property's value. If the new value is rejected, the change is cancelled; the event source keeps its old value.
The concept of constrained properties has not been heavily used in the normal operation of Swing, so we won't cover it in detail here. But it goes something like this. Normally, PropertyChangeEvents are delivered to a propertyChange( ) method in the listener. Constrained properties are implemented by delivering PropertyChangeEvents to a separate listener method called vetoableChange( ). The vetoableChange( ) method throws a PropertyVetoException if it doesn't like a proposed change. In this way, components can govern the acceptable values set by other components.