bekwam courses

Stringify a List with JavaFX Binding

April 13, 2018

This article presents a snippet of code I used to to String-ify a List using JavaFX Binding. Given a List of domain objects, I wanted to form a custom String for display in a JavaFX Alert dialog. There are a few ways to do this, but I opted for Binding because of the streamlined syntax and because the rest of the class used Binding to link UI controls to data sources.

This sample program initializes a three-element List and displays the names as a comma-separated String in a Label. To demonstrate the dynamic nature of Binding, a Button is added that will add items to the List. When an item is added, the String expression in the Label is automatically updated.

The class begins with a domain object, Person. Person has a single field "name".

class Person {
  private final String name;
  public Person(String name) { this.name = name; }
  public String getName() { return name; }
}

The class begins by initializing an FXCollections ObservableList. This is filled with an initial set of three domain objects. The UI controls are added -- VBox, Label, Button -- to the Scene. I apply the binding expression and then show the Stage.

public class StringifyApp extends Application {

  private int counter = 1;

  @Override
  public void start(Stage primaryStage) throws Exception {

    ObservableList<Person> persons = FXCollections.observableArrayList();
    persons.add( new Person("Carl") );
    persons.add( new Person("Edvin") );
    persons.add( new Person("Thomas") );

    VBox vbox = new VBox();
    Label label = new Label("Empty");
    Button b = new Button("Add One");
    b.setOnAction( evt -> persons.add( new Person("Matt " + counter++)));

    vbox.setSpacing( 10.0d );
    vbox.setPadding( new Insets( 40.0d ));

    vbox.getChildren().addAll( label, b );

    Scene scene = new Scene( vbox, 480, 320 );

    primaryStage.setScene( scene );

    label.textProperty()
      .bind(
        Bindings.createStringBinding(
          () ->
            persons.stream().map( Person::getName ).collect(Collectors.joining(", ")),
         persons)
        );

    primaryStage.show();
  }

  public static void main(String[] args) {
    Application.launch(args);
  }
}

The interesting part of the code is the createStringBindingCall(). This links a Callback with an Observable item. The Callback is a Java Streams expression that extracts the name field of a Person objects, gathers the name fields in a List, and then joins them as a String using the comma as a delimiter. The Observable item is the ObservableArrayList "persons".

If you looked at the program into which I integrated this code, you'd see a lot of JavaFX Binding. Labels are bound to StringProperty fields. TableViews are bound to ObservableList. The style looks nice when I can keep everything consistent rather than adding InvalidationListeners or ChangeListeners. Moreover, this StringBinding can be used in other places by storing the result in a variable.

Bonus Kotlin Content

Edvin from the TornadoFX project wrote this Kotlin code which is a cooler version of my Streams expression.

  val join = Callable {
    people.joinToString("\n") { "${it.name};${it.age}" }
  }

  val b = Bindings.createStringBinding(join, people)

The ${} syntax is a better formatted string expression than the getter method reference in the JavaFX version or a MessageFormatter object.


Headshot of Carl Walker

By Carl Walker

President and Principal Consultant of Bekwam, Inc