JavaFX FXML 对话框 - 无法使用 X 按钮关闭它

JavaFX FXML Dialog - can't close it with the X button

我需要完成一个 JavaFX 对话框。我将控制器 class 添加到准备好的 FXML

<?xml version="1.0" encoding="UTF-8"?>

<?import java.net.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>
<?import javafx.scene.image.*?>
<?import javafx.scene.image.Image?>


<Dialog fx:id="dialog"
    fx:controller="myapp.AddDialogController"
    xmlns:fx="http://javafx.com/fxml">
    <dialogPane>
        <DialogPane prefWidth="400.0" prefHeight="300.0">
            <stylesheets>
                <URL value="@/css/styles.css" />
            </stylesheets>
            <content>
                <VBox>
                    <Label text="Add some content here..."></Label>
                </VBox>
            </content>
        </DialogPane>
    </dialogPane>
</Dialog>

现在,当我尝试使用桌面环境提供的 X 按钮关闭对话框时,没有任何反应。我做错了什么?

解决方案 - 将按钮添加到对话框

查看以下问题的答案:

  • Enter Key Event Is Not Working On Dialog In Javafx?

I also recommend setting at least a CANCEL_CLOSE button or OK_DONE button in your JavaFX Alert, otherwise the user may have a difficult time actually closing the alert as the dialog will probably not respond to key presses as the user expects.

尝试在 window 框架中使用操作系统的 X 按钮关闭舞台可能也是如此。

背景信息

要进一步了解该行为,请阅读 Dialog javadoc:

中的对话框关闭规则

It is important to understand what happens when a Dialog is closed, and also how a Dialog can be closed, especially in abnormal closing situations (such as when the 'X' button is clicked in a dialogs title bar, or when operating system specific keyboard shortcuts (such as alt-F4 on Windows) are entered). Fortunately, the outcome is well-defined in these situations, and can be best summarised in the following bullet points:

  • JavaFX dialogs can only be closed 'abnormally' (as defined above) in two situations:
    1. When the dialog only has one button, or
    2. When the dialog has multiple buttons, as long as one of them meets one of the following requirements:
      1. The button has a ButtonType whose ButtonBar.ButtonData is of type ButtonBar.ButtonData.CANCEL_CLOSE.
      2. The button has a ButtonType whose ButtonBar.ButtonData returns true when ButtonBar.ButtonData.isCancelButton() is called.
  • In all other situations, the dialog will refuse to respond to all close requests, remaining open until the user clicks on one of the available buttons in the DialogPane area of the dialog.
  • If a dialog is closed abnormally, and if the dialog contains a button which meets one of the two criteria above, the dialog will attempt to set the result property to whatever value is returned from calling the result converter with the first matching ButtonType.
  • If for any reason the result converter returns null, or if the dialog is closed when only one non-cancel button is present, the result property will be null, and the showAndWait() method will return Optional.empty(). This later point means that, if you use either of option 2 or option 3 (as presented earlier in this class documentation), the Optional.ifPresent(java.util.function.Consumer) lambda will never be called, and code will continue executing as if the dialog had not returned any value at all.

所以,如果对话框中没有按钮,它就无法关闭...

后续问题的回答

And can I set the button type from FXML?

也许吧,我不知道,我没试过。无论如何我都不建议这样做,相反我建议只从代码中设置按钮类型。老实说,我可能只会在 FXML 中定义对话框的内容窗格(例如,只是 FXML 的 VBox 部分及其子元素),而将其余的封闭对话框定义留在 Java代码。我很惊讶在 FXML 中定义一个 Dialog 甚至可以工作,但它确实有效:-) 但是,您不能在 SceneBuilder 中以 Dialog 作为根 XML 元素打开 FXML (并且使用 SceneBuilder 来定义 UI 的能力是 IMO 首先使用 FXML 的最佳理由之一)。

示例解决方案

这是一个对话框的对话框定义示例,可以通过对话框中的按钮或组合键或单击操作系统提供的对话框中的关闭图标关闭:

AddDialogController.java

package myapp.ui;

public class AddDialogController {}

DialogDisplayApp.java

package myapp.ui;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

import java.io.IOException;

public class DialogDisplayApp extends Application {

    @Override
    public void start(Stage stage) throws IOException {
        FXMLLoader loader = new FXMLLoader(
                getClass().getResource(
                        "add-dialog.fxml"
                )
        );

        stage.setScene(new Scene(new VBox(new Label("Main Window")), 600, 400));
        stage.show();

        Dialog dialog = loader.load();
        dialog.getDialogPane().getButtonTypes().addAll(
                ButtonType.CLOSE
        );

        dialog.initOwner(stage);
        dialog.showAndWait();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

myapp/ui/add-dialog.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Dialog?>
<?import javafx.scene.control.DialogPane?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.VBox?>
<Dialog fx:id="dialog"
        fx:controller="myapp.ui.AddDialogController"
        xmlns:fx="http://javafx.com/fxml">
  <dialogPane>
    <DialogPane prefWidth="400.0" prefHeight="300.0">
      <content>
        <VBox>
          <Label text="Add some content here..."></Label>
        </VBox>
      </content>
    </DialogPane>
  </dialogPane>
</Dialog>