阻止程序执行直到用户单击按钮 [英] Block program execution until user clicks button

查看:116
本文介绍了阻止程序执行直到用户单击按钮的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试创建类似于 Swing的模态对话框 JOptionPane 。我想提出一个确认对话框,在我在代码中执行某些操作之前,该对话框会让用户明确地说是。



我从以下方面无耻地窃取了这个例子:。


I'm attempting to make a modal dialog similar to Swing's JOptionPane. I want to present a confirmation dialog which makes the users explicitly say "yes" before I perform some action in code.

I've shamelessly stolen this example from: https://gist.github.com/jewelsea/1887631

And modded it to be:

public class ModalConfirmDialog {
    private boolean confirm = false;
    private boolean isDialogOpen = false;

    public boolean isOpen() {
        return isDialogOpen;
    }

    public boolean getConfirmation() {
        return confirm;
    }

    public void showConfirmationDialog(final Stage primaryStage) {
        // initialize the confirmation dialog
        final Stage dialog = new Stage(StageStyle.TRANSPARENT);
        dialog.initModality(Modality.WINDOW_MODAL);
        dialog.initOwner(primaryStage);
        dialog.setScene(
                new Scene(
                        HBoxBuilder.create().styleClass("modal-dialog").children(
                                LabelBuilder.create().text("Confirm Action?").build(),
                                ButtonBuilder.create().text("Yes").defaultButton(true).onAction(new EventHandler<ActionEvent>() {
                                    @Override public void handle(ActionEvent actionEvent) {
                                        // take action and close the dialog.
                                        primaryStage.getScene().getRoot().setEffect(null);
                                        dialog.close();
                                        confirm = true;
                                        isDialogOpen = false;
                                    }
                                }).build(),
                                ButtonBuilder.create().text("No").cancelButton(true).onAction(new EventHandler<ActionEvent>() {
                                    @Override public void handle(ActionEvent actionEvent) {
                                        // abort action and close the dialog.
                                        primaryStage.getScene().getRoot().setEffect(null);
                                        dialog.close();
                                        confirm = false;
                                        isDialogOpen = false;
                                    }
                                }).build()
                                ).build()
                                , Color.TRANSPARENT
                        )
                );
        dialog.getScene().getStylesheets().add(getClass().getResource("modal-dialog.css").toExternalForm());

        // allow the dialog to be dragged around.
        final Node root = dialog.getScene().getRoot();
        final Delta dragDelta = new Delta();
        root.setOnMousePressed(new EventHandler<MouseEvent>() {
            @Override public void handle(MouseEvent mouseEvent) {
                // record a delta distance for the drag and drop operation.
                dragDelta.x = dialog.getX() - mouseEvent.getScreenX();
                dragDelta.y = dialog.getY() - mouseEvent.getScreenY();
            }
        });
        root.setOnMouseDragged(new EventHandler<MouseEvent>() {
            @Override public void handle(MouseEvent mouseEvent) {
                dialog.setX(mouseEvent.getScreenX() + dragDelta.x);
                dialog.setY(mouseEvent.getScreenY() + dragDelta.y);
            }
        });
        primaryStage.getScene().getRoot().setEffect(new BoxBlur());
        dialog.show();
        isDialogOpen = true;
    }

    //records relative x and y co-ordinates.
    class Delta { double x, y; }
}

My problem is, when this dialog pops up, the program execution continues even though the user has not selected an option (yes or no).

I've tried setting a boolean value and checking it in a loop to know when I should check for the users choice, but this ends up causing other programs (race condition and/or just blocks everything including the dialog).

    ModalConfirmDialog dialog = new ModalConfirmDialog();
    dialog.showConfirmationDialog((Stage) relatedTransTable.getScene().getWindow());
    while (dialog.isOpen()) {
        // wait for it to close?
    }

    if (dialog.getConfirmation()) {
        System.out.println("Confirmed choice!");
    } else {
        System.out.println("User denied choice!");
    }

I'm unsure how to replicate JOptionPane without needing to embed Swing into my entirely JavaFX application.

解决方案

Solution

Use showAndWait to display your dialog.

Shows this stage and waits for it to be hidden (closed) before returning to the caller. This method temporarily blocks processing of the current event, and starts a nested event loop to handle other events.


I'm attempting to make a modal dialog similar to Swing's JOptionPane.

The javafx.scene.control.Alert class from Java 8u40 offers functionality similar to Swing's JOptionPane and its design and implementation was heavily influenced by JOptionPane.


Current Recommendation

  1. For basic common dialogs like alerts or confirmations, use the built-in dialog functionality in the JavaFX 8u40+ toolkits. Early access release for 8u40 is available and the dialog API there is stable and will map to the projected March 2015 production release.
  2. If you need a specific kind of dialog box, like for tasks such as login, font selection, progress feedback and wizards, use ControlsFX (which supports a greater range of default dialog implementations than the core JavaFX platform).

  3. If you need a completely customized dialog, then use the techniques outlined later in this answer.

Background

Don't reinvent the wheel. Use the ControlsFX library for JOptionPane type dialogs rather than writing your own implementation (and JavaFX 8u40 will introduce common dialog boxes into the platform).

The code you reference for modal dialog display is quite old now. There have been numerous features (including the addition of showAndWait) implemented both in the JavaFX core platform and 3rd party libraries, which mean that if you are doing the task today, it would probably be done in a different way.

The referenced code in the question makes use of Builders which have been deprecated in Java 8, so it is no longer recommended to use them (they no longer even show up in the JavaFX javadoc).

Code Demonstrating the built-in JavaFX 8u40+ Alert class

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.stage.Stage;

public class ShowAndWaitBuiltInAlert extends Application {
    @Override public void start(Stage stage) {
        Button showDialog = new Button("Show Dialog");
        showDialog.setOnAction(event -> {
            Alert dialog = new Alert(
                    Alert.AlertType.CONFIRMATION,
                    "Are you sure you want to exit the Dialog Demo Application?"
            );
            dialog.showAndWait()
                    .filter(response -> response.equals(ButtonType.OK))
                    .ifPresent(response -> stage.close());
        });

        stage.setScene(new Scene(showDialog));
        stage.show();
    }

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

An alert extends from Dialog, so it can be customized using all the customization methods of the Dialog class.

Code Demonstrating the built-in JavaFX 8u40+ Dialog class

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.stage.Stage;

public class ShowAndWaitBuiltInDialog extends Application {    
    @Override public void start(Stage stage) {
        Button showDialog = new Button("Show Dialog");
        showDialog.setOnAction(e -> {
            Dialog<ButtonType> dialog = new Dialog<>();
            dialog.setTitle("Dialog Demo");
            dialog.setHeaderText("Confirm Exit");
            dialog.setContentText("Are you sure you want to exit the Dialog Demo Application?");
            ButtonType exit = new ButtonType("Exit", ButtonBar.ButtonData.OK_DONE);
            dialog.getDialogPane().getButtonTypes().addAll(
                    exit, ButtonType.CANCEL
            );
            dialog.showAndWait()
                    .filter(response -> response.equals(exit))
                    .ifPresent(response -> stage.close());
        });

        stage.setScene(new Scene(showDialog));
        stage.show();
    }

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

Custom Dialog Code Demonstrating showAndWait

The following code will work in Java 8 versions predating Java 8u40.

import javafx.application.Application;
import javafx.beans.property.*;
import javafx.geometry.*;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.*;

public class ShowAndWaitDialog extends Application {

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

    @Override
    public void start(Stage stage) {
        Button showDialog = new Button("Show Dialog");
        showDialog.setOnAction(e -> {
            ConfirmationDialog dialog = new ConfirmationDialog(
                    "Would you like to exit the application?"
            );
            dialog.setY(stage.getY() + stage.getHeight() + 10);
            dialog.setX(stage.getX());
            dialog.showAndWait();
            if (dialog.isSelected()) {
                stage.close();
            }
        });
        stage.setScene(new Scene(showDialog));
        stage.show();
    }

    class ConfirmationDialog extends Stage {
        private VBox layout = new VBox();

        private ReadOnlyBooleanWrapper selected = new ReadOnlyBooleanWrapper();
        public boolean isSelected() {
            return selected.get();
        }
        public ReadOnlyBooleanProperty selectedProperty() {
            return selected.getReadOnlyProperty();
        }

        public ConfirmationDialog(String question) {
            initStyle(StageStyle.UTILITY);
            initModality(Modality.APPLICATION_MODAL);

            layout.setSpacing(10);
            layout.setPadding(new Insets(10));

            createControls();

            layout.getChildren().addAll(
                    new Label(question),
                    createControls()
            );

            setScene(new Scene(layout));
            sizeToScene();  // workaround because utility stages aren't automatically sized correctly to their scene.
        }

        private HBox createControls() {
            final Button ok = new Button("OK");
            ok.setOnAction(e -> {
                selected.set(true);
                close();
            });

            final Button cancel = new Button("Cancel");
            cancel.setOnAction(e -> {
                selected.set(false);
                close();
            });

            final HBox controls = new HBox(10, ok, cancel);
            controls.setAlignment(Pos.CENTER_RIGHT);

            return controls;
        }
    }
}

Update for comment question on Dec 19, 2014

Isn't this some basic UI functionality expected in every UI framework? Why did JavaFX lacked this native functionality until now?

showAndWait has been in JavaFX for about two and a half years and was added about 8 months after the initial JavaFX 2 release until showAndWait appeared as public API in JavaFX 2.2. During that period, the majority of the showAndWait functionality could be simulated using event handlers similar to the code in the asker's question.

In the more general case of built-in common dialog box API for things like alerts and confirmations (now added to Java 8u40), there is extensive history, design discussion and initial deferral reasons provided in the JavaFX issue tracker for the feature RT-12643.

这篇关于阻止程序执行直到用户单击按钮的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆