简体   繁体   中英

Binding objects in JavaFX

I am very confused as how to bind an ObjectProperty to another Object using Object binding. From what I have read & understood in javaFX documentations, this MCVE should output 20, but it is not working. Output is coming to 0.

// I tried to bind the attribute B's Integer attribute in class A to class C's SimpleIntegerProperty

package sample;

import javafx.beans.binding.ObjectBinding;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty;

class ObjectBindingImpl extends ObjectBinding {
    private A a;

    public ObjectBindingImpl(A a) {
        this.a = a;
        bind(new SimpleObjectProperty<>(a.b));
    }

    @Override
    protected Object computeValue() {
        return a.b == null ? null : a.b.b;
    }
}
public class Main {

    public static void main(String[] args) {
        A a = new A();
        C c = new C();

        c.c.bind(new ObjectBindingImpl(a));
        a.b = new B(20); // Clearly I change the value to 20 here

        System.out.println(c.c.get()); // Expected output 20 as B.b = 20 but output returned is 10.
    }
}

class A {
    B b = new B(10); // Assigned it value 10.
}
class B {
    Integer b;
    B(int b) {
        this.b = b;
    }
}
class C {
    SimpleIntegerProperty c = new SimpleIntegerProperty();
}

As per my understanding, since I have called bind(new SimpleObjectProperty(ab)) so everytime the value of ab changes, the binding is invalidated, and then sometime later, it uses the computeValue() method impl to assign it updated value.

But it appears that whatever the first value assigned to it was does not change even after changing the data. What am I missing here? How can I make this same example work?

The problem in your code is, that you don't change the property, but only the underlying data. In order to make Property-Bindings recognise your change, you need to set the property:

    A a = new A();

    SimpleObjectProperty aProperty = new SimpleObjectProperty<>(a);
    SimpleObjectProperty bProperty = new SimpleObjectProperty<>(a.b);

    System.out.println(aProperty.get()); // A, because we set it to a.

    aProperty.bind(bProperty);

    System.out.println(aProperty.get()); // null, because a.b is null (and we bound the value from bProperty now)

    bProperty.set(new B()); // Set the bProperty. This will update the aProperty as well, because it is bound to bProperty

    System.out.println(aProperty.get()); 
    // B, because we set bProperty to B, and as aProperty is bound to bProperty, this will change aProperty to it as well.

This is also done in all JavaFX Controls. Just take a look in eg TableView :

private ObjectProperty<ObservableList<S>> items = new SimpleObjectProperty<ObservableList<S>>(this, "items");
public final ObjectProperty<ObservableList<S>> itemsProperty() { return items; }
public final void setItems(ObservableList<S> value) { itemsProperty().set(value); }
public final ObservableList<S> getItems() {return items.get(); }

So every setter will set the property. You then can bind the property to another one (or add a listener with addListener to react to changes).

More or less my explanation is similar to @Maran23. You can make the bindings work between observable properties only.

  • In the example you provided, the binding is between C.c and a "new SimpleObjectProperty" instance in the Binding class.

  • You are updating ab value, which is not an observable property.

  • To get the value reflected in C.c, the value in "new SimpleObjectProperty" should be updated. And this is not possible with your example because the instance is created on fly and there is no reference to update it.

  • So to make your example work, create a reference for the observable property you are creating in the ObjectBindingImpl and ensure you update this observable property value.

I would also like to correct one of your assumptions about

" ...and then sometime later, it uses the computeValue() method impl to assign it updated value "

It is does not update at some random time. It calls the computeValue only when you explicitly call the "get" method of the observable property. Untill the getter is called, the observable property will be in "invalid" state.

Please find below demo, with the changes I mentioned. Also note that there can be many other efficient ways to let the binding happen, but I am trying to answer your actual OP with the example you provided.

import javafx.beans.binding.ObjectBinding;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty;

public class ObjectBinding_Demo {

    public static void main(String[] args) {
        new ObjectBinding_Demo().init();
    }

    private void init() {
        A a = new A();
        C c = new C();

        ObjectBindingImpl binding = new ObjectBindingImpl(a);
        c.c.bind(binding);
        a.b = new B(20);
        binding.obj.setValue(a.b); // Update the observable property with the new value
        System.out.println("Value set in binding observable property..");
        System.out.println(c.c); // Output:: IntegerProperty [bound, invalid]
        System.out.println(c.c.get()); // Output:: 'Computing value...' string and then 20
        System.out.println(c.c); // Output:: IntegerProperty [bound, value: 20]

    }

    class ObjectBindingImpl extends ObjectBinding {
        private A a;
        ObjectProperty obj; // Observable property reference for future updating.

        public ObjectBindingImpl(A a) {
            this.a = a;
            obj = new SimpleObjectProperty(a.b);
            bind(obj);
        }

        @Override
        protected Object computeValue() {
            System.out.println("Computing value...");
            return a.b == null ? null : a.b.b;
        }
    }

    class A {
        B b = new B(10); // Assigned it value 10.
    }

    class B {
        Integer b;

        B(int b) {
            this.b = b;
        }
    }

    class C {
        SimpleIntegerProperty c = new SimpleIntegerProperty();
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM