JavaFX Dialogs: How to set min-size and prevent closing?

javafx

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.

todo app with JavaFX

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);

// Create an event filter that consumes the action if the text is empty
EventHandler<ActionEvent> filter = event -> {
	if(textField.getText().isEmpty()) {
		event.consume();
	}
};

// lookup the buttons
Button okButton = (Button) dialog.getDialogPane().lookup(ButtonType.OK);
Button cancelButton = (Button) dialog.getDialogPane().lookup(ButtonType.CANCEL);

// add the event-filter
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();
	}
});