In this post I will write about two problems with JavaFX dialogs and how to solve them: How can you set a minimal size
for a dialog and how can you prevent the dialog from closing?
How to set a min-size for JavaFX Dialogs?
If you create a JavaFX Application Window without further configuration, you can resize the window without limitations.
This means you can change the size so that no content is visible anymore.
To prevent this you have to set a minHeight
and minWidth
on the Stage
of the window:
public class TestApp extends Application {
@Override
public void start(Stage primaryStage) {
VBox root = new Vbox();
primaryStage.setMinHeight(200);
primaryStage.setMinWidth(200);
primaryStage.setScene(new Scene(root, 400, 400);
primaryStage.show();
}
}
With Dialog you have a similar situation. By default JavaFX Dialogs are not resizeable but it is possible to change this
default behavior. However, if the Dialog is set to be resizeable you are then able to minimize the size to 0 pixel like
with the application window. In contrast to the main application window you can't set the min-size on the Dialog though.
At least not directly.
To fix this you have to set the min-size on the stage of the dialog. This can be done like this:
Dialog dialog = new Dialog<>();
dialog.setResizable(true);
final Window window = dialog.getDialogPane().getScene().getWindow();
Stage stage = (Stage) window;
stage.setMinHeight(200);
stage.setMinWidth(200);
How to prevent the closing of a Dialog.
Another situation that we were facing in a real-world app lately was to prevent closing of a JavaFX Dialog when some
validation logic has failed.
In the JavaDoc for the Dialog
class
you can find two paragraphs that look promising at a first glance: "Dialog Validation / Intercepting Button Actions" and
"Dialog Closing Rules".
The first one tells you how you can add validation rules for dialog buttons and how to prevent the dialog from closing
if the condition is not met when attempting to close the dialog.
With the lookup
method of the DialogPane
you can get the buttons of the Dialog and add EventFilters on them. If your
validation fails you can consume the event to prevent the Dialog from closing:
Dialog dialog = new Dialog();
dialog.getDialogPane().getButtonTypes.addAll(ButtonType.OK, ButtonType.CANCEL);
TextField textField = new TextField();
dialog.getDialogPane().setContent(textField);
EventHandler<ActionEvent> filter = event -> {
if(textField.getText().isEmpty()) {
event.consume();
}
};
Button okButton = (Button) dialog.getDialogPane().lookup(ButtonType.OK);
Button cancelButton = (Button) dialog.getDialogPane().lookup(ButtonType.CANCEL);
okButton.addEventFilter(ActionEvent.ACTION, filter);
cancelButton.addEventFilter(ActionEvent.ACTION, filter);
The code above creates a Dialog with a TextField and two Buttons (OK and CANCEL). You can't close the dialog with these
buttons as long as the TextField is empty. In many situations this may not be your desired behavior: Typically you like
to prevent closing with the OK Button but allow the user to cancel the action and close the dialog with the CANCEL
button. However, in other situations you may want to achive exactly this: Prevent the closing even when the cancel
button is pressed. So everything is ok with this code? Not quite. You can still click the "x" in the window of the
dialog and the Dialog will close no matter what your validation says.
This is when the second part of the
JavaDoc page comes into play. It
describes the rules when it's possible to close a Dialog "abnormally" (which includes clicking the "x" but also pressing
some OS specific shortcut like Alt+F4 on Windows). These rules are not bad. You can only close a dialog this way if the
dialog has a CANCEL Button. From a usability perspective this makes sense: If the user can't finish the process in the
dialog she may want cancel it either by pressing the cancel button of the dialog or the "x" in the window title.
But what if (for some reasons) you have other requirements? What if you have to prevent the closing even though it might
not result in good usability? For this usage there is no easy API available.
But it is still possible with some workarounds: In JavaFX you can prevent the closing of a Stage
by adding an
event-filter with the setOnCloseRequest
method. With this knowledge you can use the same approach from the beginning
of this article to access the Stage of the dialog to add this event-filter:
Stage stage = (Stage) dialog.getDialogPane().getScene().getWindow();
stage.setOnCloseRequest(event -> {
if(tf.getText().isEmpty()) {
event.consume();
}
});