Search code examples
javajavafxcontrollerfxml

Opening another GridPane in the same window


So I've got a login page. If you click on "not a user? register now!" it should open a new page in the same window. Right now I'm getting second page to open but it's in a separate window. It also creates an error message on opening the register page. here's the code:

Main

public class Main extends Application {

    private ConnectToDB DB;
    @FXML
    GridPane mainPane;
    @FXML
    Hyperlink RegisterHyperLink;


    @Override
    public void start(final Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getClassLoader().getResource("sample.fxml"));
        primaryStage.setTitle("Hello World");
        primaryStage.setScene(new Scene(root, 400, 375));
        primaryStage.show();
    }

    public static void main(String[] args) {

        launch(args);
    }
}

Controller

public class Controller {
    private Stage stage = new Stage();
    private GridPane pane = new GridPane();
    private boolean passwordsCheck = false;
    @FXML TextField password;
    @FXML TextField passwordConfirm;
    @FXML GridPane mainPane;
    @FXML GridPane registerPage;

    public void handleSubmitButtonAction(ActionEvent actionEvent) {

    }
    public void goToRegisterNewUserPage(ActionEvent e) throws Exception{

        RegisterPage page = new RegisterPage(stage);
        page.getScene().getWindow().hide();
//      this is where the error gets thrown up
    }

Register page

public class RegisterPage extends GridPane {

    @FXML GridPane mainPane;
    @FXML GridPane RegisterUsers;

    public RegisterPage(Stage stage) throws Exception{

        FXMLLoader loader = new FXMLLoader();
        GridPane RegisterUsers = loader.load(getClass().getClassLoader().getResource("RegisterUsers.fxml"));
        stage.setScene(new Scene(RegisterUsers, 500, 475));
        stage.show();
    }
}

sample.fxml for the login page as well as the relevant hyperlink

<GridPane fx:id="mainPanes"
            fx:controller="sample.Controller"
            xmlns:fx="http://javafx.com/fxml"
            alignment="center" hgap="10" vgap="10">
<Hyperlink text="Forgot password?"
                   onAction="#goToForgotPasswordPage"/>

RegisterUsers.fxml is for the register users page. i'm not sure if you need this but its fx:id = registerPage. So basically clicking the hyperlink should open the Register users in the same window instead of the second. and it would be cool if it resized automatically but if not then I can do that later. I suspect the new window is related to the page.getScene().getWindow().hide(); . I've tried a bunch of different things in the Controller. my attempt before that called the GridPane mainPane from the sample.fxml file and then mainPane.getChildren().setAll(page); but that also didn't work. thanks for taking a look!


Solution

  • There are likely many way to accomplish this, but I've worked up a simple example. Essentially, you'll have one MainLayout that only contains a single VBox. This is our contentPane for the main window.

    Within that VBox, we'll load the LoginLayout.fxml. When the Register button is clicked, we'll load the RegisterLayout.fxml and replace the children of the contentPane.

    Below is a complete example you can copy to see it in action:

    Main.java:

    package loginExample;
    
    import javafx.application.Application;
    import javafx.fxml.FXMLLoader;
    import javafx.scene.Scene;
    import javafx.stage.Stage;
    
    import java.io.IOException;
    
    public class Main extends Application {
    
        public static void main(String[] args) {
            launch(args);
        }
    
        @Override
        public void start(Stage primaryStage) {
    
            try {
                FXMLLoader loader = new FXMLLoader(getClass().getResource("/loginExample/MainLayout.fxml"));
                loader.setController(new MainController());
    
                primaryStage.setTitle("Login Example");
                primaryStage.setWidth(300);
                primaryStage.setHeight(200);
                primaryStage.setScene(new Scene(loader.load()));
                primaryStage.show();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    

    MainController.java:

    package loginExample;
    
    import javafx.fxml.FXML;
    import javafx.fxml.FXMLLoader;
    import javafx.scene.layout.GridPane;
    import javafx.scene.layout.VBox;
    
    import java.io.IOException;
    
    public class MainController {
    
        @FXML
        private VBox contentPane;
    
        @FXML
        private void initialize() {
    
            // Initially start with the login layout
            try {
                FXMLLoader loader = new FXMLLoader(getClass().getResource("/loginExample/LoginLayout.fxml"));
    
                // Set the LoginController and pass a reference to the MainController. This allows the LoginController
                // to access our contentPane.
                loader.setController(new LoginController(this));
    
                // Now, load the login layout into our contentPane
                GridPane gridPane = loader.load();
                contentPane.getChildren().add(gridPane);
            } catch (IOException e) {
                e.printStackTrace();
            }
    
        }
    
        public VBox getContentPane() {
            return contentPane;
        }
    }
    

    MainLayout.fxml:

    <?xml version="1.0" encoding="UTF-8"?>
    
    <?import javafx.geometry.Insets?>
    <?import javafx.scene.layout.VBox?>
    <VBox fx:id="contentPane" xmlns="http://javafx.com/javafx/9.0.1" xmlns:fx="http://javafx.com/fxml/1">
        <padding>
            <Insets bottom="10.0" left="10.0" right="10.0" top="10.0"/>
        </padding>
    </VBox>
    

    LoginController.java

    package loginExample;
    
    import javafx.fxml.FXML;
    import javafx.fxml.FXMLLoader;
    import javafx.scene.control.Button;
    
    import java.io.IOException;
    
    public class LoginController {
    
        @FXML
        private Button btnRegister;
    
        // Reference to our main controller so we can access its content
        private MainController mainController;
    
        public LoginController(MainController mainController) {
            this.mainController = mainController;
        }
    
        @FXML
        private void initialize() {
    
            // Set our Register button to change the content of the main pane
            btnRegister.setOnAction(event -> {
                try {
                    FXMLLoader loader = new FXMLLoader(getClass().getResource("/loginExample/RegisterLayout.fxml"));
                    loader.setController(new RegisterController(mainController));
    
                    // Set our RegisterLayout as the new content for our MainLayout window
                    mainController.getContentPane().getChildren().clear();
                    mainController.getContentPane().getChildren().add(loader.load());
    
                } catch (IOException e) {
                    e.printStackTrace();
                }
            });
        }
    }
    

    LoginLayout.fxml:

    <?xml version="1.0" encoding="UTF-8"?>
    
    <?import javafx.scene.control.*?>
    <?import javafx.scene.layout.*?>
    <GridPane hgap="10.0" vgap="5.0" xmlns="http://javafx.com/javafx/9.0.1" xmlns:fx="http://javafx.com/fxml/1">
        <columnConstraints>
            <ColumnConstraints halignment="RIGHT" hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0"/>
            <ColumnConstraints hgrow="SOMETIMES" minWidth="-Infinity"/>
        </columnConstraints>
        <rowConstraints>
            <RowConstraints minHeight="-Infinity" vgrow="NEVER"/>
            <RowConstraints minHeight="-Infinity" vgrow="SOMETIMES"/>
            <RowConstraints minHeight="-Infinity" vgrow="SOMETIMES"/>
            <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES"/>
        </rowConstraints>
        <children>
            <HBox alignment="CENTER" GridPane.columnSpan="2">
                <children>
                    <Label style="-fx-font-size: 150%; -fx-font-weight: bold;" text="Please login below:"/>
                </children>
            </HBox>
            <Label text="Username:" GridPane.rowIndex="1"/>
            <Label text="Password:" GridPane.rowIndex="2"/>
            <TextField GridPane.columnIndex="1" GridPane.rowIndex="1"/>
            <HBox alignment="CENTER_RIGHT" spacing="10.0" GridPane.columnSpan="2" GridPane.rowIndex="3">
                <children>
                    <Button fx:id="btnRegister" mnemonicParsing="false" text="Register"/>
                    <Button defaultButton="true" mnemonicParsing="false" text="Login"/>
                </children>
            </HBox>
            <PasswordField GridPane.columnIndex="1" GridPane.rowIndex="2"/>
        </children>
    </GridPane>
    

    RegisterController.java:

    package loginExample;
    
    import javafx.fxml.FXML;
    import javafx.fxml.FXMLLoader;
    import javafx.scene.control.Button;
    
    import java.io.IOException;
    
    public class RegisterController {
    
        @FXML
        private Button btnCancel;
    
        private MainController mainController;
    
        public RegisterController(MainController mainController) {
            this.mainController = mainController;
        }
    
        @FXML
        private void initialize() {
    
            btnCancel.setOnAction(event -> {
                try {
                    FXMLLoader loader = new FXMLLoader(getClass().getResource("/loginExample/LoginLayout.fxml"));
                    loader.setController(new LoginController(mainController));
    
                    // Set our RegisterLayout as the new content for our MainLayout window
                    mainController.getContentPane().getChildren().clear();
                    mainController.getContentPane().getChildren().add(loader.load());
    
                } catch (IOException e) {
                    e.printStackTrace();
                }
            });
    
        }
    }
    

    RegisterLayout.fxml:

    <?xml version="1.0" encoding="UTF-8"?>
    
    <?import javafx.scene.control.*?>
    <?import javafx.scene.layout.*?>
    <GridPane hgap="10.0" vgap="5.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/9.0.1">
        <columnConstraints>
            <ColumnConstraints halignment="RIGHT" hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0"/>
            <ColumnConstraints hgrow="SOMETIMES" minWidth="-Infinity"/>
        </columnConstraints>
        <rowConstraints>
            <RowConstraints minHeight="-Infinity" vgrow="NEVER"/>
            <RowConstraints minHeight="-Infinity" vgrow="SOMETIMES"/>
            <RowConstraints minHeight="-Infinity" vgrow="SOMETIMES"/>
            <RowConstraints minHeight="-Infinity" vgrow="SOMETIMES"/>
            <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES"/>
        </rowConstraints>
        <children>
            <HBox alignment="CENTER" GridPane.columnSpan="2">
                <children>
                    <Label style="-fx-font-size: 150%; -fx-font-weight: bold;" text="Fill Form to Register:"/>
                </children>
            </HBox>
            <Label text="Username:" GridPane.rowIndex="1"/>
            <Label text="Password:" GridPane.rowIndex="2"/>
            <Label text="Re-enter Password:" GridPane.rowIndex="3"/>
            <TextField GridPane.columnIndex="1" GridPane.rowIndex="1"/>
            <HBox alignment="CENTER_RIGHT" spacing="10.0" GridPane.columnSpan="2" GridPane.rowIndex="4">
                <children>
                    <Button fx:id="btnCancel" cancelButton="true" mnemonicParsing="false" text="Cancel"/>
                    <Button defaultButton="true" mnemonicParsing="false" text="Register"/>
                </children>
            </HBox>
            <PasswordField GridPane.columnIndex="1" GridPane.rowIndex="2"/>
            <PasswordField GridPane.columnIndex="1" GridPane.rowIndex="3"/>
        </children>
    </GridPane>
    

    The Result:

    screenshot 1screenshot2

    This is a very simple example meant to demonstrate the concept; you'd likely want to structure your real world project a little differently.