JavaFX8 : Single Controller for 1+1popup scene

101 views Asked by At

After following the tutorial at http://www.javafxtutorials.com/tutorials/switching-to-different-screens-in-javafx-and-fxml/, I have a functioning popup window, however, I am having difficulty figuring out how to interact with it with a controller.

From the tutorial, I got the impression that there is to be a shared controller between the two FXML files, but I'm having trouble referencing the new stage.

To this, I have a few questions.

@FXML
private void toOutput(ActionEvent event) throws Exception {
    Stage stage;
    Parent root;
    stage = new Stage();
    root = FXMLLoader.load(getClass().getResource("TextWindow.fxml"));
    stage.setScene(new Scene(root));
    stage.initModality(Modality.APPLICATION_MODAL);
    stage.initOwner(searchBox.getScene().getWindow());
    stage.showAndWait();
}

1) When the above is run, does the stage.showAndWait() create a new scene instance with a new controller, or does it work off of the existing scene controller?

2a) If it uses a new controller, is this beneficial? I'm guessing the controller goes through some other wrapping to build things, so it may only pull what it needs off of the @FXML tags, but I have local variables and etc that seems like it's not a good idea

2b) If it uses the existing controller, how do I reference the variables local to the class instance? It's probably extremely simple, but FXML in general is still quite alien to me.

3) If I were to use a completely separate controller for (presuming I cannot get it to work with a single controller), how would I pass a data element from one controller to another? I see that the initialize() method has an argument for a ResourceBundle, but I'm not certain how to utilize this.


Answer Edit

Correct Code:

@FXML
private void toOutput(ActionEvent event) throws Exception {
    Stage stage = new Stage();
    Parent root;

    FXMLLoader loader = new FXMLLoader();
    loader.setLocation(getClass().getResource("OutputWindow.fxml"));
    root = loader.load(); 
    OutputWindowController controller = loader.getController();
    controller.setMat(mat);

    stage.setScene(new Scene(root));
    stage.initModality(Modality.APPLICATION_MODAL);
    stage.initOwner(searchBox.getScene().getWindow());
    stage.showAndWait();

}
1

There are 1 answers

5
aw-think On BEST ANSWER

You need to get the (new) controller that was set (if one) by the fxml.

  FXMLLoader loader = new FXMLLoader();
  loader.setLocation(getClass().getResource("TextWindow.fxml"));
  root = loader.load(); 
  Controller controller = loader.getController(); // type need to be put here
  controller.setCallingController(this); // this could be any method you need

Please do not try to use one controller already assinged by another fxml to any other fxml, this is not the correct way. So you only need to get the controller from the loader. The Controller in the above code is only a placeholder for your controller class that you have set to the fxml (you not mentioned). If you don't have already set the controller in the fxml like this:

fx:controller="package.to.your.Controller"

please set it there. Calling the load() method at the loader goes in a generic way (Thanks to James_D), so it returns the type of component your fxml uses as root.

Answers to your questions:

1) Stage is like the Window or like a JFrame in Swing, so a new Stage will be created with a new Controller (if set).

2a) Every fxml needs an own controller (because of resource injection). It's about object identity. In your mentioned tutorial it's a bit hiding, but on every call to FXMLLoader.load(fxml-file) it will create a new controller instance of the FXMLDocumentController class. And if this would'nt be worse enough, one of the buttons always points to null because of resource injection. And now think of a huge app with more than one button on a panel! How often you will make an if-else-statement to know which button was pressed? This is a bad pattern and from my point of view "old fashioned style" of swing code. Don't use it.

2b) Don't use existing Controller it will fail. Simply call a method on the new Controller and add this Controller with getter and setter.

3) ResourceBundle are for what the name is mention, mostly used to internationalize your application.