How to return value from a stage before closing it?

I have a "main stage" where I press a button to open a "second stage" where I have a table, the user selects one item of the the table and click on "asignar" button (which is just a confirm button), once clicked, it must return the code of the item selected in the table to the main stage and close the second stage.

Here is the code that matters.

I have an INT variable which must take the value of a function:

codigo = controller.setVista(this, usuario, password);

The "setVista" function goes like this:

public int setVista(ListHorarios vista, String usuario, String password) {
this.vista = vista;
this.usuario = usuario;
this.password = password;
this.inicializarTabla();
this.actualizarTabla(0, "%");
   
btnSeleccionar.setOnAction(e -> {
    asignarSeleccion();
    Stage stage = (Stage) btnSeleccionar.getScene().getWindow(); 
    stage.close();
});
    return codigo_horario;
}

And the "asignarSeleccion" like this:

private void asignarSeleccion() {
    final HorarioTableModelo aux_horario = getTablaSeleccionada();
    posicion = datos.indexOf(aux_horario);
    if (aux_horario != null) {
        codigo_horario = aux_horario.getCodigo();
    }
}

My problem is that I can't get the "codigo_horario" value into the first variable "codigo" before the stage closes, what do I am missing?


Here is a possible example. The structure is the same as in the answer in my comment.

The second Stage is opened through a "controller" that is stores the data that should be returned even when the Stage is closed and exposes a getter to be used to retrieve the value from the outer world.

import javafx.application.Application;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox;


public class Main extends Application {

    @Override
    public void start(Stage primaryStage) {
        try {
            BorderPane root = new BorderPane();
            Scene scene = new Scene(root,400,400);

            Button bSecondStage = new Button("Show second Stage");
            bSecondStage.setOnAction(e -> {
                WindowController wc = new WindowController();
                wc.showStage();
                System.out.println(wc.getData());
            });

            root.setCenter(bSecondStage);


            primaryStage.setScene(scene);
            primaryStage.show();
        } catch(Exception e) {
            e.printStackTrace();
        }
    }

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

    class WindowController {
        private String data;

        void showStage() {
            Stage stage = new Stage();
            stage.initModality(Modality.APPLICATION_MODAL);

            VBox root = new VBox();
            Scene scene = new Scene(root);
            TextField tf = new TextField();
            Button submit = new Button("Submit");

            submit.setOnAction(e -> {
                data = tf.getText();
                stage.close();
            });

            root.getChildren().addAll(tf, submit);
            stage.setScene(scene);
            stage.showAndWait();
        }

        String getData() {
            return data;
        }
    }
}

You can write your own Stage class with a return statement.

public class MyStage extends Stage {
   public String showAndReturn(myFXControll controll) {     
      super.showAndWait();
      return controll.getReturn();
   }
} 

After that you have to define a return function to your controller.

public class TableFilterControll implements Initializable {
    @Override
    public void initialize(URL arg0, ResourceBundle arg1) {
    }
    public String getReturn() {
        return "I'm a nice return value"; //return what you want controlled by your controller class
    }
}

Now you can controll your return from the parent controller.

String retValue=myStage.showAndReturn(childControll);

System.out.println(retValue);

I think this is a good solution for clean code. And you can style your FXML with Screne Builder.


There is some error in 4baad4's example. If the method in the controller is iCanGetDataBeforeClose, then that's what should be called:

String someValue = controller.iCanGetDataBeforeClose();

But even that didn't work right for me. I actually got this to work without using setOnCloseRequest at all. In the form controller, I had a method like this:

public boolean getCompleted() {
    return this.finished;
}

Then in the form calling it:

FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("myView.fxml"));
AnchorPane pane = (AnchorPane) loader.load();
myViewController controller = loader.getController();
Scene scene = new Scene(pane);
Stage stage = new Stage();
stage.setScene(scene);
stage.showAndWait();
if (controller.getCompleted()){
    doStuff();
}

One might think that since the stage had exited that the controller would throw a null reference exception, but it didn't, and it returned the correct response.

This solution works and is simplest proposed IMHO.