Javafx сообщение об ошибке

Sergey is correct, but if you need to get a response from your home-spun dialog(s) for evaluation in the same block of code that invoked it, you should use .showAndWait(), not .show(). Here’s my rendition of a couple of the dialog types that are provided in Swing’s OptionPane:

public class FXOptionPane {

public enum Response { NO, YES, CANCEL };

private static Response buttonSelected = Response.CANCEL;

private static ImageView icon = new ImageView();

static class Dialog extends Stage {
    public Dialog( String title, Stage owner, Scene scene, String iconFile ) {
        setTitle( title );
        initStyle( StageStyle.UTILITY );
        initModality( Modality.APPLICATION_MODAL );
        initOwner( owner );
        setResizable( false );
        setScene( scene );
        icon.setImage( new Image( getClass().getResourceAsStream( iconFile ) ) );
    }
    public void showDialog() {
        sizeToScene();
        centerOnScreen();
        showAndWait();
    }
}

static class Message extends Text {
    public Message( String msg ) {
        super( msg );
        setWrappingWidth( 250 );
    }
}

public static Response showConfirmDialog( Stage owner, String message, String title ) {
    VBox vb = new VBox();
    Scene scene = new Scene( vb );
    final Dialog dial = new Dialog( title, owner, scene, "res/Confirm.png" );
    vb.setPadding( new Inset(10,10,10,10) );
    vb.setSpacing( 10 );
    Button yesButton = new Button( "Yes" );
    yesButton.setOnAction( new EventHandler<ActionEvent>() {
        @Override public void handle( ActionEvent e ) {
            dial.close();
            buttonSelected = Response.YES;
        }
    } );
    Button noButton = new Button( "No" );
    noButton.setOnAction( new EventHandler<ActionEvent>() {
        @Override public void handle( ActionEvent e ) {
            dial.close();
            buttonSelected = Response.NO;
        }
    } );
    BorderPane bp = new BorderPane();
    HBox buttons = new HBox();
    buttons.setAlignment( Pos.CENTER );
    buttons.setSpacing( 10 );
    buttons.getChildren().addAll( yesButton, noButton );
    bp.setCenter( buttons );
    HBox msg = new HBox();
    msg.setSpacing( 5 );
    msg.getChildren().addAll( icon, new Message( message ) );
    vb.getChildren().addAll( msg, bp );
    dial.showDialog();
    return buttonSelected;
}

public static void showMessageDialog( Stage owner, String message, String title ) {
    showMessageDialog( owner, new Message( message ), title );
}
public static void showMessageDialog( Stage owner, Node message, String title ) {
    VBox vb = new VBox();
    Scene scene = new Scene( vb );
    final Dialog dial = new Dialog( title, owner, scene, "res/Info.png" );
    vb.setPadding( new Inset(10,10,10,10) );
    vb.setSpacing( 10 );
    Button okButton = new Button( "OK" );
    okButton.setAlignment( Pos.CENTER );
    okButton.setOnAction( new EventHandler<ActionEvent>() {
        @Override public void handle( ActionEvent e ) {
            dial.close();
        }
    } );
    BorderPane bp = new BorderPane();
    bp.setCenter( okButton );
    HBox msg = new HBox();
    msg.setSpacing( 5 );
    msg.getChildren().addAll( icon, message );
    vb.getChildren().addAll( msg, bp );
    dial.showDialog();
}

}

Create JavaFX Message Box

Today’s tutorial demonstrates creating a JavaFX message box in our Java application. The message box can be a confirmation, warning, information, or error alert.

Create JavaFX Message Box

To accomplish the following example code, we use Java version 18, JavaFX version 13 and Netbeans IDE version 13.

Example Code:

//write your package name
package com.mycompany.javafx_messagebox;

//import required libraries
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Button;
import javafx.scene.layout.TilePane;
import javafx.stage.Stage;

/**
 * JavaFX App
 */
public class App extends Application {

    @Override
    public void start(Stage stage) {

        // create a tile pane
        TilePane r = new TilePane();
        //add padding
        r.setPadding(new Insets(10, 10, 10, 10));

        // an array of button names
        String[] buttonNames = {"Confirmation MessageBox",
                                "Error MessageBox",
                                "Information MessageBox",
                                "Warning MessageBox"};

        //Show no alert at the startup of the program
        Alert alert = new Alert(AlertType.NONE);


        /*
        a loop to create buttons, define actions when
        they are pressed and add them to the tile pane
        */
        for (String s : buttonNames) {
            Button button = new Button(s);

            button.setOnAction((ActionEvent event) -> {
                if (null != button.getText()) {
                    switch (button.getText()) {
                        case "Confirmation MessageBox":
                            // set alert type, title, content text and then show it
                            alert.setAlertType(AlertType.CONFIRMATION);
                            alert.setTitle("Confirmation MessageBox");
                            alert.setContentText("This is a CONFIRMATION "+
                                                 "message for you!");
                            alert.show();
                            break;
                        case "Error MessageBox":
                            // set alert type, title, content text and then show it
                            alert.setAlertType(AlertType.ERROR);
                            alert.setTitle("Error MessageBox");
                            alert.setContentText("This is an ERROR message for you!");
                            alert.show();
                            break;
                        case "Information MessageBox":
                            // set alert type, title, content text and then show it
                            alert.setAlertType(AlertType.INFORMATION);
                            alert.setTitle("Information MessageBox");
                            alert.setContentText("This is a INFORMATION "+
                                                 "message for you!");
                            alert.show();
                            break;
                        case "Warning MessageBox":
                            // set alert type, title, content text and then show it
                            alert.setAlertType(AlertType.WARNING);
                            alert.setTitle("Warning MessageBox");
                            alert.setContentText("This is a WARNING message for you!");
                            alert.show();
                            break;
                        default:
                            break;
                    }
                }
            });

            //add button
            r.getChildren().add(button);

        }

        // create a scene
        Scene sc = new Scene(r, 640, 50);

        // set the scene
        stage.setScene(sc);

        //show the stage
        stage.show();

    }//end start method

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

}//end App class

Output (main window):

create javafx message box - main window

Output (confirmation message box, displayed when we click on Confirmation MessageBox button):

create javafx message box - main window

Output (error message box, displayed when we click on Error MessageBox button):

create javafx message box - main window

Output (information message box, displayed when we click on Information MessageBox button):

create javafx message box - main window

OUTPUT (warning message box, displayed when we click on the Warning MessageBox button):

create javafx message box - main window

For this tutorial, we don’t need to make any changes to the module-info.java and pom.xml files. Create a JavaFX project and practice the code given above.

We have a main class named App that extends the Application class (which is standard in Java). You can name the primary launch class (App).

Next, we override the start() method because the App is the child class of the Application class. Remember that the child class needs to implement all abstract functions/methods of the parent class.

After that, we have a start() method that takes one parameter of the Stage type. We are using the Stage type parameter because this is where all visual components JavaFX application will be displayed.

We do not need to create the Stage type object because the JavaFX runtime creates it. The following is the step-by-step explanation of what’s inside the start() method.

  • Create an object of JavaFX TilePane, which is a layout component and lays out all its child components in the grid of same-sized cells.
  • Add margins around the whole grid (top/right/bottom/left).
  • Create an array with the names of all buttons we need for this application.

  • Create an alert message box of type NONE because we do not want to display any message box at the program’s startup.
  • Next, we have a for loop, which iterates over all the button names.
    • Inside the loop, we create a button of the current name.
    • Set an action for that specific button based on the condition. We get the button text and display a message box based on the button name using the switch statement.
  • Add the button to the TilePane.
  • Create a scene using the Scene class.
  • Set the scene.
  • Finally, show the stage.

Now, it is the main method’s turn. We can launch the JavaFX application without having the main method, but it is useful when we are required to use parameters that are passed to the application using the command line.

JavaFX 8u40 finally includes simple Dialogs and Alerts! I’ve been waiting for this since 2012! In the meantime I wrote about how to use Dialogs in JavaFX 2 and later in JavaFX 8 with ControlsFX.

Now that they are available in the official JDK, let’s learn how to use them.

Prerequisites

To use the official JavaFX Dialogs you need JDK 8u40 or later.

→ Download JDK 8u40 or newer.

Standard Dialogs

Information Dialog

JavaFX Information Dialog

Alert alert = new Alert(AlertType.INFORMATION);
alert.setTitle("Information Dialog");
alert.setHeaderText("Look, an Information Dialog");
alert.setContentText("I have a great message for you!");

alert.showAndWait();

JavaFX Information Dialog without Header Text

Alert alert = new Alert(AlertType.INFORMATION);
alert.setTitle("Information Dialog");
alert.setHeaderText(null);
alert.setContentText("I have a great message for you!");

alert.showAndWait();

Warning Dialog

JavaFX Warning Dialog

Alert alert = new Alert(AlertType.WARNING);
alert.setTitle("Warning Dialog");
alert.setHeaderText("Look, a Warning Dialog");
alert.setContentText("Careful with the next step!");

alert.showAndWait();

Error Dialog

JavaFX Error Dialog

Alert alert = new Alert(AlertType.ERROR);
alert.setTitle("Error Dialog");
alert.setHeaderText("Look, an Error Dialog");
alert.setContentText("Ooops, there was an error!");

alert.showAndWait();

Exception Dialog

JavaFX Exception Dialog

There is not a complete Exception Dialog out of the box. But we can easily provide TextArea as expandable content.

Alert alert = new Alert(AlertType.ERROR);
alert.setTitle("Exception Dialog");
alert.setHeaderText("Look, an Exception Dialog");
alert.setContentText("Could not find file blabla.txt!");

Exception ex = new FileNotFoundException("Could not find file blabla.txt");

// Create expandable Exception.
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
ex.printStackTrace(pw);
String exceptionText = sw.toString();

Label label = new Label("The exception stacktrace was:");

TextArea textArea = new TextArea(exceptionText);
textArea.setEditable(false);
textArea.setWrapText(true);

textArea.setMaxWidth(Double.MAX_VALUE);
textArea.setMaxHeight(Double.MAX_VALUE);
GridPane.setVgrow(textArea, Priority.ALWAYS);
GridPane.setHgrow(textArea, Priority.ALWAYS);

GridPane expContent = new GridPane();
expContent.setMaxWidth(Double.MAX_VALUE);
expContent.add(label, 0, 0);
expContent.add(textArea, 0, 1);

// Set expandable Exception into the dialog pane.
alert.getDialogPane().setExpandableContent(expContent);

alert.showAndWait();

Confirmation Dialog

JavaFX Confirmation Dialog

Alert alert = new Alert(AlertType.CONFIRMATION);
alert.setTitle("Confirmation Dialog");
alert.setHeaderText("Look, a Confirmation Dialog");
alert.setContentText("Are you ok with this?");

Optional<ButtonType> result = alert.showAndWait();
if (result.get() == ButtonType.OK){
    // ... user chose OK
} else {
    // ... user chose CANCEL or closed the dialog
}

Confirmation Dialog with Custom Actions

JavaFX Confirmation Dialog with Custom Actions

Alert alert = new Alert(AlertType.CONFIRMATION);
alert.setTitle("Confirmation Dialog with Custom Actions");
alert.setHeaderText("Look, a Confirmation Dialog with Custom Actions");
alert.setContentText("Choose your option.");

ButtonType buttonTypeOne = new ButtonType("One");
ButtonType buttonTypeTwo = new ButtonType("Two");
ButtonType buttonTypeThree = new ButtonType("Three");
ButtonType buttonTypeCancel = new ButtonType("Cancel", ButtonData.CANCEL_CLOSE);

alert.getButtonTypes().setAll(buttonTypeOne, buttonTypeTwo, buttonTypeThree, buttonTypeCancel);

Optional<ButtonType> result = alert.showAndWait();
if (result.get() == buttonTypeOne){
    // ... user chose "One"
} else if (result.get() == buttonTypeTwo) {
    // ... user chose "Two"
} else if (result.get() == buttonTypeThree) {
    // ... user chose "Three"
} else {
    // ... user chose CANCEL or closed the dialog
}

Text Input Dialog

JavaFX Text Input Dialog

TextInputDialog dialog = new TextInputDialog("walter");
dialog.setTitle("Text Input Dialog");
dialog.setHeaderText("Look, a Text Input Dialog");
dialog.setContentText("Please enter your name:");

// Traditional way to get the response value.
Optional<String> result = dialog.showAndWait();
if (result.isPresent()){
    System.out.println("Your name: " + result.get());
}

// The Java 8 way to get the response value (with lambda expression).
result.ifPresent(name -> System.out.println("Your name: " + name));

Note: The result.isPresent() will return false if the user cancelled the dialog.

Choice Dialog

JavaFX Choice Dialog

List<String> choices = new ArrayList<>();
choices.add("a");
choices.add("b");
choices.add("c");

ChoiceDialog<String> dialog = new ChoiceDialog<>("b", choices);
dialog.setTitle("Choice Dialog");
dialog.setHeaderText("Look, a Choice Dialog");
dialog.setContentText("Choose your letter:");

// Traditional way to get the response value.
Optional<String> result = dialog.showAndWait();
if (result.isPresent()){
    System.out.println("Your choice: " + result.get());
}

// The Java 8 way to get the response value (with lambda expression).
result.ifPresent(letter -> System.out.println("Your choice: " + letter));

Note: The result.isPresent() will return false if the user didn’t choose anything or cancelled the dialog.

Custom Login Dialog

Here is an example of how to create a custom dialog with a login form:

JavaFX Custom Login Dialog

// Create the custom dialog.
Dialog<Pair<String, String>> dialog = new Dialog<>();
dialog.setTitle("Login Dialog");
dialog.setHeaderText("Look, a Custom Login Dialog");

// Set the icon (must be included in the project).
dialog.setGraphic(new ImageView(this.getClass().getResource("login.png").toString()));

// Set the button types.
ButtonType loginButtonType = new ButtonType("Login", ButtonData.OK_DONE);
dialog.getDialogPane().getButtonTypes().addAll(loginButtonType, ButtonType.CANCEL);

// Create the username and password labels and fields.
GridPane grid = new GridPane();
grid.setHgap(10);
grid.setVgap(10);
grid.setPadding(new Insets(20, 150, 10, 10));

TextField username = new TextField();
username.setPromptText("Username");
PasswordField password = new PasswordField();
password.setPromptText("Password");

grid.add(new Label("Username:"), 0, 0);
grid.add(username, 1, 0);
grid.add(new Label("Password:"), 0, 1);
grid.add(password, 1, 1);

// Enable/Disable login button depending on whether a username was entered.
Node loginButton = dialog.getDialogPane().lookupButton(loginButtonType);
loginButton.setDisable(true);

// Do some validation (using the Java 8 lambda syntax).
username.textProperty().addListener((observable, oldValue, newValue) -> {
    loginButton.setDisable(newValue.trim().isEmpty());
});

dialog.getDialogPane().setContent(grid);

// Request focus on the username field by default.
Platform.runLater(() -> username.requestFocus());

// Convert the result to a username-password-pair when the login button is clicked.
dialog.setResultConverter(dialogButton -> {
    if (dialogButton == loginButtonType) {
        return new Pair<>(username.getText(), password.getText());
    }
    return null;
});

Optional<Pair<String, String>> result = dialog.showAndWait();

result.ifPresent(usernamePassword -> {
    System.out.println("Username=" + usernamePassword.getKey() + ", Password=" + usernamePassword.getValue());
});

Styling the Dialogs

Custom Icon

Custom Dialog Icon

In the current version it’s a bit cumbersome to get to the Dialog’s Stage to be able to set its icon. Here is how:

// Get the Stage.
Stage stage = (Stage) dialog.getDialogPane().getScene().getWindow();

// Add a custom icon.
stage.getIcons().add(new Image(this.getClass().getResource("login.png").toString()));

According to this bug report the final version of the JavaFX 8u40 Dialogs should use the same icon as the application that it is running from. In that case you would need to set its owner and the Dialog would get the owner’s icon:

dialog.initOwner(otherStage);

Minimal Decorations

Another option is to remove the icon and use only minimal window decorations.

Minimal Decorations

dialog.initStyle(StageStyle.UTILITY);

Other Options

Setting the Owner

You can specify the owner Window for a dialog. If no owner or null is specified for the owner, it is a top-level, unowned dialog.

dialog.initOwner(parentWindow);

Setting the Modality

You can specify the modality for a dialog. The modality must be one of Modality.NONE, Modality.WINDOW_MODAL, or Modality.APPLICATION_MODAL.

dialog.initModality(Modality.NONE);

API Documentation

For more information on the Dialogs have a look at the JavaFX API docs:

  • Alert
  • Dialog
  • TextInputDialog
  • ChoiceDialog
  • DialogPane

How To Build JavaFX Dialogs and Alerts

Introduction

In this How To article I demonstrate some of the different ways that JavaFX dialogs and alerts are built and controlled. As a learning and exploration aid the code samples in this article have been integrated into a larger JavaFX application, JavaFX Dialog Explorer, that demonstrates usability of the various dialogs and alerts discussed as well as sources the various parts of this article’s code snippets along with the official JavaFX 11 documentation using the WebView component.

On The Coding Interace, «How To» articles are intended to be more of a collection of code samples relying on code to convey the majority of the semantics of a topic with textual descriptions being on the lighter side.

How To Contents

Running the JavaFX Dialog Explorer App

To run the JavaFX Dialog Explorer app you will need to have Java JDK 11 or higher installed and the JAVA_HOME variable set. See Installing OpenJDK 11 from my earlier article «High Level Introduction to Java for Developers» for instructions if needed. After than simply clone the GitHub repo and run with gradle as follows.

git clone https://github.com/amcquistan/javafx-dialog-explorer.git
cd javafx-dialog-explorer

Linux / Mac

Windows

Then click on one of the Alert / Dialog options from the left side bar and the associated dialog will appear. Additionally, in the main area of the UI the corresponding section of this blog post will be shown. You can also toggle back and forth between the blog post and the offical JavaFX docs by clicking the to Logo’d buttons on the top right of the UI.

The Alert class

The javafx.scene.control.Alert class is an abstraction of the javafx.scene.control.Dialog class and serves as a convience class for a set of common use cases seen in a JavaFX application. That being said working with the Alert class is incredibly easy and is generally using in the following way:

  1. instantiate a Alert class passing it the type of alert you desire from the Alert.AlertType set of enums CONFIRMATION, ERROR, INFORMATION, NONE, and WARNING
var alert = new Alert(Alert.AlertType.NONE);
  1. set informational message characteristics to be displayed to the user
alert.setTitle("I'm an alert title");
alert.setHeaderText("I'm an alert header");
alert.setContentText("I'm the main alert context (body)");
  1. show the alert window and watch for a response indicating the action the user took with the alert window
// showAndWait is a blocking call so, the code execution path pauses in 
// the executing JavaFX thread until the user interacts with the dialog
// in a way that causes it to close
Optional<ButtonType> result = alert.showAndWait();
result.ifPresent(btnType -> {
    var msg = String.format(
    		"You clicked %s in Alert %s",
    		btnType.getButtonData(),
    		BLOCKING_NONE_ALERT
    );
    feedbackLbl.setText(msg);
});

In this example I have specified the AlertType of NONE which is the least convenient way of using the Alert class because now I have to add javafx.scene.control.ButtonType values manually. For this example I do so by adding the following before the alert.showAndWait() method call. Normally this is unnecessary when using the Alert class because it abstracts away the need to specify buttons for the other common AlertType enums.

alert.getButtonTypes().addAll(ButtonType.OK, ButtonType.CANCEL);

Informational Alert

A common use case is to simply display an informational message to a user when some event happens which requires nothing more than an acknowledgement from the user. In JavaFX this is crazy simple. You can accomplish this by passing the enum constant AlertType.INFORMATION to the constructor of the Alert class and giving some textual messages for display. This results in a dialog being shown that dislays an info graphic and an OK button.

var alert = new Alert(AlertType.INFORMATION);
alert.setTitle(BLOCKING_INFO_ALERT);
alert.setHeaderText(BLOCKING_INFO_ALERT);
alert.setContentText(BLOCKING_INFO_ALERT);

alert.showAndWait().ifPresent((btnType) -> {
    feedbackLbl.setText("Thats all from " + BLOCKING_INFO_ALERT);
    clearDialogOptionSelections();
});

Warning Alert

A similar warning alert is available also which only differs in that a warn info graphic is shown.

var alert = new Alert(AlertType.WARNING);
alert.setTitle(BLOCKING_WARNING_ALERT);
alert.setHeaderText(BLOCKING_WARNING_ALERT);
alert.setContentText(BLOCKING_WARNING_ALERT);
alert.showAndWait().ifPresent((btnType) -> {
    feedbackLbl.setText("Thats all from " + BLOCKING_WARNING_ALERT);
    clearDialogOptionSelections();
});

Confirmation Alert

Displaying a message to the user that requires them to either confirm or deny is another common use case for a JavaFX application. This is easily accomplished via the AlertType.CONFIRMATION enum which displays a question info graphic along with OK and Cancel buttons similar to what I showed with the NONE example at first. You can check to see what the user did by checking the ButtonType from the Optional that is returned from calling Dialog#showAndWait (the showAndWait method is inherited from the Dialog parent class).

var alert = new Alert(AlertType.CONFIRMATION);
alert.setTitle(BLOCKING_CONFIRM_ALERT);
alert.setHeaderText(BLOCKING_CONFIRM_ALERT);
alert.setContentText(BLOCKING_CONFIRM_ALERT);
alert.showAndWait().ifPresent((btnType) -> {
  if (btnType == ButtonType.OK) {
    feedbackLbl.setText("Confirmed " + BLOCKING_CONFIRM_ALERT);
  } else if (btnType == ButtonType.CANCEL) {
    feedbackLbl.setText("Cancelled " + BLOCKING_CONFIRM_ALERT);
  }
  clearDialogOptionSelections();
});

Error Alert

Again, very similar to the INFORMATION and WARN AlertType enums you can use the ERROR enum to display an error info graphic containing alert.

var alert = new Alert(AlertType.ERROR);
alert.setTitle(BLOCKING_ERR_ALERT);
alert.setHeaderText(BLOCKING_ERR_ALERT);
alert.setContentText(BLOCKING_ERR_ALERT);
alert.showAndWait().ifPresent((btnType) -> {
    if (btnType == ButtonType.OK) {
      feedbackLbl.setText("Thats all from " + BLOCKING_ERR_ALERT);
    }
    clearDialogOptionSelections();
});

Error Alert (Non-Blocking)

Up to this point I have been using the Dialog#showAndWait method to display the alert dialog making the path of code execution pause until the dialog is closed. This is the most common way I have come to use dialogs but, the JavaFX framework provides another method, Dialog#show, which shows the dialog without blocking the executing code path. In this example I have chosen to add an event handler to hook into the dialog window closing event to know when the user closed the dialog window.

var alert = new Alert(AlertType.ERROR);
alert.setTitle(NONBLOCKING_ERR_ALERT);
alert.setHeaderText(NONBLOCKING_ERR_ALERT);
alert.setContentText(NONBLOCKING_ERR_ALERT);

// Alert#show contrasts Alert#showAndWait where Alert#show is non-blocking 
// meaning that code within the execution path that contains the Alert 
// instance will continue to execute after Alert#show is called.
alert.show();

// the code below the show() method call will get exectued immediately
// after show() is called rather than wait on a user to close the dialog
alert.setOnHiding((evt) -> {
    feedbackLbl.setText("Thats all from " + NONBLOCKING_ERR_ALERT);
    clearDialogOptionSelections();
});

feedbackLbl.setText(NONBLOCKING_ERR_ALERT + 
    " execution path kept running after the dialog was displayed");

Error Alert (Non-Modal)

The default behavior of dialogs are for them to be modal which disallows the user from interacting with the window(s) behind the dialog. This is easily overriden via the Dialog#initModality(Modality) method. For example, to make a non-modal dialog pass javafx.stage.Modality.NONE as the argument.

var alert = new Alert(AlertType.ERROR);
alert.setTitle(NONMODAL_ERR_ALERT);
alert.setHeaderText(NONMODAL_ERR_ALERT);
alert.setContentText(NONMODAL_ERR_ALERT);

// Alerts exhibit Modality.APPLICATION_MODAL by default but,
// you can specify WINDOW_MODAL to block the window or NONE
// to be non-modal at all.
alert.initModality(Modality.NONE);

// you can also hook into when Dialog's are initially shown using the
// inherited Dialog#setOnShowing event handler
alert.setOnShowing((evt) -> {
    feedbackLbl.setText("Go ahead, click another option. I won't stop you.");
});

alert.showAndWait().ifPresent((btnType) -> {
    if (btnType == ButtonType.OK) {
        feedbackLbl.setText("Thats all from " + NONMODAL_ERR_ALERT);
    }
    clearDialogOptionSelections();
});

Alert (No Header)

You can omit title, header or body content like so.

var alert = new Alert(AlertType.INFORMATION);
alert.setTitle(ALERT_NO_HEADER);
// null as a value will cause the section to not be displayed
alert.setHeaderText(null);
alert.setContentText(ALERT_NO_HEADER);

alert.showAndWait().ifPresent((btnType) -> {
    if (btnType == ButtonType.OK) {
        feedbackLbl.setText("Thats all from " + ALERT_NO_HEADER);
    }
    clearDialogOptionSelections();
});

Convience Input Dialogs

The JavaFX framework also provides two convience implementation classes of Dialogs that accept textual as well as dropdown like selection inputs.

Text Input Dialog

To easily accept text input via a dialog use the javafx.scene.control.TextInputDialog class. This class differs in that a Optional is returned if the user clicks Ok. In this example I as the user to enter the section name of another dialog from this article which I then display as the next dialog. If the user enters text that doesn’t match the title of one of the sections then I inform them with a warning dialog that no match as found.

// Two constructors: TextInputDialog() and TextInputDialog(String)
// where the String is the default value
var txtDlg = new TextInputDialog(TEXT_DIALOG);
txtDlg.setTitle(TEXT_DIALOG);
txtDlg.setHeaderText(TEXT_DIALOG);
txtDlg.setContentText("Enter name of another dialog to open.");

// returns String optional
Optional<String> result = txtDlg.showAndWait();

// true => String (ie, user entered value and clicked ok)
// false => user clicked cancel
result.ifPresent(input -> {

    // find the VBox child node that is a DialogOption
    // and matches the user's input
    Optional<Node> matchedDlg = menuVBox.getChildren().stream()
        .filter(node -> {
            return !input.equalsIgnoreCase(txtDlg.getDefaultValue())
              && ((DialogOption) node).getText().equalsIgnoreCase(input);
        }).findFirst();

    if (matchedDlg.isPresent()) {
        // if present fire the action event of the underlying ToggleButton
        var dlgOption = (DialogOption) matchedDlg.get();
        dlgOption.fire();
        dlgOption.setSelected(true);
    } else {
        // otherwise let the user know they didn't type in something
        // that matched on of the titles of the available OptionDialog(s)
        dialogsToggleGrp.selectToggle(null);
        var notFoundAlert = new Alert(AlertType.WARNING);
        notFoundAlert.setTitle("Input Not Recognized");
        notFoundAlert.setHeaderText(null);
        notFoundAlert.setContentText(String.format("%s does not match a dialog from the menu", input));
        notFoundAlert.show();
    }
});

Choice Box Dialog

To limit input options a javafx.scene.control.ChoiceDialog can be used. To do this I supply a predifined list of String values for the user to select from along with a default value to the ChoiceDialog constructor. In this example I display all other dialog sections from this article for the user to choose from then display that dialog.

var titles = webResourceMap.keySet().stream()
    .filter(dlgOption -> !dlgOption.equals(CHOICE_DIALOG))
    .collect(Collectors.toList());

var defaultTitle = titles.get(0);
var choiceDlg = new ChoiceDialog<String>(defaultTitle, titles);

// returns Optional<String> on clicking ok
// or false if cancel is clicked
choiceDlg.showAndWait().ifPresent(selection -> {
  Optional<Node> matchedDlg = menuVBox.getChildren().stream()
      .filter(node -> ((DialogOption) node).getText().equals(selection))
      .findFirst();

  matchedDlg.ifPresent(node -> {
      var dlgOption = (DialogOption) matchedDlg.get();
      dlgOption.fire();
      dlgOption.setSelected(true);
  });
});

The Dialog Class

Of course you can still implement your own version of a javafx.scene.control.Dialog and have complete control of what is displayed and now things are handled.

Custom Dialog

In this example I have implemented a class named CountDownDialog which displays a dialog that counts down to zero allowing the user to start and stop (pause) the countdown as well as reset and cancel. This interactivity is provided by implementing javafx.scene.control.ButtonType instances with meaningful text to display to the user and paired with a javafx.scene.control.ButtonBar.ButtonData enum that fits the semantics of the action being handled. Then the ButtonType instances are added to the ObservableList of ButtonType instances via DialogPane#getButtonTypes of the DialogPane which is part of the Dialog class.

An important distinction to make is that the ButtonType instances being added to the Dialog are different from the Button control. However, you can gain access to the actual Button instances that get added to the Dialog via DialogPane#lookupButton(ButtonType) method which I demonstrate below in order to disable buttons and add action event handlers.

The other thing worth mentioning is that the Dialog class is generically typed with regards to the Object that is returned when the Dialog is closed. In this example I have implemented the CountDownDialog class to return an Integer value represeting the countdown value the dialog was closed at.

package com.thecodinginterface.dialogs;

import java.util.Timer;
import java.util.TimerTask;

import javafx.application.Platform;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.event.ActionEvent;
import javafx.geometry.Pos;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonBar;
import javafx.scene.control.ButtonType;
import javafx.scene.control.Dialog;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.stage.Stage;
import javafx.stage.StageStyle;

class CountDownDialog extends Dialog<Integer> {
    private BorderPane root;
    private int startFrom;
    private IntegerProperty current;
    private Timer timer;

    private static final String PAUSE_TXT = "Pause";
    private static final String START_TXT = "Start";


    CountDownDialog(Stage primaryStage, int startFrom, String header, boolean decorated) {
        super();
        initOwner(primaryStage);
        if (!decorated) {
            initStyle(StageStyle.UNDECORATED);
        }
        
        this.startFrom = startFrom;
        current = new SimpleIntegerProperty(startFrom);

        root = new BorderPane();
        root.setTop(new Label(header));

        var countDownLbl = new Label();
        countDownLbl.textProperty().bind(current.asString());
        countDownLbl.setFont(Font.font("Cambria", FontWeight.BOLD, 60));
        var vbox = new VBox(countDownLbl);
        vbox.setAlignment(Pos.CENTER);
        root.setCenter(vbox);

        getDialogPane().setContent(root);

        var startStopBtnType = new ButtonType(PAUSE_TXT, ButtonBar.ButtonData.APPLY);
        var resetBtnType = new ButtonType("Reset", ButtonBar.ButtonData.BACK_PREVIOUS);
        var closeBtnType = new ButtonType("Close", ButtonBar.ButtonData.CANCEL_CLOSE);

        getDialogPane().getButtonTypes().addAll(startStopBtnType, resetBtnType, closeBtnType);

        var startStopBtn = (Button) getDialogPane().lookupButton(startStopBtnType);
        var resetBtn = (Button) getDialogPane().lookupButton(resetBtnType);
        
        startStopBtn.addEventFilter(ActionEvent.ACTION, (evt) -> {
            evt.consume();
            var startCounting = timer == null;
            if (startCounting) {
              startStopBtn.setText(PAUSE_TXT);
              startTimer();
            } else {
              startStopBtn.setText(START_TXT);
              stopTimer();
            }
        });

        resetBtn.addEventFilter(ActionEvent.ACTION, (evt) -> {
            evt.consume();
            resetTimer();
            startStopBtn.setText(START_TXT);
        });

        setResultConverter((btnType) -> {
            return Integer.valueOf(current.get());
        });

        startTimer();
    }

    private void startTimer() {
        if (timer == null) {
            timer = new Timer("Custom Dialog Countdown", true);
            timer.schedule(new CountDownTask(), 1000, 1000);
        }
    }

    private void stopTimer() {
        if (timer != null) {
            timer.cancel();
            timer = null;
        }
    }

    private void resetTimer() {
        current.set(startFrom);
        if (timer != null) {
            stopTimer();
        }
    }

    private final class CountDownTask extends TimerTask {
        @Override
        public void run() {
            Platform.runLater(() -> {
                int currentCnt = current.intValue();
                if (currentCnt > 0) {
                    current.set(currentCnt - 1);
                }
            });
        }
    }
}

Conclusion

In this How To article I have demonstrated a number of ways that I have learned to display dialogs in JavaFX applications. To aid in the discussion I wrote a simple learning aid app named JavaFX Dialog Explorer that was a fun little experiment in which I hope gives a sufficient number of working examples along with an easy way to find useful information on dialog API details.

import javafx.application.Application;

import javafx.scene.Scene;

import javafx.scene.control.Button;

import javafx.scene.layout.*;

import javafx.event.ActionEvent;

import javafx.event.EventHandler;

import javafx.scene.control.*;

import javafx.stage.Stage;

import javafx.scene.control.Alert.AlertType;

public class Alert_2 extends Application {

    public void start(Stage s)

    {

        s.setTitle("creating alerts");

        Button b = new Button("Confirmation alert");

        Button b1 = new Button("error alert");

        Button b2 = new Button("Information alert");

        Button b3 = new Button("Warning alert");

        Button b4 = new Button("none alert");

        TilePane r = new TilePane();

        Alert a = new Alert(AlertType.NONE);

        EventHandler<ActionEvent> event = new

                          EventHandler<ActionEvent>() {

            public void handle(ActionEvent e)

            {

                a.setAlertType(AlertType.CONFIRMATION);

                a.setContentText("ConfirmationDialog");

                a.show();

            }

        };

        EventHandler<ActionEvent> event1 = new

                          EventHandler<ActionEvent>() {

            public void handle(ActionEvent e)

            {

                a.setAlertType(AlertType.ERROR);

                a.setContentText("error Dialog");

                a.show();

            }

        };

        EventHandler<ActionEvent> event2 = new

                          EventHandler<ActionEvent>() {

            public void handle(ActionEvent e)

            {

                a.setAlertType(AlertType.INFORMATION);

                a.setContentText("Information Dialog");

                a.show();

            }

        };

        EventHandler<ActionEvent> event3 = new

                            EventHandler<ActionEvent>() {

            public void handle(ActionEvent e)

            {

                a.setAlertType(AlertType.WARNING);

                a.setContentText("Warning Dialog");

                a.show();

            }

        };

        EventHandler<ActionEvent> event4 = new

                               EventHandler<ActionEvent>() {

            public void handle(ActionEvent e)

            {

                Alert a1 = new Alert(AlertType.NONE,

                              "default Dialog",ButtonType.APPLY);

                a1.show();

            }

        };

        b.setOnAction(event);

        b1.setOnAction(event1);

        b2.setOnAction(event2);

        b3.setOnAction(event3);

        b4.setOnAction(event4);

        r.getChildren().add(b);

        r.getChildren().add(b1);

        r.getChildren().add(b2);

        r.getChildren().add(b3);

        r.getChildren().add(b4);

        Scene sc = new Scene(r, 200, 200);

        s.setScene(sc);

        s.show();

    }

    public static void main(String args[])

    {

        launch(args);

    }

}

Like this post? Please share to your friends:
  • Java поднять ошибку
  • Java пишет ошибку java lang nullpointerexception
  • Java переустановить ошибка
  • Java ошибка сертификата
  • Java ошибка времени исполнения