February 24, 2024
/This article presents a custom JavaFX Dialog. A custom Dialog returns a value using a function call-like syntax. This makes procedural code readable since a user interaction can be blended cleanly with other method calls.
The most important aspect of the custom Dialog subclass is the result. If the Dialog is expected to
cause side effects -- say saving data as it is being operated -- it is better to use a general-purpose
component. The result enables the synchronous showAndWait()
to be used.
The app presented in this article allows the user to manipulate a TableView by adding and editing hyperlinks: New button and double-click on a TableRow. The user is also able to change the title of the table: Change button. The following screenshot is the main window when the app is started.
The main window is based on the scene graph from hyperlinks-view.fxml which is paired with HyperlinksController. The Hyperlink class is a Java record domain object containing a URL with a text description. The following UML shows the static structure of the app.
An off-the-shelf Dialog is used to implement the change title feature. This Dialog is a TextInputDialog. Other standard JavaFX Dialogs include Alert and ChoiceDialog. See the JavaFX Javadoc for a description of those classes. Alert is a widely used standard Dialog. Most apps will use Alert to communicate errors, information, or confirmations.
In HyperlinksController, a Label is injected as lblTitle
. In an initialize()
method,
the Label is bound to a StringProperty.
The button handler attached to the Change button sets the Label text property based on the result returned from the dialog. There are a few lines for setting the title and header of the Dialog window.
The TextInputDialog result flows neatly into the StringProperty set with ifPresent
from the
Optional and a method reference.
To add a hyperlink to the TableView or to edit an existing hyperlink, the user enters a URL and text description
in a custom Dialog. Both operations result in the instantiation of a custom Dialog "HyperlinkDialog". For the add
operation, the user presses the New button. The following handler is called
which will add the Hyperlink domain object result to the TableView. Note the similar call
signature to what was used with the standard TextInputDialog. An Optional, chained to showAndWait()
,
will add to TableView if present.
There is a slight variation in the call to edit an existing hyperlink. In this case, an initial value is given
to fill the UI controls. This allows the user to review the existing values. The following listing is a section
of the
initialize()
method showing the
response replacing the selected TableRow.
Referring back to the class model, the custom Dialog extends the Dialog class and is called HyperlinkDialog.
This extension defines a constructor that adds hyperlinks-dialog.fxml to the supplied
DialogPane. The most important part of this constructor is the result converter. This is a lambda passed to
setResultConverter()
that knows how
to retrieve a Hyperlink value from the embedded controller, HyperlinksController. The caller does not need to
interact directly with the controller (or, worse, the controller does not need to interact with the caller) since
the result will be available through the familiar showAndWait()
.
In addition to providing the result converter, the preceding constructor sets the content for the DialogPane, configures title and header text, and adds buttons. The buttons belong to the Dialog superclass but can be retrieved by the subclass. In this code, the save Button is bound to a property of the controller "valid" which will disable the save Button if the input is invalid.
Dialog subclasses are best for side-effect-free interactions. If a Dialog needs to manipulate external resources like an API, opt for a general-purpose component. Tasks, error reporting through Alert, and ProgressBars will be easier to apply to a class built from the ground up rather than a dismantled Dialog subclass.
The code listing for HyperlinkDialogController is long and contains JavaFX code not specific to custom
Dialogs. Its source file is available as a link in a new tab. The
controller gathers two fields -- text and URL -- and packages them into a Hyperlink domain object
for retrieval via the getHyperlink()
method. There is repetition in this class that could easily be
factored out. It is permitted in this example to keep the indirection to a minimum.
Use the standard JavaFX Dialogs judiciously. showAndWait()
is a convenient call that allows
you to blend user interactions with other function calls in your methods. Create a custom Dialog for
cases where you need more than a singular piece of data (say a pair of text values). Keep the custom
Dialog synchronous and result-focused. If you find a custom Dialog requiring side effects like an API,
use a component that will give you more control over the button bar to report progress and messages.
A runnable Maven project with source code supporting this article can be found on GitLab under Carl Walker / javafx-hyperlink-dialog.