Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to bind a List's size property to a Node's disabled property?

Suppose I have an ObservableList and a Button in the same controller class:

private ObservableList<NameItem> _selectedList = _database.getONameList();

@FXML
private Button nextButton;

How do I make it so that the Button is only enabled while the ObservableList is not empty? Is there a binding property I can use to set this?

like image 266
bigfocalchord Avatar asked Jan 27 '23 09:01

bigfocalchord


1 Answers

This can be done fairly easily with just a couple of simple Bindings, actually.

First, you want to create an IntegerBinding that is bound to the size of your ObservableList:

IntegerBinding listSize = Bindings.size(_selectedList);

Then create a new BooleanBinding that is bound to whether or not the listSize binding is greater than 0:

BooleanBinding listPopulated = listSize.greaterThan(0);

Now, all you need to do is bind the button's disableProperty to the opposite of the listPopulated property using the not() method (since listPopulated will be true if items are in the list, you want to actually pass false to the button's disableProperty):

button.disableProperty().bind(listPopulated.not());

Here is a quick MCVE to demonstrate:

import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.binding.IntegerBinding;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class Main extends Application {

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

    @Override
    public void start(Stage primaryStage) {

        // Simple interface
        VBox root = new VBox(5);
        root.setPadding(new Insets(10));
        root.setAlignment(Pos.CENTER);

        // Create an empty ObservableList
        ObservableList<String> list = FXCollections.observableArrayList();

        // Create a binding to extract the list's size to a property
        IntegerBinding listSizeProperty = Bindings.size(list);

        // Create a boolean binding that will return true only if the list has 1 or more elements
        BooleanBinding listPopulatedProperty = listSizeProperty.greaterThan(0);

        // Create the button we want to enable/disable
        Button button = new Button("Submit");

        // Bind the button's disableProperty to the opposite of the listPopulateProperty
        // If listPopulateProperty is false, the button will be disabled, and vice versa
        button.disableProperty().bind(listPopulatedProperty.not());

        // Create another button to add an item to the list, just to demonstrate the concept
        Button btnAddItem = new Button("Add Item");
        btnAddItem.setOnAction(event -> {
            list.add("New Item");
            System.out.println(list.size());
        });

        // Add the buttons to the layout
        root.getChildren().addAll(btnAddItem, button);

        // Show the Stage
        primaryStage.setScene(new Scene(root));
        primaryStage.show();
    }
}

In the above example, the "Submit" button is disabled until you add an item to the ObservableList using the "Add Item" button.

EDIT: As Lukas excellently points out in the comments below, these Bindings can also all be chained together to simplify things (either method is equally valid; it just depends on which you find more readable, really):

button.disableProperty().bind(Bindings.size(list).greaterThan(0).not())

Another Method

Another way to do this is with a ListChangeListener that enables or disables the button any time the list changes:

    list.addListener((ListChangeListener<String>) c -> {
        // If the size of the list is less than 1, disable the button; otherwise enable it
        button.setDisable(c.getList().size() < 1);
    });

This will essentially do exactly the same thing as the first method, but you'll need to set the initial state of the button yourself before the listener can keep it updated for you.

like image 146
Zephyr Avatar answered Feb 04 '23 14:02

Zephyr