2017-02-22 108 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 
相關問題