UI logic with using JavaFX Bindings

For MQTT.fx I needed a ComboBox to enter new Topics or to choose recent topics from list.
But I want the user to enter or choose only (more or less) reasonable values otherwise the “Publish”/”Subscribe” buttons should be disabled:

ComboBox Bindings Demo 1

ComboBox Bindings Demo 2

Using the JavaFX Bindings API prevents some boilerplate code (implementing Listeners) and it is even more flexible as the result can be bound to several controls and at the end of the day its much more readable.

Contraint 1: The selection must not be empty.

  1. First create a JavaFX BooleanBinding.
  2. Then bind it to the disabledProperty of the button.

Contraint 2: Entered values must not contain only blanks (leading/trailing blanks can be trimmed later).

My approach is to

  1. add an EventFilter and to consume the event in case of only entered character is “SPACE”
  2. and if there are just blanks left when editing the value the content is cleared.
public class ComboBoxBindingsDemoController {

    @FXML
    private Label valueLabel;

    @FXML
    private Button goButton;

    @FXML
    private ComboBox<String> valueComboBox;

    private ObservableList<String> demoItemsList;
    private static final Pattern LEADING_BLANKS_PATTERN = Pattern.compile("^\\s*+");

    @FXML
    public void initialize() {
        valueComboBox.setItems(getDemoItemsList());

        BooleanBinding goButtonState = valueComboBox.editorProperty().get().textProperty().isEmpty()
                .or(valueComboBox.valueProperty().asString().isEmpty());

        goButton.disableProperty().bind(goButtonState);

        valueComboBox.getEditor().addEventFilter(KeyEvent.KEY_TYPED, (KeyEvent event) -> {
            Matcher matcher = LEADING_BLANKS_PATTERN.matcher(valueComboBox.getEditor().getText());
            if (" ".equals(event.getCharacter()) && valueComboBox.getEditor().getText().length() == 0) {
                event.consume();
            }
            else if(matcher.matches()){
                valueComboBox.getEditor().clear();
            }
        });

    }

    public ObservableList<String> getDemoItemsList() {
        if (demoItemsList == null) {
            demoItemsList = FXCollections.observableArrayList();
            demoItemsList.add("Erlangen");
            demoItemsList.add("Nürnberg");
            demoItemsList.add("Büchenbach");
        }
        return demoItemsList;
    }

    @FXML
    public void goAction() {
        System.out.println("Value '" + valueComboBox.getValue().trim() + "'");
    }

}

Complete runnable demo can be found at Bitbucket.

Leave a Comment