3

I use FXMLLOADER to load a fxml file SignInUI.fxml in LogUIController. The code is here:

Stage signIn = new Stage();
FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("SignInUI.fxml"));
SignInUIController signInUIController = new SignInUIController();
signInUIController.setStage(signIn);
Scene sceneSignIn = new Scene(fxmlLoader.load());
signIn.setTitle("Sign In");
Image icon = new Image("calculator.jpg");
signIn.getIcons().add(icon);
signIn.setScene(sceneSignIn);
signIn.show();

I wrote a method called setStage in SignInUIController, which can assign the instance variable stage:

public Stage stage;

public void setStage(Stage stage) {
    this.stage = stage;
}

I tried to build a SignInUIController instance in LogUIController and call the setStage method. Lastly, the cancel method in SignInUIController tied to a button and use the instance variable stage to close the stage:

@FXML
private void cancel() throws IOException {
    stage.close();
}

But every time, it has an error:Cannot invoke "javafx.stage.Stage.close()" because "this.stage" is null. I do not know why, and how to fix this?

  • Firstly, if `cancel` is a `Button's` `onAction` method, it should have `private void cancel(ActionEvent actionEvent)`. Secondly, my guess is that you are looking for the following. https://stackoverflow.com/a/41838183/2423906 – SedJ601 Jul 26 '23 at 05:28

2 Answers2

3

You are not binding the controller on the running stage, thus the instance you are placing the parent stage, is not the one linked with the actual rendered window. In case you want to set it programmatically check this answer.

Sample :

  FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("SignInUI.fxml"));
  fxmlLoader.setController(new SignInUIController());
AntJavaDev
  • 1,204
  • 1
  • 18
  • 24
  • I bind the controller in the fxml file. But I fix this by creating a new instance variable which is the ahchorPane and use this: stage = (Stage) anchorPane.getScene().getWindow(); Now I just want to know why my previous try always returns a null stage. – Cao Dingjie Jul 26 '23 at 07:15
  • 1
    At your 3d line, you are creating a new instance of the SignInUIController. This instance is not controlled by the Java FX application so the state for the parent stage is always null. In case you want to reuse the same instance check also this [link](https://stackoverflow.com/questions/14370183/passing-parameters-to-a-controller-when-loading-an-fxml) – AntJavaDev Jul 26 '23 at 07:41
  • @CaoDingjie, You are doing things all wrong. The first thing I notice is that you never do `fxmlLoader.load` and also you never do `fxmlLoader.getController`. See https://stackoverflow.com/questions/32342864/applying-mvc-with-javafx – SedJ601 Jul 26 '23 at 13:47
  • @SedJ601 Cao does call `Scene sceneSignIn = new Scene(fxmlLoader.load());` in his question. Cao could get the controller from the loader after loading as you suggest, with some refactoring, or Cao could set the controller as in AntJavaDev's answer, similar to the setting controllers section of [Passing Parameters JavaFX FXML](https://stackoverflow.com/questions/14187963/passing-parameters-javafx-fxml), but also see the comment there *"To set a controller on a loader (in JavaFX 2.x) you CANNOT also define a fx:controller attribute in your fxml file."*, not sure that applies to recent JavaFX. – jewelsea Jul 26 '23 at 23:11
  • @jewelsea, that's for the info. I also missed `Scene sceneSignIn = new Scene(fxmlLoader.load());` – SedJ601 Jul 27 '23 at 01:40
1

There's probably a better way of doing this, but here's a quick, alternative fix.

Try instantiating your controller using the .getController() class of the FXMLLoader object. Also you can try getting the Window of the scene you have created and cast that to a stage. I made sure to call the setStage method only after setting the scene so that the Scene's Window isn't null.

Stage signIn = new Stage();
FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("SignInUI.fxml"));
SignInUIController signInUIController = fxmlLoader.getController();
Scene sceneSignIn = new Scene(fxmlLoader.load());
signIn.setTitle("Sign In");
Image icon = new Image("calculator.jpg");
signIn.getIcons().add(icon);
signIn.setScene(sceneSignIn);
signInUIController.setStage(sceneSignIn.getWindow());
signIn.show();

And then in the setStage function of your controller, try setting the parameter to Window instead of Stage:

public void setStage(Window _window){
    // set the stage variable in your class to the casted window
    this.primaryStage = (Stage) _window; 
}