Search code examples
javajavafxfxml

JavaFX StackPane not showing object from inside of method


Okay, I re-edited this question with a much easier smaller example.

My question is though, I have a StackPane, myStackPane that I am trying to load the Label onto. When the program first runs, the StackPane is empty. When I click on the handleButtonAction button it takes me to another stage so I can enter in the text I want on the label. When that new text is saved, it passes the new settings back to the main controller and updates the text of the label. It then adds the label to the StackPane.

However.... It's not displaying the new label in the stackpane. I know I am getting into the method and the variable is being passed correctly since I can System.out.println everything and it's all appropriate. But why will it not add it to the StackPane?

Thanks everyone!

Problems.java

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class Problems extends Application {

    @Override
    public void start(Stage stage) throws Exception {
        Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));

        Scene scene = new Scene(root);

        stage.setScene(scene);
        stage.show();
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        launch(args);
    }

}

FXMLDocumentController

import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class FXMLDocumentController implements Initializable {

    @FXML
    private Button button;
    @FXML
    private StackPane myStackPane;

    private Label label = new Label("Text 1");

    @FXML
    private void handleButtonAction(ActionEvent event) {
        FXMLLoader secondLoader = new FXMLLoader();

        secondLoader.setLocation(getClass().getResource("FXMLSecond.fxml"));

        try {
            secondLoader.load();
        } catch (IOException e) {
        }

        FXMLSecondController secondController = secondLoader.getController();

        secondController.setFieldText(label.getText());

        Parent p = secondLoader.getRoot();
        Stage stage = new Stage();
        stage.setScene(new Scene(p));
        stage.showAndWait();
    }

    public void putLabelOnStackPane(String value) {
        label.setText(value);

        myStackPane.getChildren().add(label);
    }

    @Override
    public void initialize(URL url, ResourceBundle rb) {
        // TODO
    }    

}

FXMLSecondController

import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.stage.Stage;

public class FXMLSecondController implements Initializable {

    @FXML
    private Button saveText;
    @FXML
    private TextField textField;

    /**
     * Initializes the controller class.
     */


    @Override
    public void initialize(URL url, ResourceBundle rb) {
        // TODO
    }    

    @FXML
    private void saveTextBtnPressed(ActionEvent event) {
        FXMLLoader firstLoader = new FXMLLoader();

        firstLoader.setLocation(getClass().getResource("FXMLDocument.fxml"));

        try {
            firstLoader.load();
        } catch (IOException e) {
        }

        FXMLDocumentController firstController = firstLoader.getController();

        firstController.putLabelOnStackPane(textField.getText());

        Stage stage = (Stage) saveText.getScene().getWindow();
        stage.close();
    }

    public void setFieldText(String value) {
        textField.setText(value);
    }

}

FXMLDocument

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

<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.StackPane?>

<AnchorPane id="AnchorPane" prefHeight="200" prefWidth="320" xmlns="http://javafx.com/javafx/10.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="problems.FXMLDocumentController">
    <children>
        <Button fx:id="button" layoutX="128.0" layoutY="14.0" onAction="#handleButtonAction" text="Click Me!" />
      <StackPane fx:id="myStackPane" layoutX="61.0" layoutY="45.0" prefHeight="150.0" prefWidth="200.0" />
    </children>
</AnchorPane>

FXMLSecond

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

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>


<AnchorPane id="AnchorPane" prefHeight="400.0" prefWidth="600.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/10.0.1" fx:controller="problems.FXMLSecondController">
   <children>
      <Button fx:id="saveText" layoutX="274.0" layoutY="106.0" mnemonicParsing="false" onAction="#saveTextBtnPressed" text="Button" />
      <TextField fx:id="textField" layoutX="226.0" layoutY="175.0" />
   </children>
</AnchorPane>

Solution

  • The main issue is that by

    FXMLLoader firstLoader = new FXMLLoader();
    firstLoader.setLocation(getClass().getResource("FXMLDocument.fxml"));
    

    you actually construct a new FXMLLoader rather then getting a reference to the open one.
    You could get a reference to the open FXMLLoader by, for example, using static members (see here) but it would be better to use a slightly different approach:

    FXMLDocumentController.java

    public class FXMLDocumentController implements Initializable {
    
        @FXML
        private Button button;
        @FXML
        private StackPane myStackPane;
    
        private Label label = new Label("Text 1");
    
        @FXML
        private void handleButtonAction(ActionEvent event) {
    
            FXMLLoader secondLoader = new FXMLLoader();
            secondLoader.setLocation(getClass().getResource("FXMLSecond.fxml"));
    
            try {
                secondLoader.load();
            } catch (IOException e) {e.printStackTrace();  }
    
            FXMLSecondController secondController = secondLoader.getController();
            //secondController.setFieldText(label.getText());
            secondController.setTextProperty(label.textProperty());
    
            Parent p = secondLoader.getRoot();
            Stage stage = new Stage();
            stage.setScene(new Scene(p));
            stage.showAndWait();
            myStackPane.getChildren().add(label);
        }
    
        /*
        public void putLabelOnStackPane(String value) {
            label.setText(value);
    
            myStackPane.getChildren().add(label);
        }*/
    
        @Override
        public void initialize(URL url, ResourceBundle rb) { //no need to implement Initializable 
                                                             //if initialize not used 
        }    
    }
    

    FXMLSecondController.java

    public class FXMLSecondController implements Initializable {
    
        @FXML
        private Button saveText;
        @FXML
        private TextField textField;
    
        private StringProperty labelStringProperty;
    
        @Override
        public void initialize(URL url, ResourceBundle rb) {
            textField.setEditable(true);
        }    
    
        @FXML
        private void saveTextBtnPressed(ActionEvent event) {
    
            /** By doing so you actually construct a new FXMLDocument
            FXMLLoader firstLoader = new FXMLLoader();
            firstLoader.setLocation(getClass().getResource("FXMLDocument.fxml"));
    
            try {
                firstLoader.load();
            } catch (IOException e) {
            }
    
            FXMLDocumentController firstController = firstLoader.getController();
            firstController.putLabelOnStackPane(textField.getText());
            */
            labelStringProperty.set(textField.getText());
            Stage stage = (Stage) saveText.getScene().getWindow();
            stage.close();
        }
    
        /*
        public void setFieldText(String value) {
            textField.setText(value);
        }*/
    
        public void setTextProperty(StringProperty labelStringProperty) {
            this.labelStringProperty = labelStringProperty;
            textField.setText(labelStringProperty.get());
        }
    }
    

    Also consider using Dialog for input.

    EDIT To have FXMLSecondController add a label to FXMLDocoment change FXMLDocumentController to pass the needed references to FXMLSecondController:

    public class FXMLDocumentController {
    
        @FXML
        private Button button;
        @FXML
        private StackPane myStackPane;
    
        private Label label = new Label("Text 1");
    
        @FXML
        private void handleButtonAction(ActionEvent event) {
    
            FXMLLoader secondLoader = new FXMLLoader();
            secondLoader.setLocation(getClass().getResource("FXMLSecond.fxml"));
    
            try {
                secondLoader.load();
            } catch (IOException e) {e.printStackTrace();  }
    
            FXMLSecondController secondController = secondLoader.getController();
            secondController.setRefrences(myStackPane.getChildren(), label);
    
            Parent p = secondLoader.getRoot();
            Stage stage = new Stage();
            stage.setScene(new Scene(p));
            stage.showAndWait();
        } 
    }
    

    And have FXMLSecondController use those references:

    public class FXMLSecondController {
    
        @FXML
        private Button saveText;
        @FXML
        private TextField textField;
    
        private ObservableList<Node> children;
        private Label label;
    
        @FXML
        private void saveTextBtnPressed(ActionEvent event) {
    
            label.setText(textField.getText());
            children.add(label);
            Stage stage = (Stage) saveText.getScene().getWindow();
            stage.close();
        }
    
        public void setRefrences(ObservableList<Node> children, Label label) {      
            this.children = children;
            this.label = label;
            textField.setText(label.getText());
        }
    }