Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use javafx.beans.binding.Bindings.select(...) for concise value binding

Overview

As a Swing developer of ten years, I've been thrilled with the features introduced with JavaFX 2.0, especially the rich, fluent, high-level data-binding facilities. This facility alone is worth the cost of learning a new API (which is much less since abandoning FX script). It's going to have a direct impact on the readability and maintainably of my model/view synchronization code.

So far I'm having great success at first level and basic derived bindings, but am struggling to figure out the "JavaFX way" of binding one value to a value two or more levels of indirection in the data graph.

Problem

As shown in the code example below, I'm attempting to use javafx.beans.binding.Bindings.select() to synchronize the text value of a Label with one of the contained properties of the currently selected item in a ComboBox. This code is a simple example of something more complex I'm trying to do, so I understand that it's not hard to do this with the lower level bindings API. I'd like to know if it's possible with the higher-level fluent API, and if the select(...) method actually tracks changes in the indirect properties (i.e. update property if either the direct property or the selected subproperty change).

The documentation and examples on select(...) are sparse, so I'm hoping someone with advanced experience with this can tell me if I'm trying to use the API as designed, or if there's another way to use the high-level binding API to do what I want.

Sample Code

Here's the demo code. When run, there's a ComboBox with two items in it, and then two labels. The first label shows the toString() version of the selected item. The second label attempts to display one of the properties of the selected item, but only displays 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();
        }
    }

}
like image 935
metasim Avatar asked Mar 22 '12 18:03

metasim


People also ask

What are binding properties used for in JavaFX?

JavaFX properties are often used in conjunction with binding, a powerful mechanism for expressing direct relationships between variables. When objects participate in bindings, changes made to one object will automatically be reflected in another object. This can be useful in a variety of applications.

What is double property in JavaFX?

Class DoubleProperty. This class defines a Property wrapping a double value. The value of a DoubleProperty can be get and set with ObservableDoubleValue. get() , DoubleExpression.

What is JavaFX beans property?

The package javafx. beans. property defines read-only properties and writable properties, plus a number of implementations.


1 Answers

Bindings.select can't access private class. Make MoPoJo a public class and your code will work.

public static class MoPoJo {

P.S: I believe that fact worth to be mentioned in docs, so I filed http://javafx-jira.kenai.com/browse/RT-20640 on JavaFX javadoc.

like image 192
Sergey Grinev Avatar answered Sep 21 '22 15:09

Sergey Grinev