67

I need to close the current fxml window by code in the controller

I know stage.close() or stage.hide() do this in fx

how to implement this in fxml? I tried

private void on_btnClose_clicked(ActionEvent actionEvent) {
        Parent root = FXMLLoader.load(getClass().getResource("currentWindow.fxml"));    
        Scene scene = new Scene(root);

        Stage stage = new Stage();            
        stage.setScene(scene);
        stage.show();
}

but it doesn't work!

All help will be greatly appreciated. Thanks!

Pablo Fernandez
  • 279,434
  • 135
  • 377
  • 622
alfredo138923
  • 1,509
  • 1
  • 15
  • 15

9 Answers9

184
  1. give your close button an fx:id, if you haven't yet: <Button fx:id="closeButton" onAction="#closeButtonAction">
  2. In your controller class:

    @FXML private javafx.scene.control.Button closeButton;
    
    @FXML
    private void closeButtonAction(){
        // get a handle to the stage
        Stage stage = (Stage) closeButton.getScene().getWindow();
        // do what you have to do
        stage.close();
    }
    
a.b
  • 9,414
  • 5
  • 26
  • 22
  • 4
    This approach did not work for me. I receive a `NullPointerException` – Austin Feb 11 '16 at 05:13
  • 1
    amazing Works ! , just in case, people remember check the name of your Button (this case closeButton)and the name of your onClick (this case closeButtonAction) – diego matos - keke May 04 '16 at 15:10
  • 1
    I get this error when I use this code. Exception in thread "JavaFX Application Thread" java.lang.ClassCastException: com.sun.javafx.stage.EmbeddedWindow cannot be cast to javafx.stage.Stage – ryan.s Sep 12 '17 at 15:32
  • 3
    @ZinMinn if you want to close the app, just call Platform.exit(). Closing a stage is different than closing the app (you can have multiple stages). – Pablo Fernandez Dec 20 '17 at 19:53
  • This solution is perfect to close a secondary window. `closeButton` must be a button in the window to close and the method `closeButtonAction()` must be in the same class also. – Orici Apr 29 '18 at 16:59
  • @AustinD probably your Button in Controller is declared as private – Antizam Aug 18 '18 at 18:01
  • He forgot the prameter ActionEvent event – Yeshwin Verma Mar 19 '21 at 08:34
25

If you have a window which extends javafx.application.Application; you can use the following method. (This will close the whole application, not just the window. I misinterpreted the OP, thanks to the commenters for pointing it out).

Platform.exit();

Example:

public class MainGUI extends Application {
.........

Button exitButton = new Button("Exit");
exitButton.setOnAction(new ExitButtonListener());
.........

public class ExitButtonListener implements EventHandler<ActionEvent> {

  @Override
  public void handle(ActionEvent arg0) {
    Platform.exit();
  }
}

Edit for the beauty of Java 8:

 public class MainGUI extends Application {
    .........

    Button exitButton = new Button("Exit");
    exitButton.setOnAction(actionEvent -> Platform.exit());
 }
codepleb
  • 10,086
  • 14
  • 69
  • 111
12

I implemented this in the following way after receiving a NullPointerException from the accepted answer.

In my FXML:

<Button onMouseClicked="#onMouseClickedCancelBtn" text="Cancel">

In my Controller class:

@FXML public void onMouseClickedCancelBtn(InputEvent e) {
    final Node source = (Node) e.getSource();
    final Stage stage = (Stage) source.getScene().getWindow();
    stage.close();
}
Austin
  • 8,018
  • 2
  • 31
  • 37
4

If you don't want to overspread your controller with fxml linked methods you can do something like this:

You have to give it a "fx:id", like "closeButton" for example. Should look like this in your FXML file:

<Button fx:id="closeButton" layoutX="876.0" layoutY="74.0" mnemonicParsing="false" textAlignment="CENTER">

Then you can just code the button in your initialize method or wherever you want:

@FXML
Button closeButton

public initialize(){
    closeButton.setOnAction(new EventHandler<ActionEvent>(){
        @Override
        public void handle(ActionEvent e){
            ((Stage) closeButton.getScene().getWindow()).close();
        }
    });
}
Laurel
  • 5,965
  • 14
  • 31
  • 57
2

Hide doesn't close the window, just put in visible mode. The best solution was:

@FXML
private void exitButtonOnAction(ActionEvent event){
        ((Stage)(((Button)event.getSource()).getScene().getWindow())).close();      
}
Jean Castro
  • 311
  • 2
  • 6
1

I'm not sure if this is the best way (or if it works), but you could try:

private void on_btnClose_clicked(ActionEvent actionEvent) {

        Window window = getScene().getWindow();   

        if (window instanceof Stage){
            ((Stage) window).close();
        }
}

(Assuming your controller is a Node. Otherwise you have to get the node first (getScene() is a method of Node)

Puce
  • 37,247
  • 13
  • 80
  • 152
1
stage.setOnCloseRequest(new EventHandler<WindowEvent>() {
    public void handle(WindowEvent we) {                        
        Platform.setImplicitExit(false);
        stage.close();
    }
});

It is equivalent to hide. So when you are going to open it next time, you just check if the stage object is exited or not. If it is exited, you just show() i.e. (stage.show()) call. Otherwise, you have to start the stage.

Adowrath
  • 701
  • 11
  • 24
Om Prakash
  • 793
  • 7
  • 14
1

I found a nice solution which does not need an event to be triggered:

@FXML
private Button cancelButton;

close(new Event(cancelButton, stage, null));

@FXML
private void close(Event event) {
    ((Node)(event.getSource())).getScene().getWindow().hide();                      
}
António Ribeiro
  • 4,129
  • 5
  • 32
  • 49
lolo
  • 11
  • 1
1

finally, I found a solution

 Window window =   ((Node)(event.getSource())).getScene().getWindow(); 
            if (window instanceof Stage){
                ((Stage) window).close();
            }