José Cousiño
José Cousiño

Reputation: 399

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?

Upvotes: 3

Views: 11481

Answers (4)

Gary Malkasian
Gary Malkasian

Reputation: 11

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.

Upvotes: 1

4baad4
4baad4

Reputation: 27

In my main controller I create Stage. Load controller which I can use like any class. And by creating an event I can get data just before closing the window.

try {
        FXMLLoader loader = new FXMLLoader();
        loader.setLocation(getClass().getResource("/package/mySceneBuilderWindow.fxml"));
        final Pane rootPane = (Pane)loader.load();
        Scene scene =  new Scene(rootPane);

        Stage stage = new Stage();
        stage.setTitle("Optional title");
        stage.setScene(scene);

        mySceneBuilderWindowController controller = loader.<mySceneBuilderWindowController>getController();
        controller.iCanSendDataToCtrl("String for now"); // sending data or init textFields...

        stage.show();

        /* set event which is fired on close
            // How to close in the other window... (pressing X is OK too)
                @FXML private Button fxidSave = new Button(); // global var
                @FXML private void handleSaveButton() {
                    Stage stage = (Stage) fxidSave.getScene().getWindow();
                    stage.close(); // closes window
                }
         */
        stage.setOnCloseRequest((EventHandler<WindowEvent>) new EventHandler<WindowEvent>() {
            public void handle(WindowEvent we) {
                String iCanGetDataBeforeClose = controller.getData();
                System.out.println(iCanGetDataBeforeClose);
                // static class can be used aswell -> System.out.println(Context.getMyString());
            }
        });

    } catch (IOException e) {
        System.out.println("something wrong with .fxml - name wrong? path wrong? building error?");
    }  

My other mySceneBuilderWindowController methods:

 public void iCanSendDataToCtrl(String giveMe) {
    // do something ex. myTextBox.setText(giveMe);
 }
 public String iCanGetDataBeforeClose() {
    // do something ex. return myTextBox.getText();
 }

Upvotes: 0

Kollos
Kollos

Reputation: 21

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.

Upvotes: 2

DVarga
DVarga

Reputation: 21799

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;
        }
    }
}

Upvotes: 3

Related Questions