Alberto Amat Alvarez
Alberto Amat Alvarez

Reputation: 95

javafx: How to bind disable button with dynamic created checkboxes?

I want to bind the disable of a button with dynamically created checkboxes. The Button should be enabled if a checkbox is selected.

This is my code

public class DietTabPageController {
    @FXML
    private FlowPane parent;
    @FXML
    private Button okButton;

    private ObservableList<CheckBox> checkBoxes=FXCollections.observableArrayList();

    @FXML
    private void initialize() {
        ObservableList<Diet> diets = DietDAO.getDiets();
        diets.forEach(diet -> checkBoxes.add(new CheckBox(diet.getName())));
        //checkboxes added in parent Flowpane
        parent.getChildren().addAll(checkBoxes);
    }
}

Any suggestions? Thanks

Upvotes: 0

Views: 2121

Answers (2)

Halko Karr-Sajtarevic
Halko Karr-Sajtarevic

Reputation: 2278

You can use JavaFX's really nice Bindings-class! Try this:

okButton.disableProperty().bind(
    Bindings.createBooleanBinding(
        ()->!checkBoxes.stream().anyMatch(CheckBox::isSelected), 
        checkBoxes.stream().map(x->x.selectedProperty()).toArray(Observable[]::new)
    )
);

This creates a new Binding, which will listen on every checkbox and then call the given function to calculate the value of your property. Additional reading here: Bindings

Regarding your comment: I don't know how much you can edit your Diet class, but if you can, there is a very simple way to display your checkboxes and add the button-binding. Take a look at the following sample:

ListView<Diet> dietsView = new ListView<>(diets);
dietsView.setCellFactory(CheckBoxListCell.forListView(diet -> 
diet.selectedProperty()));

btn.disableProperty().bind(
    Bindings.createBooleanBinding(
        () -> !diets.stream().anyMatch(diet->diet.isSelected()),
        diets.stream().map(x->x.selectedProperty())
        .toArray(Observable[]::new)
    )
);

add this to Diet class:

private final BooleanProperty selected = new SimpleBooleanProperty();

public final BooleanProperty selectedProperty() {
    return this.selected;
}

public final boolean isSelected() {
    return this.selectedProperty().get();
}

public final void setSelected(final boolean on) {
    this.selectedProperty().set(on);
}

Upvotes: 2

fabian
fabian

Reputation: 82491

You need to add listeners to all the selected properties of the CheckBoxes. Every time one of the property changes, modify the Button's disable property, if necessary. BTW: Making checkBoxes observable doesn't seem necessary:

private List<CheckBox> checkBoxes;

@FXML
private void initialize() {
    ObservableList<Diet> diets = DietDAO.getDiets();
    checkBoxes = new ArrayList<>(diets.size());

    ChangeListener<Boolean> listener = (o, oldValue, newValue) -> {
        if (newValue) {
            // activate button since at least one CheckBox is selected
            okButton.setDisable(false);
        } else {
            // disable button, if the last CheckBox was unselected
            for (CheckBox cb : checkBoxes) {
                if (cb.isSelected()) {
                    return; // don't do anything, if there still is a selected CheckBox
                }
            }
            okButton.setDisable(true);
        }
    };

    for (Diet diet : diets) {
        CheckBox cb = new CheckBox(diet.getName());
        cb.selectedProperty().addListener(listener);
        checkBoxes.add(cb);
    }

    //checkboxes added in parent Flowpane
    parent.getChildren().addAll(checkBoxes);
}

Upvotes: 0

Related Questions