Search code examples
javafx

Java FX create Custom Dialog with fxml file. How to set or get result from it?


Here, I want to create a custom dialog that works like Alert, with a beautiful UI; "showDialog.fxml" is a DialogPane.

I tried JFXDialog but it is not waiting and it is not giving me the users's choice. Is there any way to get a good result? Thanks in advance.

This is my Alert implementation:

primaryStage.setOnCloseRequest(event -> {
    Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
    alert.setTitle("Выход");
    alert.setHeaderText("Are you really want to exit ?");
    ButtonType yes = new ButtonType("Yes");
    ButtonType no = new ButtonType("No");
    alert.getButtonTypes().clear();
    alert.getButtonTypes().addAll(yes, no);

    Optional<ButtonType> option = alert.showAndWait();
    if (option.get() == yes) {
        System.exit(0);
    } else {
        alert.close();
        event.consume();
    }
}

This is the new one.

 primaryStage.setOnCloseRequest(event -> {
     FXMLLoader loader = new FXMLLoader(getClass().getResource("/views/common/showDialog.fxml"));
     ShowDialogController controller = loader.getController();
     Dialog<JFXButton> dialog = new Dialog<>();

     DialogPane confirmationDialogView = null;
     try {
         dialog.setDialogPane(loader.load());
     } catch (IOException e) {
         e.printStackTrace();
     }

     dialog.showAndWait();
}

And this the FXML file:

<?xml version="1.0" encoding="UTF-8"?>

<?import com.jfoenix.controls.JFXButton?>
<?import de.jensd.fx.glyphs.octicons.OctIconView?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.DialogPane?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.text.Font?>

<DialogPane prefHeight="400.0" prefWidth="900.0" styleClass="main_color_green" stylesheets="@../../css/style.css" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1">
    <graphic>
        <AnchorPane prefHeight="400.0" prefWidth="900.0" styleClass="main_color_green" stylesheets="@../../css/style.css">
            <GridPane layoutX="161.0" layoutY="84.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
                <columnConstraints>
                    <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" percentWidth="30.0" prefWidth="100.0" />
                    <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
                </columnConstraints>

                <rowConstraints>
                    <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
                </rowConstraints>

                <GridPane>
                    <columnConstraints>
                        <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
                    </columnConstraints>

                    <rowConstraints>
                        <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
                    </rowConstraints>

                    <OctIconView fx:id="octionIconView" fill="WHITE" glyphName="ALERT" size="200" wrappingWidth="198.0" GridPane.halignment="CENTER" GridPane.valignment="CENTER" />
                </GridPane>

                <GridPane GridPane.columnIndex="1">
                    <columnConstraints>
                        <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
                    </columnConstraints>

                    <rowConstraints>
                        <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
                        <RowConstraints minHeight="10.0" percentHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES" />
                    </rowConstraints>

                    <JFXButton fx:id="btnNo" prefHeight="94.0" prefWidth="295.0" styleClass="dialogBtn" stylesheets="@../../css/btn.css" text="НЕТ" GridPane.halignment="LEFT" GridPane.rowIndex="1" GridPane.valignment="CENTER">
                        <GridPane.margin>
                            <Insets left="50.0" />
                        </GridPane.margin>
                    </JFXButton>

                    <JFXButton fx:id="btnYes" prefHeight="94.0" prefWidth="337.0" styleClass="dialogBtn" stylesheets="@../../css/btn.css" text="ДА" GridPane.halignment="RIGHT" GridPane.rowIndex="1" GridPane.valignment="CENTER">
                        <GridPane.margin>
                            <Insets right="50.0" />
                        </GridPane.margin>
                    </JFXButton>

                    <Label fx:id="labelInformation" alignment="CENTER" text="Label" textFill="WHITE" GridPane.halignment="CENTER" GridPane.valignment="CENTER">
                        <font>
                            <Font size="24.0" />
                        </font>
                    </Label>
                </GridPane>
            </GridPane>
        </AnchorPane>
    </graphic>
</DialogPane>

Solution

  • The dialogs in javaFX are an instance of the TEMPLATE class Dialog<R>. Where R is the return type (if missing, ButtonType is used by default).

    For your own implementation of a dialog, you must expand Dialog and e if you want to get a different type of data from it, submit ResultConverter (instance of Callback<ButtonType, R>). Another option to get the result of a dialog is to manually call setResult and then close the dialog.

    When you want to use FXML, you usually use the structure of your dialog to load the desired DialogPane from the corresponding fxml file.

    Here is an example:

    <DialogPane xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml" prefWidth="500">
    
        <content>
            <GridPane hgap="5" vgap="5">
                <Label text="Адрес на хост:" GridPane.columnIndex="0" GridPane.rowIndex="0"/>
                <TextField fx:id="hostnameTextField" GridPane.columnIndex="1" GridPane.rowIndex="0"/>
    
                <Label text="База данни:" GridPane.columnIndex="0" GridPane.rowIndex="1"/>
                <TextField fx:id="databaseTextField" GridPane.columnIndex="1" GridPane.rowIndex="1"/>
    
                <Label text="Потребителско име:" GridPane.columnIndex="0" GridPane.rowIndex="2"/>
                <TextField fx:id="usernameTextField" prefWidth="200" GridPane.columnIndex="1" GridPane.rowIndex="2" GridPane.fillWidth="false"/>
    
                <Label text="Парола:" GridPane.columnIndex="0" GridPane.rowIndex="3"/>
                <PasswordField fx:id="passwordField" prefWidth="200" GridPane.columnIndex="1" GridPane.rowIndex="3" GridPane.fillWidth="false"/>
    
                <columnConstraints>
                    <ColumnConstraints/>
                    <ColumnConstraints hgrow="ALWAYS"/>
                </columnConstraints>
            </GridPane>
        </content>
    
        <buttonTypes>
            <ButtonType fx:id="connectButtonType" text="Свързване" buttonData="OK_DONE"/>
            <ButtonType text="Затвори" buttonData="CANCEL_CLOSE"/>
        </buttonTypes>
    
    </DialogPane>
    
    public class ConnectDialog extends Dialog<Connection> {
    
        @FXML
        private TextField hostnameTextField;
    
        @FXML
        private TextField databaseTextField;
    
        @FXML
        private TextField usernameTextField;
    
        @FXML
        private PasswordField passwordField;
    
        @FXML
        private ButtonType connectButtonType;
    
        private ObjectProperty<Connection> connection = new SimpleObjectProperty<>(null);
    
        public ConnectDialog(Window owner) {
            try {
                FXMLLoader loader = new FXMLLoader();
                loader.setLocation(getClass().getResource("/com/xelapos/gui/client/dialog/dialog_pane_connect.fxml"));
                loader.setController(this);
    
                DialogPane dialogPane = loader.load();
                dialogPane.lookupButton(connectButtonType).addEventFilter(ActionEvent.ANY, this::onConnect);
    
                initOwner(owner);
                initModality(Modality.APPLICATION_MODAL);
    
                setResizable(true);
                setTitle("Свързване с база данни");
                setDialogPane(dialogPane);
                setResultConverter(buttonType -> {
                    if(!Objects.equals(ButtonBar.ButtonData.OK_DONE, buttonType.getButtonData())) {
                        return null;
                    }
    
                    return connection.getValue();
                });
    
                setOnShowing(dialogEvent -> Platform.runLater(() -> hostnameTextField.requestFocus()));
            }
            catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    
        @FXML
        private void initialize() {
    
        }
    
        @FXML
        private void onConnect(ActionEvent event) {
            String jdbcUrl = String.format(
                    "jdbc:postgresql://%s/%s",
                    hostnameTextField.textProperty().getValueSafe(),
                    databaseTextField.textProperty().getValueSafe()
            );
    
            try {
                Connection conn = DriverManager.getConnection(jdbcUrl, usernameTextField.textProperty().getValueSafe(), passwordField.textProperty().getValueSafe());
                connection.setValue(conn);
                return;
            }
            catch (SQLException e) {
                Alert alert = new Alert(Alert.AlertType.ERROR);
                alert.initOwner(getDialogPane().getScene().getWindow());
                alert.initModality(Modality.APPLICATION_MODAL);
    
                alert.setResizable(true);
    
                alert.setTitle(getTitle());
                alert.setHeaderText(null);
                alert.setContentText(e.getLocalizedMessage());
    
                alert.show();
            }
    
            event.consume();
        }
    }
    

    And of course, the use:

    ConnectDialog dialog = new ConnectDialog(stage);
    dialog.showAndWait().ifPresent(conn -> {
        System.out.println("Open connection!");
        connection.setValue(conn);
    });