JavaFX: How to get stage from controller during initialization?

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

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.


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