Обзор
Как десятилетний разработчик Swing, я был в восторге от функций, представленных в JavaFX 2.0, особенно от богатых, быстрых и высокоуровневых средств привязки данных. Одно только это средство стоит затрат на изучение нового API (что намного меньше, чем отказ от сценария FX). Это окажет прямое влияние на удобочитаемость и поддержку кода моей модели / представления.
Пока что у меня большой успех на первом уровне и базовые производные привязки, но я изо всех сил пытаюсь выяснить "JavaFX-способ" привязки одного значения к значению двух или более уровней косвенности в графе данных.
Проблема
Как показано в примере кода ниже, я пытаюсь использовать javafx.beans.binding.Bindings.select()
для синхронизации текстового значения метки с одним из содержащихся в данный момент свойств выбранного элемента в ComboBox. Этот код - простой пример чего-то более сложного, что я пытаюсь сделать, поэтому я понимаю, что это не сложно сделать с помощью API привязок нижнего уровня. Я хотел бы знать, возможно ли это с помощью высокоуровневого беглого API, и действительно ли метод select(...)
отслеживает изменения косвенных свойств (т. Е. Обновляет свойство, если изменяется либо прямое свойство, либо выбранное свойство).
Документация и примеры по select(...)
немногочисленны, поэтому я надеюсь, что кто-то, имеющий опыт работы с этим, может сказать мне, пытаюсь ли я использовать API, как это было задумано, или есть другой способ использовать API привязки уровня, чтобы делать то, что я хочу.
Пример кода
Вот демонстрационный код. При запуске появляется ComboBox с двумя элементами, а затем две метки. Первая метка показывает версию выбранного элемента toString()
. Вторая метка пытается отобразить одно из свойств выбранного элемента, но отображает только null
.
import static javafx.beans.binding.Bindings.*;
import javafx.application.Application;
import javafx.beans.property.ReadOnlyStringProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
/** Testing cascading binding change triggers. */
public class SandboxTest extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage stage) throws Exception {
VBox root = new VBox(8);
root.setStyle("-fx-padding: 8;");
Scene s = new Scene(root);
stage.setWidth(200);
stage.setScene(s);
ComboBox<MoPoJo> list = new ComboBox<SandboxTest.MoPoJo>();
list.itemsProperty().set(FXCollections.observableArrayList(new MoPoJo("foo", "bar"), new MoPoJo("baz", "bat")));
Label direct = new Label();
direct.setTooltip(new Tooltip("Selected item to string"));
Label withSelect = new Label();
withSelect.setTooltip(new Tooltip("Second property of selected item"));
direct.textProperty().bind(convert(list.getSelectionModel().selectedItemProperty()));
withSelect.textProperty().bind(convert(select(list.getSelectionModel().selectedItemProperty(), "two")));
root.getChildren().addAll(list, direct, withSelect);
stage.show();
}
private static class MoPoJo {
private StringProperty _one = new SimpleStringProperty();
private StringProperty _two = new SimpleStringProperty();
private StringProperty _name = new SimpleStringProperty();
public MoPoJo(String o, String t) {
_one.set(o);
_two.set(t);
_name.bind(format("{ %s, %s }", oneProperty(), twoProperty()));
}
public StringProperty oneProperty() {
return _one;
}
public StringProperty twoProperty() {
return _two;
}
public ReadOnlyStringProperty nameProperty() {
return _name;
}
@Override
public String toString() {
return nameProperty().get();
}
}
}