Search code examples
javajavafxfxml

Fxml needs to get same controller as fxml that it is in


I have a borderpane written in fxml which has interchangeable layouts for the left and center pane.

borderpane fxml:

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

<?import javafx.scene.image.*?>
<?import java.net.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.text.*?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<BorderPane fx:id="mainBorderPane" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="Program.gui.MainSceneController">
   <left>
      <ScrollPane fitToWidth="true" BorderPane.alignment="CENTER">
         <content>
            <VBox id="sideMenu" spacing="10.0" styleClass="side-menu">
               <children>
                   <Button fx:id="buttonCash" contentDisplay="TOP" maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#buttonCashPress" styleClass="side-menu-button" text="Cash">
                     <cursor>
                        <Cursor fx:constant="HAND" />
                     </cursor>
                     <graphic>
                        <ImageView fitHeight="50.0" fitWidth="50.0" pickOnBounds="true" preserveRatio="true">
                           <image>
                              <Image url="@../../Libraries/Icons/pos.png" />
                           </image>
                        </ImageView>
                     </graphic></Button>
                   <Button fx:id="buttonUsers" contentDisplay="TOP" layoutX="10.0" layoutY="10.0" maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#buttonUsersPress" styleClass="side-menu-button" text="Users">
                     <cursor>
                        <Cursor fx:constant="HAND" />
                     </cursor>
                     <graphic>
                        <ImageView fitHeight="50.0" fitWidth="50.0" pickOnBounds="true" preserveRatio="true">
                           <image>
                              <Image url="@../../Libraries/Icons/users.png" />
                           </image>
                        </ImageView>
                     </graphic></Button>
                   <Button fx:id="buttonInventory" contentDisplay="TOP" layoutX="10.0" layoutY="35.0" maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#buttonInventoryPress" styleClass="side-menu-button" text="Inventory">
                     <cursor>
                        <Cursor fx:constant="HAND" />
                     </cursor>
                     <graphic>
                        <ImageView fitHeight="50.0" fitWidth="50.0" pickOnBounds="true" preserveRatio="true">
                           <image>
                              <Image url="@../../Libraries/Icons/inventory.png" />
                           </image>
                        </ImageView>
                     </graphic></Button>
                   <Button fx:id="buttonCustomers" contentDisplay="TOP" layoutX="10.0" layoutY="60.0" maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#buttonCustomersPress" styleClass="side-menu-button" text="customers">
                     <cursor>
                        <Cursor fx:constant="HAND" />
                     </cursor>
                     <graphic>
                        <ImageView fitHeight="50.0" fitWidth="50.0" pickOnBounds="true" preserveRatio="true">
                           <image>
                              <Image url="@../../Libraries/Icons/customers.png" />
                           </image>
                        </ImageView>
                     </graphic></Button>
                  <Button fx:id="buttonLogout" contentDisplay="TOP" layoutX="31.0" layoutY="232.0" maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#buttonLogoutPress" styleClass="side-menu-button" text="Log out">
                     <cursor>
                        <Cursor fx:constant="HAND" />
                     </cursor>
                     <graphic>
                        <ImageView fitHeight="50.0" fitWidth="50.0" pickOnBounds="true" preserveRatio="true">
                           <image>
                              <Image url="@../../Libraries/Icons/log_out.png" />
                           </image>
                        </ImageView>
                     </graphic>
                  </Button>
               </children>
            </VBox>
         </content>
         <BorderPane.margin>
            <Insets />
         </BorderPane.margin>
         <padding>
            <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
         </padding>
      </ScrollPane>
   </left>
   <bottom>
      <HBox BorderPane.alignment="CENTER">
         <children>
            <Label fx:id="currentUserLabel" styleClass="current-user-label" text="Current user: name here">
               <font>
                  <Font size="15.0" />
               </font>
            </Label>
            <AnchorPane HBox.hgrow="ALWAYS" />
            <Label fx:id="currentTimeLabel" layoutX="10.0" layoutY="10.0" styleClass="current-time-label" text="TIME">
               <font>
                  <Font size="15.0" />
               </font>
            </Label>
            <AnchorPane layoutX="170.0" layoutY="10.0" HBox.hgrow="ALWAYS" />
            <Label fx:id="currentDateLabel" layoutX="170.0" layoutY="10.0" styleClass="current-date-label" text="DATE">
               <font>
                  <Font size="15.0" />
               </font>
            </Label>
         </children>
         <padding>
            <Insets left="10.0" right="10.0" />
         </padding>
      </HBox>
   </bottom>
   <stylesheets>
      <URL value="@ScreenStylesheet.css" />
   </stylesheets>
</BorderPane>

One of the buttons found in the fxml file will run some code in the controller to change the right sides of the borderpane with a VBox loaded from another fxml file.

Now to question is how do I tell the fxml of the right pane to use the same controller as the main borderpane? In other words I want to inherit the controller.

If I define the controller in its fxml with fx:controller="Program.gui.MainSceneController" it just makes a new instance, and if I don't define it, it just gives errors because the buttons inside it don't have a controller to refer to.

edit: someone asked for the code that changes the right side of the screen here it is: (note: this is found in the controller)

//mainBorderPane is gotten from the main fxml file.
FXMLLoader loader = new FXMLLoader(getClass().getResource("cashRightArea.fxml"));
            mainBorderPane.setRight(loader.load());

Solution

  • If you want to assign a specific controller to a view you can do it like this:

    Main.fxml:

    <?xml version="1.0" encoding="UTF-8"?>
    
    <?import javafx.scene.control.*?>
    <?import java.lang.*?>
    <?import javafx.scene.layout.*?>
    <?import javafx.scene.layout.AnchorPane?>
    
    <AnchorPane minHeight="400.0" minWidth="400.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.MainController">
       <children>
          <Label fx:id="myLabel" layoutX="28.0" layoutY="53.0" text="Label" />
          <Accordion layoutX="42.0" layoutY="158.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" />
          <AnchorPane fx:id="childContainer" layoutX="56.0" layoutY="177.0" maxHeight="200.0" minHeight="200.0" prefHeight="200.0" prefWidth="200.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" />
          <Button fx:id="loadChild" layoutX="135.0" layoutY="43.0" mnemonicParsing="false" text="Load child panel" />
       </children>
    </AnchorPane>
    

    MainController.java:

    public class MainController implements Initializable{
        @FXML
        protected Label     myLabel;
    
        @FXML
        private Button loadChild;
    
        @FXML
        private AnchorPane  childContainer;
    
        @FXML
        protected Button    updateButton;
    
        StringProperty myStringProp=new SimpleStringProperty();
    
    
        private void loadChildpanel() {
            System.out.println(this.hashCode());
            FXMLLoader loader = new FXMLLoader(getClass().getResource("Child.fxml"));
            Node childNode;
            try {
                loader.setController(this);
                childNode = (Node) loader.load();
                childContainer.getChildren().add(childNode);
    
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
    
        }
    
    
        private void updateLabel() {
            myStringProp.setValue("updated text");
            System.out.println(this.hashCode());
        }
    
    
        @Override
        public void initialize(URL location, ResourceBundle resources) {
            myStringProp.set("Initial text");
            loadChild.setOnAction(e->loadChildpanel());
            myLabel.textProperty().bind(myStringProp);
    
            if(updateButton!=null)updateButton.setOnAction(e->updateLabel());
        }
    
    }
    

    Child.fxml:

    <?xml version="1.0" encoding="UTF-8"?>
    
    <?import java.lang.*?>
    <?import javafx.scene.control.*?>
    <?import javafx.scene.layout.*?>
    <?import javafx.scene.layout.AnchorPane?>
    
    
    <AnchorPane prefHeight="100.0" prefWidth="180.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8">
       <children>
          <Button fx:id="updateButton" layoutX="10.0" layoutY="18.0" mnemonicParsing="false" prefHeight="65.0" prefWidth="161.0" text="Update" />
       </children>
    </AnchorPane>
    

    Make sure you do not specify the controller in the Child.fxml file. This solution works, the button in the child view can update the label on the main view.

    However, this is nasty solution, it is not good practice to use the same controller for different views. It is cleaner to have view-controller pairs. When you need to display another view you can do the followings from your parent controller:

    FXMLLoader loader = new 
    FXMLLoader(getClass().getResource(InterfaceToNetworkController.UI_PATH));
    Node rootNode = (Parent) loader.load();
    InterfaceToNetworkController childController = loader.getController();
    mainBorderPane.setRight(rootNode);
    

    Hope it helps