2017-02-22 105 views
0

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

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映射。外部可以独立改变并且可以不同于值,所以我使用单向绑定。

+2

'p.value.bind(外);'意味着'p.value'将始终具有相同的值'value'。 'p.value.bindBidirectional(p.a1.p)'表示'p.value'和'p.a1.p'将始终具有相同的值。显然,这些规则不能同时执行 - 特别是如果您明确地将“p.a1.p”设置为与“outside”不同的值。你正在得到一个例外,因为你试图同时执行矛盾的规则。没有解决办法;你可以声明'int i;'并且问你怎么可以同时使'i = 0'和'i = 10'。 –

+0

@James_D我不知道我能否遵循这个推理。 Parent.value和Child.p应始终具有相同的值。但用'p.value.bind(outside)'(这是一个单向绑定?),我想我是说“当外部变化时,p.value将被改变为相同的值。当'p.value'改变'outside'时不会改变,因此可以有不同的值。我尝试将此视为外部 - >值<-> p。 – Jens

+0

这不是绑定的意思。 'p.value.bind(outside)'意味着'p.value'被*绑定到*'outside'外:即'p.value'将始终与'outside'具有相同的值。 –

回答

0

在了解JavaFx绑定不是我认为的那样并且语义不允许的情况下,我切换到ReactFx'sEventStream/Val/Var。 ReactFx的模式似乎是一个更好的匹配我的意料,让我创造行为如下系统:

  1. 如果外面的值发生变化,这应该被传播到p.value再到p.a1.p
  2. 如果p.a1.p变化,这应该被传播到p.value

下面的代码实现这一点。它使用两个Var,它们是双向绑定的,因此其中一个的变化被馈送到另一个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 
相关问题