Joel
Joel

Reputation: 473

JavaFX binding StringProperty and IntegerProperty

I have a question about binding in JavaFX. Let's say I have mapping 1:"Aaa", 2:"Bbb", 3:"Ccc" and so on. What I want is to bind (bidirectional) 2 properties IntegerProperty and StringProperty according to this mapping. How this can be achieved?

UPD: I'll try to describe my use case. I have a class

class A {
   IntegerProperty num;
   ...
}

But in the user interface I want to show not this numerical value but some meaningful string. So I want to add StringProperty numValue and bind num and numValue. In table I'll use numValue as property for TableColumn. And when user change value of numValue (via combobox for example) I want num automatically update.

Upvotes: 1

Views: 7411

Answers (1)

Tomas Mikula
Tomas Mikula

Reputation: 6537

I recommend to avoid bidirectional bindings if you can, they are problematic1.

If you really need a bidirectional binding, then you are lucky that one of your types is String. There is the bindBidirectional method that let's you specify a StringConverter used to convert between a String and the other type T, in your case Integer.

Map<Integer, String> m = ...;
StringProperty sp = ...;
IntegerProperty ip = ...;

Bindings.bindBidirectional(sp, ip, new StringConverter<Number>() {

    @Override
    public Integer fromString(String s) {
        for(Integer key: m.keySet()) {
            if(m.get(key).equals(s)) {
                return key;
            }
        }
        return -1; // or whatever makes sense for you
    }

    @Override
    public String toString(Integer i) {
        return m.get(i);
    }
});

Why this is supported for types String and T and not for arbitrary types T and U is a mystery for me.

In more general terms, you have two types, T, U, and two functions, f: T -> U and g: U -> T, and then you want to establish a bidirectional binding between Property<T> and Property<U> using these two functions. In the above example, both f and g are realized by the StringConverter (for example, f is fromString and g is toString). You can establish such a bidirectional binding using ReactFX's Vars2:

Function<T, U> f = ...;
Function<U, T> g = ...;
Var<T> vt = ...;
Var<U> vu = ...;

Var<U> vu1 = vt.map(f).asVar(u -> vt.setValue(g.apply(u)));
Bindings.bindBidirectional(vu, vu1);

You don't necessarily need f to be the inverse of g, i.e. you don't need g(f(x)) = x, but in order to avoid infinite loop (stack overflow), it better converge quickly, for example f(g(f(x))) = f(x), or g(f(g(f(g(f(g(f(x)))))))) = g(f(g(f(g(f(x)))))) or something like that.


1 I realize I just threw this statement in without providing any valid argument.
2 Var is really just a Property with some extra methods.

Upvotes: 5

Related Questions