A Dialog in JavaFX wraps a DialogPane
and provides the necessary API
to present it to end users. In JavaFX 8u40, this essentially means that the
DialogPane
is shown to users inside a Stage
, but future releases
may offer alternative options (such as 'lightweight' or 'internal' dialogs).
This API therefore is intentionally ignorant of the underlying implementation,
and attempts to present a common API for all possible implementations.
The Dialog class has a single generic type, R, which is used to represent
the type of the result
property (and also, how to
convert from ButtonType
to R, through the use of the
result converter
Callback
).
Critical note: It is critical that all developers who choose
to create their own dialogs by extending the Dialog class understand the
importance of the result converter
property.
A result converter must always be set, whenever the R type is not
Void
or ButtonType
. If this is not heeded, developers will find
that they get ClassCastExceptions in their code, for failure to convert from
ButtonType
via the result converter
.
It is likely that most developers would be better served using either the
Alert
class (for pre-defined, notification-style alerts), or either of
the two pre-built dialogs (TextInputDialog
and ChoiceDialog
),
depending on their needs.
Once a Dialog is instantiated, the next step is to configure it. Almost
all properties on Dialog are not related to the content of the Dialog, the
only exceptions are contentTextProperty()
,
headerTextProperty()
, and graphicProperty()
, and these
properties are simply forwarding API onto the respective properties on the
DialogPane
stored in the dialog pane
property. These three properties are forwarded from DialogPane for developer
convenience. For developers wanting to configure their dialog, they will in many
cases be required to use code along the lines of
dialog.getDialogPane().setExpandableContent(node)
.
After configuring these properties, all that remains is to consider whether
the buttons (created using ButtonType
and the
DialogPane.createButton(ButtonType)
method) are fully configured.
Developers will quickly find that the amount of configurability offered
via the ButtonType
class is minimal. This is intentional, but does not
mean that developers can not modify the buttons created by the ButtonType
that have been specified. To do this, developers simply call the
DialogPane.lookupButton(ButtonType)
method with the ButtonType
(assuming it has already been set in the DialogPane.getButtonTypes()
list. The returned Node is typically of type Button
, but this depends
on if the DialogPane.createButton(ButtonType)
method has been overridden. A
typical approach is therefore along the following lines:
ButtonType loginButtonType = new ButtonType("Login", ButtonData.OK_DONE);
Dialog<String> dialog = new Dialog<>();
dialog.getDialogPane().getButtonTypes().add(loginButtonType);
boolean disabled = false; // computed based on content of text fields, for example
dialog.getDialogPane().lookupButton(loginButtonType).setDisable(disabled);
Once a Dialog is instantiated and fully configured, the next step is to show it. More often than not, dialogs are shown in a modal and blocking fashion. 'Modal' means that the dialog prevents user interaction with the owning application whilst it is showing, and 'blocking' means that code execution stops at the point in which the dialog is shown. This means that you can show a dialog, await the user response, and then continue running the code that directly follows the show call, giving developers the ability to immediately deal with the user input from the dialog (if relevant).
JavaFX dialogs are modal by default (you can change this via the
initModality(javafx.stage.Modality)
API). To specify whether you want
blocking or non-blocking dialogs, developers simply choose to call
showAndWait()
or show()
(respectively). By default most
developers should choose to use showAndWait()
, given the ease of
coding in these situations. Shown below is three code snippets, showing three
equally valid ways of showing a dialog:
Option 1: The 'traditional' approach
Optional<ButtonType> result = dialog.showAndWait();
if (result.isPresent() && result.get() == ButtonType.OK) {
formatSystem();
}
Option 2: The traditional + Optional approach
dialog.showAndWait().ifPresent(response -> {
if (response == ButtonType.OK) {
formatSystem();
});}
Option 3: The fully lambda approach
dialog.showAndWait()
.filter(response -> response == ButtonType.OK)
.ifPresent(response -> formatSystem());
There is no better or worse option of the three listed above, so developers
are encouraged to work to their own style preferences. The purpose of showing
the above is to help introduce developers to the Optional
API, which
is new in Java 8 and may be foreign to many developers.
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();
});}
It is important to understand what happens when a Dialog is closed, and also how a Dialog can be closed, especially in abnormal closing situations (such as when the 'X' button is clicked in a dialogs title bar, or when operating system specific keyboard shortcuts (such as alt-F4 on Windows) are entered). Fortunately, the outcome is well-defined in these situations, and can be best summarised in the following bullet points:
ButtonType
whose ButtonData
is of type
ButtonData.CANCEL_CLOSE
.ButtonType
whose ButtonData
returns true
when ButtonData.isCancelButton()
is called.DialogPane
area of the dialog.
result
property to whatever value is returned
from calling the result converter
with
the first matching ButtonType
.
result
property will be null, and the
showAndWait()
method will return Optional.empty()
. This
later point means that, if you use either of option 2 or option 3 (as
presented earlier in this class documentation), the
Optional.ifPresent(java.util.function.Consumer)
lambda will never
be called, and code will continue executing as if the dialog had not
returned any value at all.
implements
<R> | The return type of the dialog, via the
result property. |
Alert, TextInputDialog, ChoiceDialog