I have a simple bean that has some properties related with each other. For example, this bean has a property called discountRate and another called discountValue. The discountRate is the percentage (%) of discount applied to a sale. The discountValue is the value($) of discount applied to a sale. As the user can inform either the percentage or the value and I need store the two values in the database, a JavaFX bidirectional binding would solve the problem, however, as you can imagine, these values are correlated but aren't the same. I tried solve this problem creating bindings in the two sides:
public class ExampleBean{
private ObjectProperty<BigDecimal> discountValue;
private ObjectProperty<BigDecimal> discountRate;
public BigDecimal getDiscountvalue() {
return discountValueProperty().getValue();
}
public void setDiscountValue(BigDecimal discountvalue) {
this.discountValueProperty().set(discountvalue);
}
public ObjectProperty<BigDecimal> discountValueProperty() {
if(discountValue==null){
discountValue=new SimpleObjectProperty<BigDecimal>(new BigDecimal("0.00"));
discountRate=new SimpleObjectProperty<BigDecimal>(new BigDecimal("0.00"));
configureDiscountBinding();
}
return discountValue;
}
private void configureDiscountBinding(){
discountValue.bind(Bindings.createObjectBinding(new Callable<BigDecimal>() {
@Override
public BigDecimal call() throws Exception {
return getDiscountRate().multiply(getTotalValue()).divide(new BigDecimal("100"));
}
}, discountRateProperty()));
discountRate.bind(Bindings.createObjectBinding(new Callable<BigDecimal>() {
@Override
public BigDecimal call() throws Exception {
return getDiscountValue().multiply(new BigDecimal("100")).divide(getTotalValue());
}
}, discountValueProperty()));
}
public BigDecimal getDiscountRate() {
return discountRateProperty().getValue();
}
public void setDiscountRate(BigDecimal discountRate) {
this.discountRateProperty().set(discountRate);
}
public ObjectProperty<BigDecimal> discountRateProperty() {
if(discountRate==null){
discountRate=new SimpleObjectProperty<BigDecimal>(new BigDecimal("0.00"));
discountValue=new SimpleObjectProperty<BigDecimal>(new BigDecimal("0.00"));
configureDiscountBinding();
}
return discountRate;
}
}
As you could see, I'm trying calculate the percentage when the value is setted, and calculate the value when the rate is setted. The binding I tried above can't be bound, as this will enter in a eternal loop. Is there a way I can do a binding to solve this problem, or I need do the calculation inside setters?
You will need to listen to changes to the fields but keep track if the listener has been fired, so as not to fire again in endless loops. Inspiration was the actual code from JavaFX, decompiled here.
This is simplistic and cumbersome; if you are using this feature extensively, you could refactor the inner
ChangeListener
s to some common type, or some other clever solution.I tested the code above with the following main (you wil have to provide a
BigDecimal getTotalValue()
method, in my case I just returned a constantBigDecimal
):