How to get stage from controller during initialization?

JavafxInitializationJavafx 2Javafx 8Stage

Javafx Problem Overview


I want to handle stage events (i.e. hiding) from my controller class. So all I have to do is to add a listener like this:

((Stage) myPane.getScene().getWindow()).setOn*whatIwant*(...);

But the problem is that initialization starts right after this code:

Parent root = FXMLLoader.load(getClass().getResource("MyGui.fxml"));

And before this code:

Scene scene = new Scene(root);
stage.setScene(scene);

Thus getScene returns null.

The only workaround I found by myself is to add a listener to myPane.sceneProperty, and when it becomes not null I get scene, add to it's windowProperty my listener handling which I finally retrieve stage. And it all ends with setting desired listeners to stage events.

I think there are too many listeners.

Is it the only way to solve my problem?

Javafx Solutions


Solution 1 - Javafx

You can get the instance of the controller from the FXMLLoader after initialization via getController(), but you need to instantiate an FXMLLoader instead of using the static methods then.

I'd pass the stage after calling load() directly to the controller afterwards:

FXMLLoader loader = new FXMLLoader(getClass().getResource("MyGui.fxml"));
Parent root = (Parent)loader.load();
MyController controller = (MyController)loader.getController();
controller.setStageAndSetupListeners(stage); // or what you want to do

Solution 2 - Javafx

All you need is to give the AnchorPane an ID, and then you can get the Stage from that.

@FXML private AnchorPane ap;
Stage stage = (Stage) ap.getScene().getWindow();

From here, you can add in the Listener that you need.

Edit: As stated by EarthMind below, it doesn't have to be the AnchorPane element; it can be any element that you've defined.

Solution 3 - Javafx

I know it's not the answer you want, but IMO the proposed solutions are not good (and your own way is). Why? Because they depend on the application state. In JavaFX, a control, a scene and a stage do not depend on each other. This means a control can live without being added to a scene and a scene can exist without being attached to a stage. And then, at a time instant t1, control can get attached to a scene and at instant t2, that scene can be added to a stage (and that explains why they are observable properties of each other).

So the approach that suggests getting the controller reference and invoking a method, passing the stage to it adds a state to your application. This means you need to invoke that method at the right moment, just after the stage is created. In other words, you need to follow an order now: 1- Create the stage 2- Pass this created stage to the controller via a method.

You cannot (or should not) change this order in this approach. So you lost statelessness. And in software, generally, state is evil. Ideally, methods should not require any call order.

So what is the right solution? There are two alternatives:

1- Your approach, in the controller listening properties to get the stage. I think this is the right approach. Like this:

pane.sceneProperty().addListener((observableScene, oldScene, newScene) -> {
	if (oldScene == null && newScene != null) {
		// scene is set for the first time. Now its the time to listen stage changes.
		newScene.windowProperty().addListener((observableWindow, oldWindow, newWindow) -> {
			if (oldWindow == null && newWindow != null) {
				// stage is set. now is the right time to do whatever we need to the stage in the controller.
				((Stage) newWindow).maximizedProperty().addListener((a, b, c) -> {
					if (c) {
						System.out.println("I am maximized!");
					}
				});
			}
		});
	}
});

2- You do what you need to do where you create the Stage (and that's not what you want):

Stage stage = new Stage();
stage.maximizedProperty().addListener((a, b, c) -> {
			if (c) {
				System.out.println("I am maximized!");
			}
		});
stage.setScene(someScene);
...

Solution 4 - Javafx

The simplest way to get stage object in controller is:

  1. Add an extra method in own created controller class like (it will be a setter method to set the stage in controller class),

    private Stage myStage;
    public void setStage(Stage stage) {
         myStage = stage;
    }
    
  2. Get controller in start method and set stage

    FXMLLoader loader = new FXMLLoader(getClass().getResource("MyFXML.fxml"));
    OwnController controller = loader.getController();
    controller.setStage(this.stage);
    
  3. Now you can access the stage in controller

Solution 5 - Javafx

Platform.runLater works to prevent execution until initialization is complete. In this case, i want to refresh a list view every time I resize the window width.

Platform.runLater(() -> {
    ((Stage) listView.getScene().getWindow()).widthProperty().addListener((obs, oldVal, newVal) -> {
        listView.refresh();
    });
});

in your case

Platform.runLater(()->{
    ((Stage)myPane.getScene().getWindow()).setOn*whatIwant*(...);
});

Solution 6 - Javafx

Assign fx:id or declare variable to/of any node: anchorpane, button, etc. Then add event handler to it and within that event handler insert the given code below:

Stage stage = (Stage)((Node)((EventObject) eventVariable).getSource()).getScene().getWindow();

Hope, this works for you!!

Solution 7 - Javafx

You can get with node.getScene, if you don't call from Platform.runLater, the result is a null value.

example null value:

node.getScene();

example no null value:

Platform.runLater(() -> {
    node.getScene().addEventFilter(KeyEvent.KEY_PRESSED, event -> {
               //your event
     });
});

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionChechulinView Question on Stackoverflow
Solution 1 - JavafxzhujikView Answer on Stackoverflow
Solution 2 - JavafxRobert MartinView Answer on Stackoverflow
Solution 3 - JavafxUtku ÖzdemirView Answer on Stackoverflow
Solution 4 - JavafxSandeep KumarView Answer on Stackoverflow
Solution 5 - JavafxAdamOutlerView Answer on Stackoverflow
Solution 6 - JavafxAnkit RajDeoView Answer on Stackoverflow
Solution 7 - Javafxfjqa86View Answer on Stackoverflow