1

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();

}
Cœur
  • 37,241
  • 25
  • 195
  • 267
Captain Prinny
  • 459
  • 8
  • 23
  • 1
    With the usual setup, `FXMLLoader.load(...)` will create a controller instance for you. It is almost always a mistake to use the same controller class for two different FXML files. You need to pass data to the new controller, which is covered completely in http://stackoverflow.com/questions/14187963/passing-parameters-javafx-fxml/14190310#14190310 – James_D Jun 16 '15 at 20:42

1 Answers1

4

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.

aw-think
  • 4,723
  • 2
  • 21
  • 42
  • `getController()` and `load()` are generic methods, so in Java 8 you don't need a cast (the compiler will be able to infer the type). – James_D Jun 16 '15 at 20:53
  • well the answer is correct as well its correct the one about avoid one controller with many stages , which can be panels, so its pretty useful that java fx it self allows you to bind to each panel the specific - generic controller – AntJavaDev Jun 16 '15 at 21:10
  • @AntJavaDev Sounds interesting, never thougt about that - generic controller. Thank you. – aw-think Jun 16 '15 at 21:13
  • @James_D Thank you for clearing out the generic way of load and getController. I've updated my answer. – aw-think Jun 17 '15 at 07:22
  • Thank you for the updated edit, now I can actually really start to see what's going on. – Captain Prinny Jun 17 '15 at 14:28