将JavaFx属性绑定到多个可观察的

时间:2017-02-22 16:19:15

标签: java javafx reactfx

我正在编写一个JavaFx控件,它由一个获取用户输入的子控件组成,例如:一个TextField。主要组件包含一个属性,该属性表示文本的解析表示,例如,一个LocalDateTime。当用户输入内容时,应该更新此属性,因此我将其绑定到子的value属性。还应该可以通过绑定value属性来从外部更改当前值,因此这必须是双向绑定以自动更新子项。控件正常工作,直到客户端代码绑定到属性。以下代码显示了我的问题:

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);         
    }
}

当我运行代码时,我得到一个异常,即无法设置绑定属性:

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)

我确信这一定是一个常见问题,我只是没有看到明显的解决方案。我正在使用ReactFx,因此欢迎使用普通JavaFx或ReactFx的任何解决方案。真实代码使用Var.mapBidirectional在内部绑定Parent和Child属性。

我想要实现的目标如下: 1.如果outside的值发生变化,则应将其传播到p.value然后传播到p.a1.p 2.如果p.a1.p发生变化,则应将其传播到p.value

由此我得出结论,Parent.value和Parent.a1.p总是相同的(加上映射中应用的一些转换),我使用的是bidrectional mapping。外部可以独立变化,可以与值不同,所以我使用单向绑定。

1 个答案:

答案 0 :(得分:0)

在理解JavaFx Bindings不是我认为的那样并且语义不允许这样做之后,我切换到ReactFx's EventStream/Val/Var。 ReactFx的模型似乎更符合我的期望,这使我能够创建一个行为如下的系统:

  1. 如果外部的值发生变化,则应将其传播到p.value然后再传播到p.a1.p
  2. 如果p.a1.p发生变化,则应将其传播到p.value
  3. 以下代码实现了这一点。它使用两个Var s,它们被双向绑定,因此其中一个的变化被馈送到另一个Var。来自外部的更改被定义为EventStream,其被提供给Var之一。

    import org.reactfx.EventStream;
    import org.reactfx.EventStreams;
    import org.reactfx.value.Var;
    
    import javafx.beans.property.SimpleIntegerProperty;
    
    public class Playbook {
    
        static class Child {
            public Var<Integer> p = Var.newSimpleVar(0);
    
            public void set(int i) {this.p.setValue(i);}
        }
    
        static class Parent {
            public Child a1;
    
            public Var<Integer> value = Var.newSimpleVar(0);
    
            public Parent() {
                this.a1 = new Child();
    
                this.value.bindBidirectional(this.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();
    
            // A SimpleIntegerProperty is not a Property<Integer> but a 
            // Property<Number> so we have to use map to translate to Integer
            EventStream<Integer> stream = EventStreams.valuesOf(outside).map(Number::intValue);
    
            stream.feedTo(p.value);
    
            // simulate a change in the child control
            p.a1.p.setValue(10);
            System.out.println( p.value.getValue() );
            System.out.println(p.a1.p.getValue() );
            System.out.println(outside.get());
    
            // feed new value from outside
            outside.set(42);
    
            System.out.println( p.value.getValue() );
            System.out.println(p.a1.p.getValue() );
            System.out.println(outside.get());
        }
    }
    

    程序运行并打印

    10
    10
    0
    42
    42
    42