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

3

5

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>
Schlenger answered 23/11, 2020 at 7:57 Comment(0)
A
10

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);
});
Applique answered 23/11, 2020 at 11:38 Comment(0)
S
1

Here is an implementation of MessageBox, which extends the Alert class. If you want to add CSS style to beautify your window, you have to define MessageBox.setStyleSheet when loading your application (only once, it is not necessary to call it each time you want to display a MessageBox):

public class Main extends Application {
    @Override
    public void start(Stage primaryStage) throws Exception {
        MessageBox.setStyleSheet("/your/path/.../message-box.css");

        ...

        primaryStage.setOnCloseRequest(event -> {
            try {
                MessageBox dialog = new MessageBox(AlertType.CONFIRMATION, primaryStage, "Do you really want to exit?", ButtonType.YES, ButtonType.NO);
                dialog.setTitle("Выход");

                Optional<ButtonType> option = dialog.showAndWait();
                if (option != null && option.get() == ButtonType.YES)
                    System.exit(0);
                else {
                    dialog.close();
                    event.consume();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

You can check message-box.css whether you want to custom the component style.

Skywriting answered 23/11, 2020 at 10:32 Comment(0)
C
0

It's very simple

  1. Use fx:root in your FXML ( root is DialogPane ), note : don't specify controller in file FXML
  2. Make your controller extend Dialog
  3. In contructor of controller, insert this code

FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(
                "fxml"));
        fxmlLoader.setController(this);
        fxmlLoader.setRoot(this.getDialogPane());
        try {
            fxmlLoader.load();
        } catch (IOException exception) {
            throw new RuntimeException(exception);
        }

Done, now you can set result in your controller and get it in your application

Coomb answered 19/7, 2024 at 11:12 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.