A long standing issue (some call it - arguably - feature :) is the weakness of all listeners installed by all fx-bindings. As a consequence, we can't build "chains" of properties without keeping a strong reference to each link of the chain.
A particular type of such a chain link is a JavaBeanProperty: its purpose is to adapt a javabean property to a fx-property. Typically, nobody is interested in the adapter as such, so its usage would do something like
private Parent createContentBean() {
...
// local ref only
Property property = createJavaBeanProperty();
Bindings.bindBidirectional(label.textProperty(), property, NumberFormat.getInstance());
.. wondering why the label isn't updated. Changing property to a strong reference will work as expected (leaving me puzzeld as to who is responsible to feed the dummy, but that's another question):
Property property;
private Parent createContentBean() {
...
// instantiate the field
property = createJavaBeanProperty();
Bindings.bindBidirectional(label.textProperty(), property, NumberFormat.getInstance());
Long intro, but nearly there: jdk8 somehow changed the implementation so that the first approach is now working, there's no longer any need to keep a strong reference to a JavaBeanProperty. On the other hand, custom implementations of "chain links" still need a strong reference.
Questions:
- is the change of behaviour intentional and if so, why?
- how is it achieved? The code looks very similar ... and I would love to try something similar in custom adapters
A complete example to play with:
public class BeanAdapterExample extends Application {
private Counter counter;
public BeanAdapterExample() {
this.counter = new Counter();
}
Property property;
private Parent createContentBean() {
VBox content = new VBox();
Label label = new Label();
// strong ref
property = createJavaBeanProperty();
// local property
Property property = createJavaBeanProperty();
Bindings.bindBidirectional(label.textProperty(), property, NumberFormat.getInstance());
Slider slider = new Slider();
slider.valueProperty().bindBidirectional(property);
Button button = new Button("increase");
button.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent paramT) {
counter.increase();
}
});
content.getChildren().add(label);
content.getChildren().add(slider);
content.getChildren().add(button);
return content;
}
protected JavaBeanDoubleProperty createJavaBeanProperty(){
try {
return JavaBeanDoublePropertyBuilder.create()
.bean(counter).name("count").build();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
return null;
}
@Override
public void start(Stage stage) throws Exception {
Scene scene = new Scene(createContentBean());
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
Application.launch(args);
}
public static class Counter {
private double count;
public Counter() {
this(0);
}
public Counter(double count) {
this.count = count;
}
/**
* Increases the counter by 1.
*/
public void increase() {
setCount(getCount()+ 1.);
}
/**
* @return the count
*/
public double getCount() {
return count;
}
/**
* @param count the count to set
*/
public void setCount(double count) {
double old = getCount();
this.count = count;
firePropertyChange("count", old, getCount());
}
PropertyChangeSupport support = new PropertyChangeSupport(this);
public void addPropertyChangeListener(PropertyChangeListener l) {
support.addPropertyChangeListener(l);
}
public void removePropertyChangeListener(PropertyChangeListener l) {
support.removePropertyChangeListener(l);
}
protected void firePropertyChange(String name, Object oldValue,
Object newValue) {
support.firePropertyChange(name, oldValue, newValue);
}
}
}
BTW: added the Swing tag because adapting core beans will be a frequent task in migration