Warm tip: This article is reproduced from stackoverflow.com, please click
javafx listener javafx-8

TextField textProperty Listener across embedded scenes JavaFX

发布于 2020-04-11 11:40:28

This may be a little convoluted but please bear with me.

I have 3 scenes built using SceneBuilder. The first ("Main") I am using as the parent scene. This scene contains 1 AnchorPane which holds a TabPane and a ToolBar which holds 3 Buttons ("Previous", "Next" and "Close").

The second scene ("PersonaDetails") contains an AnchorPane, a GridPane a number of Textflow (which I am using as field labels) several TextField and DatePicker. This whole scene is embedded into one of the tabs on the TabPane on the first scene ("Main").

The third scene ("Address") is very similar to the second where it contains an AnchorPane, a GridPane a number of Textflow (which I am using as field labels) several TextField and ComboBox. This whole scene is embedded into the other tab on the TabPane on the first scene ("Main").

(I have included the FXML script for each below)

The application will later include additional scenes on additional tab on the TabPane on the first scene ("Main"). This application will form part of larger application and is meant to be a kind of wizard that allows new clients to be registered.

Furthermore, each FXML file and its Controller are in separate packages.

This issue I am having is that I need to add .textProperty() listeners on several of the Textfield so that I can enable, or disable the "Next" Button on the first, or parent, scene ("Main").

I have tried the following code in the MainController class, but it does not work, although it does not generate any errors.

package com.yas.registrationwizard.main;

import com.yas.registrationwizard.personaldetails.PersonalDetailsController;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.TabPane;
import javafx.scene.control.ToolBar;
import javafx.scene.layout.AnchorPane;

import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;

public class MainController implements Initializable {

    @FXML AnchorPane apMain;
    @FXML ToolBar tbMain;
    @FXML TabPane tpMain;

    @FXML Button btnPrevious;
    @FXML Button btnNext;
    @FXML Button btnClose;

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("../personaldetails/PersonalDetails.fxml"));
        try {
            fxmlLoader.load();
        } catch (IOException e) {
            e.printStackTrace();
        }
        PersonalDetailsController personalDetailsController = fxmlLoader.getController();
        personalDetailsController.tfFirstName.textProperty().addListener((obs, oldVal, newVal) -> {
            if(newVal.equals("")) {
                btnNext.setDisable(true);
            } else {
                btnNext.setDisable(false);
            }
        });
    }
}

The FXML scripts are as follows:

Main

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

<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane fx:id="apMain" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.yas.registrationwizard.main.MainController">
    <children>
        <ToolBar fx:id="tbMain" layoutX="259.0" layoutY="339.0" prefHeight="40.0" prefWidth="200.0" AnchorPane.bottomAnchor="5.0" AnchorPane.leftAnchor="5.0" AnchorPane.rightAnchor="5.0" AnchorPane.topAnchor="355.0">
            <items>
                <Pane prefHeight="30.0" prefWidth="370.0" />
                <Button fx:id="btnPrevious" maxHeight="25.0" maxWidth="65.0" minHeight="25.0" minWidth="65.0" mnemonicParsing="false" prefHeight="25.0" prefWidth="65.0" text="Previous" />
                <Button fx:id="btnNext" layoutX="10.0" layoutY="13.0" maxHeight="25.0" maxWidth="65.0" minHeight="25.0" minWidth="65.0" mnemonicParsing="false"  prefHeight="25.0" prefWidth="65.0" text="Next" />
                <Button fx:id="btnClose" layoutX="66.0" layoutY="13.0" maxHeight="25.0" maxWidth="65.0" minHeight="25.0" minWidth="65.0" mnemonicParsing="false" prefHeight="25.0" prefWidth="65.0" text="Close" />
            </items>
        </ToolBar>
        <TabPane fx:id="tpMain" layoutX="14.0" layoutY="14.0" prefHeight="335.0" prefWidth="200.0" tabClosingPolicy="UNAVAILABLE" AnchorPane.bottomAnchor="50.0" AnchorPane.leftAnchor="5.0" AnchorPane.rightAnchor="5.0" AnchorPane.topAnchor="5.0">
            <tabs>
                <Tab fx:id="tabPersonalDetails" text="Personal Deatils">
               <content>
                  <fx:include fx:id="apPersonalDetails" source="../personaldetails/PersonalDetails.fxml" />
               </content></Tab>
                <Tab fx:id="tabAddress" text="Address">
               <content>
                  <fx:include fx:id="apAddress" source="../address/Address.fxml" />
               </content></Tab>
            </tabs>
        </TabPane>
    </children>
</AnchorPane>

Address

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

<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane fx:id="apAddress" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="325.0" prefWidth="590.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.yas.registrationwizard.address.AddressController">
   <children>
      <GridPane fx:id="gpAddress" layoutX="195.0" layoutY="103.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
        <columnConstraints>
            <ColumnConstraints hgrow="SOMETIMES" maxWidth="103.0" minWidth="3.0" prefWidth="82.0" />
            <ColumnConstraints hgrow="SOMETIMES" maxWidth="219.0" minWidth="10.0" prefWidth="155.0" />
            <ColumnConstraints hgrow="SOMETIMES" maxWidth="422.0" minWidth="10.0" prefWidth="274.0" />
          <ColumnConstraints hgrow="SOMETIMES" maxWidth="93.0" minWidth="0.0" prefWidth="78.0" />
        </columnConstraints>
        <rowConstraints>
            <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
            <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
            <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
            <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
            <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
            <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
            <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
            <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
            <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
            <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
          <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
        </rowConstraints>
         <children>
            <Label alignment="CENTER_RIGHT" contentDisplay="RIGHT" prefHeight="17.0" prefWidth="295.0" text="House Name / Number:" GridPane.columnIndex="1" GridPane.rowIndex="2">
               <GridPane.margin>
                  <Insets right="10.0" />
               </GridPane.margin></Label>
            <Label alignment="CENTER_RIGHT" contentDisplay="RIGHT" prefHeight="17.0" prefWidth="246.0" text="Address Line 1:" GridPane.columnIndex="1" GridPane.rowIndex="3">
               <GridPane.margin>
                  <Insets right="10.0" />
               </GridPane.margin></Label>
            <Label alignment="CENTER_RIGHT" contentDisplay="RIGHT" prefHeight="17.0" prefWidth="239.0" text="Address Line 2:" GridPane.columnIndex="1" GridPane.rowIndex="4">
               <GridPane.margin>
                  <Insets right="10.0" />
               </GridPane.margin></Label>
            <Label alignment="CENTER_RIGHT" contentDisplay="RIGHT" prefHeight="17.0" prefWidth="234.0" text="Town / City:" GridPane.columnIndex="1" GridPane.rowIndex="5">
               <GridPane.margin>
                  <Insets right="10.0" />
               </GridPane.margin></Label>
            <Label alignment="CENTER_RIGHT" contentDisplay="RIGHT" prefHeight="17.0" prefWidth="237.0" text="Region / County:" GridPane.columnIndex="1" GridPane.rowIndex="6">
               <GridPane.margin>
                  <Insets right="10.0" />
               </GridPane.margin></Label>
            <TextField fx:id="tfHseNameNum" GridPane.columnIndex="2" GridPane.rowIndex="2" />
            <TextField fx:id="tfAddLine1" GridPane.columnIndex="2" GridPane.rowIndex="3" />
            <TextField fx:id="tfAddLine2" GridPane.columnIndex="2" GridPane.rowIndex="4" />
            <TextField fx:id="tfTownCity" GridPane.columnIndex="2" GridPane.rowIndex="5" />
            <TextField fx:id="tfRegionCounty" GridPane.columnIndex="2" GridPane.rowIndex="6" />
            <Label alignment="CENTER_RIGHT" contentDisplay="RIGHT" prefHeight="17.0" prefWidth="234.0" text="Postcode:" GridPane.columnIndex="1" GridPane.rowIndex="7">
               <GridPane.margin>
                  <Insets right="10.0" />
               </GridPane.margin>
            </Label>
            <TextField fx:id="tfPostcode" GridPane.columnIndex="2" GridPane.rowIndex="7" />
            <Label alignment="CENTER_RIGHT" contentDisplay="RIGHT" prefHeight="17.0" prefWidth="234.0" text="Country:" GridPane.columnIndex="1" GridPane.rowIndex="8">
               <GridPane.margin>
                  <Insets right="10.0" />
               </GridPane.margin>
            </Label>
            <ComboBox fx:id="cboCountry" prefHeight="25.0" prefWidth="294.0" GridPane.columnIndex="2" GridPane.rowIndex="8" />
         </children>
      </GridPane>
   </children>
</AnchorPane>

PersonDetails

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

<?import javafx.scene.text.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.paint.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>

<AnchorPane fx:id="apPersonalDetails" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="325.0" prefWidth="590.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.yas.registrationwizard.personaldetails.PersonalDetailsController">
   <children>
      <GridPane fx:id="gpPersonalDetails" layoutX="195.0" layoutY="103.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
        <columnConstraints>
            <ColumnConstraints hgrow="SOMETIMES" maxWidth="80.0" minWidth="37.0" prefWidth="37.0" />
            <ColumnConstraints hgrow="SOMETIMES" maxWidth="236.0" minWidth="10.0" prefWidth="236.0" />
            <ColumnConstraints hgrow="SOMETIMES" maxWidth="422.0" minWidth="10.0" prefWidth="277.0" />
          <ColumnConstraints hgrow="SOMETIMES" maxWidth="80.0" minWidth="38.0" prefWidth="41.0" />
        </columnConstraints>
        <rowConstraints>
            <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
            <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
            <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
            <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
            <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
            <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
            <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
            <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
          <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
        </rowConstraints>
         <children>
            <TextField fx:id="tfFirstName" GridPane.columnIndex="2" GridPane.rowIndex="2" />
            <TextField fx:id="tfMidNameInit" GridPane.columnIndex="2" GridPane.rowIndex="3" />
            <TextField fx:id="tfLastName" GridPane.columnIndex="2" GridPane.rowIndex="4" />
            <DatePicker fx:id="dpDoB" prefHeight="25.0" prefWidth="365.0" GridPane.columnIndex="2" GridPane.rowIndex="5" />
            <TextField fx:id="tfNatInsNum" GridPane.columnIndex="2" GridPane.rowIndex="6" />
            <TextFlow fx:id="tflFirstName" prefHeight="16.0" prefWidth="162.0" style="-fx-text-alignment: right;" styleClass="txtFlow" GridPane.columnIndex="1" GridPane.rowIndex="2">
               <GridPane.margin>
                  <Insets right="10.0" top="10.0" />
               </GridPane.margin></TextFlow>
            <TextFlow fx:id="tflLastName" prefHeight="16.0" prefWidth="162.0" style="-fx-text-alignment: right;" styleClass="txtFlow" GridPane.columnIndex="1" GridPane.rowIndex="4">
               <padding>
                  <Insets right="10.0" top="10.0" />
               </padding>
            </TextFlow>
            <TextFlow fx:id="tflDoB" prefHeight="16.0" prefWidth="162.0" style="-fx-text-alignment: right;" styleClass="txtFlow" GridPane.columnIndex="1" GridPane.rowIndex="5">
               <padding>
                  <Insets right="10.0" top="10.0" />
               </padding>
            </TextFlow>
            <TextFlow fx:id="tflNatInsNum" prefHeight="16.0" prefWidth="162.0" style="-fx-text-alignment: right;" styleClass="txtFlow" GridPane.columnIndex="1" GridPane.rowIndex="6">
               <padding>
                  <Insets right="10.0" top="10.0" />
               </padding>
            </TextFlow>
            <TextFlow fx:id="tflMidNameInit" prefHeight="16.0" prefWidth="162.0" style="-fx-text-alignment: right;" styleClass="txtFlow" GridPane.columnIndex="1" GridPane.rowIndex="3">
               <padding>
                  <Insets right="10.0" top="10.0" />
               </padding>
            </TextFlow>
         </children>
      </GridPane>
   </children>
</AnchorPane>

The folder structure for the project is shown in the image below:

Folder Structure

Questioner
Yasterfari
Viewed
77
Sai Dandem 2020-02-04 06:14

Firstly, I am bit confused with the wording "scene" in the question. I believe what you mentioned in the question is about handling the nodes, because all the fxmls are handled in the same Scene/Stage.

Anyway, the main problem of the issue lies in the MainController initialize method. You are loading a new instance of PersonalDetailsController and working on it, instead of working on the controller that is actually binded to MainController.

When including fxmls in an fxml, the sub fxml's controller will be already injected to the main fxml controller. So I believe changing your MainController code as below should work as expected.

Update: Sorry for a bit misleading info. The correct thing is, you need to inject the controller with a certain naming convention. If you are including an fxml using fx:include, to get the controller you need to inject with a naming convention <fxid>Controller and <fxid> for getting the node reference.

So considering your example, for the given fx:include line:

<fx:include fx:id="apPersonalDetails" source="../personaldetails/PersonalDetails.fxml" />

your code in MainController should be:

// For controller reference
@FXML PersonalDetailsController apPersonalDetailsController;

// For Node reference
@FXML AnchorPane apPersonalDetails;

So the updated code will be as below:

import com.yas.registrationwizard.personaldetails.PersonalDetailsController;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.TabPane;
import javafx.scene.control.ToolBar;
import javafx.scene.layout.AnchorPane;

import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;

public class MainController implements Initializable {

    @FXML AnchorPane apMain;
    @FXML ToolBar tbMain;
    @FXML TabPane tpMain;

    @FXML Button btnPrevious;
    @FXML Button btnNext;
    @FXML Button btnClose;

    @FXML PersonalDetailsController apPersonalDetailsController;

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        apPersonalDetailsController.tfFirstName.textProperty().addListener((obs, oldVal, newVal) -> {
            if(newVal.equals("")) {
                btnNext.setDisable(true);
            } else {
                btnNext.setDisable(false);
            }
        });
    }
}