2014-10-21 54 views
1

我知道Java和C#,但屬性綁定我只知道從C#MVVM。 我想了解JavaFX與自定義getters和setter屬性值(如在C#中)的屬性綁定。JavaFX綁定設置器公約

我創建了以下類:

public class ViewModel { 

    private StringProperty name; 

    public ViewModel() { 
     name = new SimpleStringProperty(); 
    } 

    public final String getName() { 
     return name.get(); 
    } 

    public final void setName(String name) { 
     this.name.set(name); 
    } 

    public StringProperty getNameProperty() { 
     return name; 
    } 
} 

public class Controller implements Initializable { 

    @FXML 
    private TextField nameField; 

    private final ViewModel viewModel; 

    public Controller() { 
     viewModel = new ViewModel(); 
    } 

    @Override 
    public void initialize(URL url, ResourceBundle resourceBundle) { 
     Bindings.bindBidirectional(nameField.textProperty(), 
       viewModel.getNameProperty()); 
    } 
} 

我下的印象是,如果我按照推薦的JavaBean/JavaFX的命名約定,那麼綁定系統會足夠聰明的使用反射和使用(?)我的屬性的自定義getter/setter。但我的視圖模型getter/setter從未使用。

相反,綁定使用屬性的get/set方法直接沒有我的交互。 我讀,我可以使用下面的代碼,但必須有比這更好的方式:

name = new SimpleStringProperty() { 
    @Override public void set(String value) { 
     // do something... 
     super.set(value); 
    } 
}; 

我可以指定哪些方法綁定應該使用獲取/設置我的財產? 另外,我怎麼能通知該屬性已經改變(NotifyOfPropertyChange()在C#中),而不必改變它?

編輯:

我所試圖做的是多一點選擇,以被設置爲我的財產,因爲我想用它的價值以後,以填充我的數據模型是什麼結束(在這裏省略)。

在C#中這是微不足道的,我只是在setter中設置一個謂詞。通過設置其他屬性,我可以推動表單/嚮導的進度。

public String Property { 
    get { return _property; } 
    set { 
     if(SomePredicate(value)) { 
      _property = value; 
      _nextButtonCommand.canExecute() = true; 
      // notify... 
     } 
    } 
} 
+0

更新回答以迴應編輯。 – 2014-10-22 12:02:26

回答

3

JavaFX的屬性

在您ViewModel類中定義name財產的約定是:

public class ViewModel { 

    private StringProperty name; 

    public ViewModel() { 
     // parameters are owning bean, property name, and initial value, 
     // and are optional for the property convention 
     name = new SimpleStringProperty(this, "name", ""); 
    } 

    public final String getName() { 
     return name.get(); 
    } 

    public final void setName(String name) { 
     this.name.set(name); 
    } 

    // Note this method name: 
    public final StringProperty nameProperty() { 
     return name; 
    } 
} 

有一些變種:特別是如果你想的東西覆蓋的,您可以從nameProperty()中刪除final修飾符,並在get和se中用this.nameProperty()替換this.name t方法。關鍵在於確保呼叫setName(...)總是給出與nameProperty().set(...)相同的結果(對於get方法也是如此)。

我認爲你的一般解釋在某種程度上是關閉的(如果這是有道理的話)。 StringProperty類定義了getset方法,這些方法將由於綁定而「自動」調用。所以(即使你有一個非標準的命名約定),輸入文本字段仍然會更新屬性值。就我所知,綁定API不會使用(太多?)反射 - 它只是向偵聽器註冊屬性,並在另一個更改時更新。

至於自定義getter和setter,那些實際上只有通過您顯示的重寫技術才支持屬性類。但是,我從來沒有真正發現過這樣的用例,特別是綁定API,它可以讓您很容易地創建依賴值。

更新

因此,對於你具體的例子,我會實現它大致如下:

public class ViewModel { 
    private StringProperty name = new SimpleStringProperty(this, "name"); 
    // usual JavaFX Property methods... 
} 

然後,無論你需要它:

Predicate<String> predicate = ... ; 
BooleanBinding canExecute = Bindings.createBooleanBinding(() -> 
    predicate.test(viewModel.getName()), 
    viewModel.nameProperty()); 

,然後你可以做類似

Button nextButton = new Button("Next"); 
nextButton.disableProperty().bind(canExecute.not()); 

如果斷言可能會改變,你甚至可以做

ObjectProperty<Predicate<String>> predicate = new SimpleObjectProperty<>(s -> true); 
BooleanBinding canExecute = Bindings.createBooleanBinding(() -> 
    predicate.get().test(viewModel.getName()), 
    predicate, viewModel.nameProperty()); 

裏有Bindings class很多很多的工廠方法創建綁定,並且如果需要的話,你也可以繼承抽象ObjectBindingStringBinding等類。

請注意,此方法與問題中建議的方法之間存在細微的變化:在問題的方法中(子類爲SimpleStringProperty),確定動作是否可執行的邏輯由字符串屬性保存。在這種方法中,它被分解成了一個觀察字符串屬性的不同對象(通過綁定,它實際上在字符串屬性上註冊了一個WeakInvalidationListener)。

通知

可以註冊InvalidationListenersChangeListeners與屬性。 addListener(InvalidationListener)方法從Observable繼承,並指示上次觀察到的值可能不再有效。這允許「懶惰評估」觀測值,它只在請求時才計算新的值。 addListener(ChangeListener)方法從ObservableValue繼承,該名稱(如其名稱所示)是明確包裝值的可觀察值。註冊ChangeListener會強制進行急切的評估,因爲聽者會收到新的價值通知。該API對我來說有點過於細緻,儘管它爲高性能實現提供了很多靈活性。

所以在您的測試應用程序,你可以做

viewModel.nameProperty().addListener(new ChangeListener<String>() { 
    @Override 
    public void changed(ObservableValue<? extends String> obs, String oldValue, String newValue) { 
     System.out.println("Name changed from "+oldValue+" to "+newValue); 
    } 
}); 

或者,在Java 8中,極大地更加簡潔

viewModel.nameProperty().addListener((obs, oldName, newName) -> 
    System.out.println("Name changed from "+oldValue+" to "+newValue)); 

的區別無效聽衆之間,改變的聽衆,如果你只是變得明顯具有取決於計算的ObservableValue。比較:

IntegerProperty x = new SimpleIntegerProperty(2); 
IntegerProperty y = new SimpleIntegerProperty(3); 
ObservableNumberValue sum = x.add(y); 

sum.addListener(obs -> System.out.println("Invalidated")); // invalidation listener 
x.set(3); 
y.set(5); 

代碼相同,但與變化監聽器:

sum.addListener((obs, oldSum, newSum) -> System.out.println("Changed")); 

,而不是無效聽衆。

tutorial有更多的細節。