Chiggiddi Chiggiddi - 5 months ago 136
Java Question

Howto make JavaFX Dialog NOT closing itself after pressing OK Button

currently I am using

Dialog<User> loginDialog = new Dialog<>();
to create a login dialog in which the user needs to input his username and password and then click the OK button to login.

My Issue



Unfortunately the dialog pane closes itself each time the ok button was pressed no matter whether the login was successful or not. I would like the dialog pane only to close itself when either the cancel button was pressed or the login was successful.

SOLUTION



as NwDX has pointed out. One need to make use of the addEventFilter method. My implementation of it looks like this:

btnLogin.addEventFilter(ActionEvent.ACTION, event -> {
if (!comboAdministrator.getValue().getPassword().equals(pfLogin.getText())) {
lblErrorNotification.setText("Password is incorrect. Try again!");
pfLogin.requestFocus();
event.consume();
}
});


Explanation: the eventfilter catches any unmatching passwords and send an error message to a label node for notification purposes. The event.consume() is neccessary, otherwise the dialog will revert to its original behaviour by closing itself once again.

SOLUTION with complete code



private Optional<User> showLoginDialog() {

Dialog<User> dialog = new Dialog<>();
dialog.setTitle("Administrator Login");
dialog.setHeaderText("Enter administrator password");
dialog.initOwner(btnShowManagerView.getScene().getWindow());
dialog.getDialogPane().getStylesheets().add("util/resources/myCSS.css");

dialog.setGraphic(new ImageView(ResourceClass.class.getResource("locker.png").toString()));

ButtonType loginButtonType = new ButtonType("Login", ButtonData.YES);
ButtonType cancelButtonType = ButtonType.CANCEL;

dialog.getDialogPane().getButtonTypes().addAll(loginButtonType, cancelButtonType);

Label lblErrorNotification = new Label();
lblErrorNotification.setTextFill(Color.RED);

GridPane gridPane = new GridPane();
gridPane.setHgap(10);
gridPane.setVgap(10);
gridPane.setPadding(new Insets(20, 150, 20, 20));

ComboBox<User> comboAdministrator = new ComboBox();
comboAdministrator.setItems(main.getAdministrators());
comboAdministrator.setValue(main.getAdministrators().get(0));
comboAdministrator.setConverter(new StringConverter<User>() {

@Override
public String toString(User object) {
return object.getLoginName();
}

@Override
public User fromString(String string) {
return null;
}
});
main.enableComboBoxBaseShowIfFocused(comboAdministrator);

PasswordField pfLogin = new PasswordField();

gridPane.add(new Label("AdminUser"), 0, 0);
gridPane.add(comboAdministrator, 1, 0);
gridPane.add(new Label("Password"), 0, 1);
gridPane.add(pfLogin, 1, 1);
gridPane.add(lblErrorNotification, 1, 2);

Button btnLogin = (Button) dialog.getDialogPane().lookupButton(loginButtonType);

btnLogin.addEventFilter(ActionEvent.ACTION, event -> {
if (!comboAdministrator.getValue().getPassword().equals(pfLogin.getText())) {
lblErrorNotification.setText("Password is incorrect. Try again!");
pfLogin.requestFocus();
event.consume();
}
});

btnLogin.disableProperty()
.bind(pfLogin.textProperty().isEmpty());

dialog.getDialogPane().setContent(gridPane);

Platform.runLater(()
-> comboAdministrator.requestFocus());

dialog.setResultConverter((ButtonType buttonType) -> {
if (buttonType == loginButtonType) {
return comboAdministrator.getValue();
}

return null;
});

return dialog.showAndWait();
}

Answer

From the Dialog API Documentation:

Dialog Validation / Intercepting Button Actions

In some circumstances it is desirable to prevent a dialog from closing until some aspect of the dialog becomes internally consistent (e.g. a form inside the dialog has all fields in a valid state). To do this, users of the dialogs API should become familiar with the DialogPane.lookupButton(ButtonType) method. By passing in a ButtonType (that has already been set in the button types list), users will be returned a Node that is typically of type Button (but this depends on if the DialogPane.createButton(ButtonType) method has been overridden). With this button, users may add an event filter that is called before the button does its usual event handling, and as such users may prevent the event handling by consuming the event. Here's a simplified example:

 final Button btOk = (Button) dlg.getDialogPane().lookupButton(ButtonType.OK);
 btOk.addEventFilter(ActionEvent.ACTION, event -> {
     if (!validateAndStore()) {
         event.consume();
     }
 });