JavaFX Opening one dialog from another
My application has the following workflow that is causing the problem:
Click "Open Dialog"> "Open Dialog"> "Click" From Dialog ">" Confirm Display Confirmation ">" Confirm "close the first dialog box and open a new dialog box
The second dialog box does not allow text to be entered into the TextField. I have included SSCE, which displays the problem. Another strange thing: if you try to close the second dialog box by clicking "X" and then close the alert, I can enter it in the field.
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; } } Problem
As explained, you have a modality issue.
Decision
The following code will demonstrate a solution in which the user is asked if he really wants to print after printing if the end number is correct.
( Note that I am using the IntField class from here )
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(); } } Working application
- Main window:
- Print Dialog:
- After clicking on Print (notifications are localized in German for me):
- After clicking Yes, the print dialog closes and progress will be visible (in this example, for 5 seconds)
- When printing is complete, a dialog box appears asking for the correct end number.
- If you click โYesโ, everything will be done if you click โThere is no other dialogโ to enter the correct final value
I found the point where the problem is. But since I'm just starting out with JavaFx, I cannot provide a why. It seems to me that the problem is dialog.close () , right after if (alert.showAndWait (). Get () == ButtonType.YES) It looks like it loses some link to the dialog when you close it or something- something like that (I let experts figure it out).
As a workaround and it works for me, move dialog.close () to 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; } I cannot explain the reasons for this, but it can be a workaround. Hope this helps you.





