How to bind visibility to controller in JavaFX


Question

I am trying to create a menu for my game using JavaFX. I want to have a "Continue" button that I only want to be visible when a game is running. I'm trying to bind the button's visibility property to a controller, but it doesn't appear to be working- the button is just invisible.

I have looked around Google and StackExchange, and I've found information on how to set it programmatically, but I'd rather not couple the Java code any tighter to the FXML than I really need to.

Here is my code for the FXML page...

<?xml version="1.0" encoding="UTF-8"?>
    <?import javafx.geometry.*?>
    <?import javafx.scene.control.*?>
    <?import javafx.scene.layout.*?>
    <?import javafx.scene.text.*?>

<GridPane fx:controller="com.silferein.erq.gui.MenuController" 
xmlns:fx="http://javafx.com/fxml" alignment="center" hgap="10" vgap="10">
    <padding><Insets top="25" right="25" bottom="25" left="25"/></padding>

    <Text id="welcome-text" text="Main Menu" GridPane.columnIndex="0" GridPane.rowIndex="0" GridPane.columnSpan="2"/>
    <Button text="New" onAction="#handleNew" GridPane.rowIndex="1"/>
    <Button text="Continue" onAction="#handleContinue" GridPane.rowIndex="2" visible="${controller.gameRunning}" />
    <Button text="Load" onAction="#handleLoad" GridPane.rowIndex="3"/>
    <Button text="Save" onAction="#handleSave" GridPane.rowIndex="4"/>
    <Button text="Quit" onAction="#handleQuit" GridPane.rowIndex="5"/>
</GridPane>

Here is my code for the controller (trimmed somewhat):

@FXML protected void handleContinue(ActionEvent event) {
    System.out.println("Continue!");
    parent.handle(new GUIEvent(GUIEvent.Type.CONTINUE));
}
// ...
@FXML protected boolean getGameRunning() {
    System.out.println("Test!");
    // Some check to see if there's a game in progress...
    return true;
}

Any idea what I'm doing wrong? My code compiles and all buttons except "Continue" are visible and working.

1
0
11/20/2014 3:45:18 AM

Accepted Answer

As sketched out by haisi, the missing link is to extend the Initialize() method to create a property binding from yout gameRunning boolean (Property) to the visibleProperty() of the button:

FXML:

xmlns:fx="http://javafx.com/fxml" alignment="center" hgap="10" vgap="10">
    <padding><Insets top="25" right="25" bottom="25" left="25"/></padding>

    <Text id="welcome-text" text="Main Menu" GridPane.columnIndex="0" GridPane.rowIndex="0" GridPane.columnSpan="2"/>
    <Button text="New" onAction="#handleNew" GridPane.rowIndex="1"/>
    <Button fx:id="continueBtn" text="Continue" onAction="#handleContinue" GridPane.rowIndex="2" />
    <Button text="Load" onAction="#handleLoad" GridPane.rowIndex="3"/>
    <Button text="Save" onAction="#handleSave" GridPane.rowIndex="4"/>
    <Button text="Quit" onAction="#handleQuit" GridPane.rowIndex="5"/>
</GridPane>

Class:

public class GameController implements Initializable {
    private BooleanProperty gameRunning = new SimpleBooleanProperty(false);

    @FXML Button continueBtn;

    @FXML protected void handleNew(ActionEvent event) {
        gameRunning.set(true);
    }
    @FXML protected void handleLoad(ActionEvent event) {

    }
    @FXML protected void handleSave(ActionEvent event) {

    }
    @FXML protected void handleQuit(ActionEvent event) {
        gameRunning.set(false);
    }
    @FXML protected void handleContinue(ActionEvent event) {
        System.out.println("Continue!");
        //parent.handle(new GUIEvent(GUIEvent.Type.CONTINUE));
    }
    @Override
    public void initialize(URL arg0, ResourceBundle arg1) {
        continueBtn.visibleProperty().bind(gameRunning);  
    }
}

I had the expectation, that it should be sufficient to implement gameRunning as full javafx bean property (setGameRunning, getGameRunning, gameRunningProperty) and the visible="${gameRunning}" shall work out automatically, but was unable to achieve that properly. It had been discussed in: Binding a Label's text property (in an FXML file) to an IntegerProperty (in a controller)

2
5/23/2017 10:28:57 AM

Create a BooleanProperty:

BooleanProperty isGameRunning = new SimpleBooleanProperty(false);

Give your 'continue'-button a fx:id (lets say btnContinue) and inject it into a controller.

@Inject Button btnContinue;

Bind the buttons visibility property to your isGameRunning property. (Do this in your initialize method)

btnContinue.visibleProperty().bind(isGameRunning);

Now whenever you change the value of isGameRunning, the visibility of your button will change.


Licensed under: CC-BY-SA with attribution
Not affiliated with: Stack Overflow
Icon