I am writing a JavaFx control that consists of a sub-control that gets user input, e.g. a TextField. The main component holds a property which represents a parsed representation of the text, e.g. a LocalDateTime. When the user enters something, this property should be updated, so I bind it to the child's value property. It should also be possible to change the current value from outside via binding the value property, so this has to be a bidirectional binding to update the child automatically. The control works fine until client code binds to the property. The following code shows my problem:
import javafx.beans.property.SimpleIntegerProperty;
public class Playbook {
// The internal control with a property p
static class Child {
public SimpleIntegerProperty p = new SimpleIntegerProperty();
public void set(int i) {p.set(i);}
}
// My new control with a property value which is bound
// bidirectionally to Child.p
static class Parent {
public Child a1;
public SimpleIntegerProperty value = new SimpleIntegerProperty();
public Parent() {
a1 = new Child();
value.bindBidirectional(a1.p);
}
}
public static void main(String[] args) {
Parent p = new Parent();
// some client code wants to keep the
// value updated and thus binds to it
SimpleIntegerProperty outside = new SimpleIntegerProperty();
p.value.bind(outside);
// simulate a change in the child control
p.a1.p.set(10);
}
}
When I run the code, I get an exception that a bound property cannot be set:
Caused by: java.lang.RuntimeException: A bound value cannot be set.
at javafx.beans.property.IntegerPropertyBase.set(IntegerPropertyBase.java:143)
at com.sun.javafx.binding.BidirectionalBinding$BidirectionalIntegerBinding.changed(BidirectionalBinding.java:467)
I am sure this must be a common problem and I am just not seeing the obvious solution. I am using ReactFx, so any solution with plain JavaFx or ReactFx would be welcome. The real code uses a Var.mapBidirectional
to bind the Parent and Child properties internally.
What I want to achieve is the following:
1. If outside
's value changes, this should be propagated to p.value and then to p.a1.p
2. If p.a1.p changes, this should be propagated to p.value
From this I concluded that Parent.value and Parent.a1.p are always identical (plus some tranformation applied in the mapping) and this I use a bidrectional mapping. outside can change independently and can be different to value, so I use a unidirectional binding.
After understanding that JavaFx Bindings are not what I thought it would be and that the semantics do not allow this, I switched to ReactFx's
EventStream/Val/Var
. ReactFx's model seems to be a better match to my expectation which allows me to create a system that behaves as follows:The following code implements this. It uses two
Var
s which are bound bidrectionally and thus changes in one of them are fed to the otherVar
. Changes from outside are defined as anEventStream
which is fed to one of theVar
s.The program runs and prints