【问题标题】:JavaFX Opening one dialog from anotherJavaFX 从另一个对话框打开一个对话框
【发布时间】:2015-12-14 15:39:50
【问题描述】:

我的应用程序中有以下工作流程导致问题:

单击按钮打开对话框 > 打开对话框 > 单击对话框中的按钮 > 显示确认警报 > 确认后关闭第一个对话框并打开一个新对话框

第二个对话框不允许输入到 TextField。我已经包含了一个显示问题的 SSCE。另外一件奇怪的事情是,如果您尝试通过单击“X”关闭第二个对话框,然后关闭警报,那么我就可以在该字段中输入。

public class DialogTest extends Application {

  @Override
  public void start(Stage primaryStage) {
    Button button = new Button("Show Dialog");

    VBox root = new VBox(10, button);
    root.setAlignment(Pos.CENTER);

    Scene scene = new Scene(root, 350, 120);
    primaryStage.setScene(scene);
    primaryStage.show();

    button.setOnAction(event -> {
      Dialog<Pair<String, String>> dialog = getDialog(scene.getWindow(), "Dialog 1", true);
      dialog.showAndWait();
    });
  }

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

  public Dialog<Pair<String, String>> getDialog(Window owner, String title, boolean addButton) {
    Dialog<Pair<String, String>> dialog = new Dialog<>();
    dialog.setTitle(title);
    dialog.initOwner(owner);
    dialog.getDialogPane().getButtonTypes().addAll(ButtonType.OK, ButtonType.CANCEL);

    if(addButton) {
      Button button = new Button("Show Dialog");
      dialog.getDialogPane().setContent(button);
      button.setOnAction(event -> {
        Alert alert = new Alert(AlertType.CONFIRMATION, "Are you sure?", ButtonType.YES, ButtonType.NO);
        alert.initOwner(owner);
        if(alert.showAndWait().get() == ButtonType.YES) {
          dialog.close();
          Dialog<Pair<String, String>> dialog2 = getDialog(owner, "Dialog 2", false);
          TextField textField = new TextField();
          dialog2.getDialogPane().setContent(textField);
          dialog2.getDialogPane().getScene().getWindow().setOnCloseRequest(closeEvent -> {
            closeEvent.consume();
            if(textField.getText().trim().isEmpty()) {
              Alert alert2 = new Alert(AlertType.ERROR, "Please enter a value", ButtonType.OK);
              alert2.initOwner(dialog2.getDialogPane().getScene().getWindow());
              alert2.showAndWait();
            }
          });
          dialog2.showAndWait();
        }
      });
    }

    return dialog;
  }
}

【问题讨论】:

  • 我没有注意到任何奇怪的行为,除了第二个对话框上的 TextField 没有显示插入的文本(但你可以在那里输入,并且 getText() 正在返回正确的字符串)。我无法确定此 TextField 错误的原因。单击 X 按钮后,第二个对话框不会关闭,因为您使用了此事件。如果您在文本字段中插入了一些文本(您看不到),最后的警报将不会显示
  • @tomasz77 是的,文本未显示在 TextField 中的问题是我正在谈论的问题。虽然单击 X 然后关闭显示“请输入一个值”的对话框,但我可以在该字段中输入。

标签: java javafx dialog alert


【解决方案1】:

在您创建 Dialog1 的“开始”方法中,您应该调用 dialog.show() 而不是 dialog.showAndWait()。

    button.setOnAction(event -> {
        Dialog<Pair<String, String>> dialog = getDialog(scene.getWindow(), "Dialog 1", true);
        // dialog.showAndWait();
        dialog.show();
    });

【讨论】:

  • 好吧,在我的应用程序中,我需要处理以等待从对话框收到响应,这通常需要 showAndWait() 调用,因为它是阻塞的。
【解决方案2】:

问题

如前所述,您遇到了模态问题。

解决方案

以下代码将演示一个解决方案,其中询问用户是否真的要打印,以及在打印后,结束数字是否正确。

注意,我使用来自here的类IntField)

import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonBar.ButtonData;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Dialog;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import javafx.stage.Window;

public class DialogTest extends Application {

    Region veil;
    ProgressIndicator indicator;

    IntField startingNumber = new IntField(0, 999999, 0);
    IntField endingNumber = new IntField(startingNumber.getValue(), 999999, startingNumber.getValue() + 1);
    ButtonType printButtonType = new ButtonType("Print", ButtonData.OK_DONE);
    Stage stage;

    @Override
    public void start(Stage primaryStage) {
        stage = primaryStage;
        Button button = new Button("Print Checks");

        VBox box = new VBox(10, button);
        box.setAlignment(Pos.CENTER);

        veil = new Region();
        veil.setStyle("-fx-background-color: rgba(0, 0, 0, 0.3);");
        veil.setVisible(false);

        indicator = new ProgressIndicator();
        indicator.setMaxHeight(60);
        indicator.setMinWidth(60);
        indicator.setVisible(false);

        StackPane root = new StackPane(box, veil, indicator);

        root.setAlignment(Pos.CENTER);

        Scene scene = new Scene(root, 400, 400);
        primaryStage.setScene(scene);
        primaryStage.show();

        button.setOnAction((event) -> {
            Dialog<ButtonType> dialog
                    = getCheckPrintDialog(primaryStage, "Enter starting check number");
            dialog.showAndWait()
                    .filter(result -> result == printButtonType)
                    .ifPresent(result -> {
                        // this is for this example only, normaly you already have this value
                        endingNumber.setValue(startingNumber.getValue() + 1);
                        printChecks(startingNumber.getValue(), endingNumber.getValue());
                    });
        });
    }

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

    public <R extends ButtonType> Dialog getCheckPrintDialog(Window owner, String title) {
        Dialog<R> dialog = new Dialog<>();
        dialog.initOwner(owner);
        dialog.setTitle(title);
        dialog.getDialogPane().getButtonTypes().addAll(printButtonType, ButtonType.CANCEL);

        Button btOk = (Button) dialog.getDialogPane().lookupButton(printButtonType);
        btOk.addEventFilter(ActionEvent.ACTION, event -> {
            Alert alert = new Alert(Alert.AlertType.CONFIRMATION, "Print Checks? Are you sure?", ButtonType.YES, ButtonType.NO);
            alert.showAndWait()
                    .filter(result -> result == ButtonType.NO)
                    .ifPresent(result -> event.consume());
        });

        GridPane grid = new GridPane();
        grid.setHgap(10);
        grid.setVgap(10);

        Text from = new Text("Starting Number:");
        grid.add(from, 0, 0);

        grid.add(startingNumber, 1, 0);

        dialog.getDialogPane().setContent(grid);
        return dialog;
    }

    private void printChecks(int from, int to) {

        Task<Void> task = new Task<Void>() {
            @Override
            protected Void call() throws Exception {
                Thread.sleep(5000);
                return null;
            }
        };

        task.setOnSucceeded((event) -> {
            Alert alert = new Alert(Alert.AlertType.CONFIRMATION, "Has the last check, the number: " + endingNumber.getValue() + "?", ButtonType.YES, ButtonType.NO);
            alert.initOwner(stage);
            Button btnNo = (Button) alert.getDialogPane().lookupButton(ButtonType.NO);
            btnNo.addEventFilter(ActionEvent.ACTION, e -> {
                Dialog<ButtonType> newEndNum = new Dialog<>();
                newEndNum.setTitle("Enter the ending check number");
                newEndNum.initOwner(stage);
                newEndNum.getDialogPane().getButtonTypes().addAll(ButtonType.OK, ButtonType.CANCEL);
                GridPane grid = new GridPane();
                grid.setHgap(10);
                grid.setVgap(10);

                Text toUser = new Text("Ending Number:");
                grid.add(toUser, 0, 0);

                grid.add(endingNumber, 1, 0);

                newEndNum.getDialogPane().setContent(grid);
                newEndNum.showAndWait().filter(result -> result == ButtonType.CANCEL)
                        .ifPresent(result -> e.consume());
            });
            alert.showAndWait();
        });
        veil.visibleProperty().bind(task.runningProperty());
        indicator.visibleProperty().bind(task.runningProperty());
        new Thread(task).start();
    }

}

工作应用程序

  1. 主窗口:

  1. 打印对话框:

  1. 点击打印后(警报已本地化,对我来说是德语):

  1. 单击“是”后,打印对话框将关闭,并且会显示一个进度(在本例中为 5 秒)

  1. 打印完成后会出现一个对话框,要求输入正确的结束编号

  1. 如果单击“是”,则全部完成,如果单击“否”,则会打开另一个对话框以输入正确的结束值

【讨论】:

  • 在移动设备上的一些说明。基本上,应用程序运行一个例程来打印一系列检查。我们提供起始支票号码,如果用户单击“确定”,我们会提示确保他们确定要运行例程,然后在完成后,我们会显示另一个对话框供用户输入结束支票号码。跨度>
  • 抱歉,上周我一直不在办公室。因此,该解决方案似乎缺少在询问用户是否确定之后显示另一个对话框以验证打印的结束支票号码与先前输入的预期结束支票号码匹配的部分。这是似乎阻止输入到字段中的对话框。
  • 嗯,可能我没看懂。因此,您需要一个用户输入起始编号的对话框,在此对话框关闭后,您需要第二个对话框,用户在其中输入结束编号。之后你想让他确认他想打印支票吗?这不符合人体工程学!为什么不要求用户开始和结束一次(在一个对话框中)并且只证明数字范围(开始
  • 希望这是一个更好的解释。我们提示用户输入起始编号,然后询问用户是否确定要开始打印,然后显示一个对话框让用户输入结束编号。逻辑是我们要确保正确打印所有支票。我们知道结束支票号码应该是什么,但请用户在支票打印完成后确认这一点,以确保打印所有支票。
  • 我已经更新了我的答案。因为打印应该是一个非阻塞语句,你可能会在打印过程完成后打开一个新的对话框。
【解决方案3】:

我找到了问题所在。但是因为我刚刚开始使用 JavaFx,所以我无法提供“为什么”。 在我看来,问题出在 dialog.close() 中,就在 if (alert.showAndWait().get() == ButtonType.YES) 之后. 当你关闭它或类似的东西时,看起来它失去了对对话框的一些引用(我让专家清除了这一点)。

作为一种解决方法,它适用于我,将 dialog.close() 移到 dialog2.showAndWait() 之后;

public Dialog<Pair<String, String>> getDialog(Window owner, String title, boolean addButton) {
        Dialog<Pair<String, String>> dialog = new Dialog<>();
        dialog.setTitle(title);
        dialog.initOwner(owner);
        dialog.getDialogPane().getButtonTypes().addAll(ButtonType.OK, ButtonType.CANCEL);

        if (addButton) {
            Button button = new Button("Show Dialog");
            dialog.getDialogPane().setContent(button);
            button.setOnAction(event -> {
                Alert alert = new Alert(AlertType.CONFIRMATION, "Are you sure?", ButtonType.YES, ButtonType.NO);
                alert.initOwner(owner);
                if (alert.showAndWait().get() == ButtonType.YES) {
                    // dialog.close(); // supressed this and placed at the bottom
                    Dialog<Pair<String, String>> dialog2 = getDialog(owner, "Dialog 2", false);
                    TextField textField = new TextField();
                    dialog2.getDialogPane().setContent(textField);
                    dialog2.getDialogPane().getScene().getWindow().setOnCloseRequest(closeEvent -> {
                        closeEvent.consume();
                        if (textField.getText().trim().isEmpty()) {
                            Alert alert2 = new Alert(AlertType.ERROR, "Please enter a value", ButtonType.OK);
                            alert2.initOwner(dialog2.getDialogPane().getScene().getWindow());
                            alert2.showAndWait();
                        }
                    });
                    dialog2.showAndWait();
                    dialog.close(); // new location
                }
            });
        }

        return dialog;
    }

我无法解释发生这种情况的原因,但这可能是一种解决方法。 希望对您有所帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-12-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-05
    • 1970-01-01
    相关资源
    最近更新 更多