0

I need to get to my stackpane that is nested inside a borberpane which is also nested in another borderpane(root).

I did not create this by code, I used scene-builder/FXML.

I am now trying to access it through code. The closes I got was by using a parent and got a unmodifiable list which seems to only have its two child but I cant seem to go deeper.

Here is the Hierarchy---

<BorderPane fx:id="rootPane" focusTraversable="true" minHeight="0.0" minWidth="0.0" prefHeight="771.0" prefWidth="1100.0" stylesheets="@styling.css" xmlns="http://javafx.com/javafx/18" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.example.passwordmanager2.HelloController">
   <center>
      <BorderPane fx:id="borderPane2" BorderPane.alignment="CENTER">
         <top>
            <StackPane fx:id="topBar" alignment="TOP_CENTER" focusTraversable="true" prefHeight="95.0" BorderPane.alignment="CENTER">
               <children>
                  .....

Here is main method

public class HelloApplication extends Application {


    @Override
    public void start(Stage stage) throws IOException {
        FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("hello-view.fxml"));
        double width = 900.0;
        double height = 950.0;

        Scene scene = new Scene(fxmlLoader.load(), width, height);
        Parent root = fxmlLoader.getRoot();
        System.out.println(root.getChildrenUnmodifiable().get(3)); // trying to get stackPane

        stage.setScene(scene);
        stage.initStyle(StageStyle.UNDECORATED);

        ResizeHelper.addResizeListener(stage);

        stage.show();
    }

    public static void main(String[] args) {
        launch();
    }
}
TookLong
  • 39
  • 5
  • Why do you need access to the `Sackpane` in the `Application` class? It seems like you are trying to do something that you should not be doing. – SedJ601 Aug 31 '22 at 23:15

1 Answers1

2

You have to create fields named the same way you specified in the .fxml file (with fx:id="..."), for each component and use the @FXML annotation.

For example you have to add @FXML private BorderPane borderPane2; to access the border pane object.

The best way to implement that is using the MVC design pattern. References:

Basically the View is the GUI (your .fxml files), the Model is the classes you use (for example you could have a Student, a Professor, and some other classes you need to implement a school score system), and the Controller is where you make changes to the model data and update the View consequently.

I'll leave here a very simple example which implements the MVC pattern:

Result:

enter image description here

Project structure:

src
 |
 +- application (package)
      |
      +-- Controller.java
      |
      +-- Main.java
      |
      +-- Test.fxml
      |
      +-- application.css

Main.java class:

package application;

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

public class Main extends Application {
    @Override
    public void start(Stage stage) {
        
        try {
            FXMLLoader loader = new FXMLLoader(Main.class.getResource("ViewTest.fxml"));
            AnchorPane basePane = (AnchorPane) loader.load();
            Scene scene = new Scene(basePane);
            scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
            stage.setTitle("Test");
            stage.setScene(scene);
            stage.show();
            
        } catch(Exception e) {
            e.printStackTrace();
        }
    }
    public static void main(String args[]) {
        launch(args);
    }
}

Model: Counter.java class:

package application;

public class Counter {
    private int value;
    
    public Counter() {
        this.value = 0;
    }
    
    public void increase() {
        this.value++;
    }
    public int getValue() {
        return this.value;
    }
}

Controller: Controller.java class:

package application;

import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Slider;
import javafx.scene.control.TextField;
import javafx.scene.control.Button;
import javafx.scene.layout.AnchorPane;

public class Controller {
    @FXML
    private AnchorPane base;
    @FXML private Slider sliderTest;
    @FXML private TextField textFieldTest;
    @FXML private Button buttonTest;
    
    private Counter counter;

    public void initialize()
    {
        this.counter = new Counter();
        
        textFieldTest.setText("" + counter.getValue());
    }
    
    @FXML private void test(ActionEvent event)
    {
        this.counter.increase();
        this.textFieldTest.setText("" + counter.getValue());
    }
}

View ViewTest.fxml file:

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

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

<AnchorPane id="base" fx:id="base" prefHeight="400.0" prefWidth="400.0" xmlns="http://javafx.com/javafx/18" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.Controller">
   <children>
      <TextField fx:id="textFieldTest" layoutX="125.0" layoutY="187.0" />
      <Button fx:id="buttonTest" layoutX="174.0" layoutY="273.0" mnemonicParsing="false" onAction="#test" text="Increment" />
      <Slider fx:id="sliderTest" layoutX="14.0" layoutY="33.0" />
   </children>
</AnchorPane>

application.css file:

.slider {
    -fx-base: red;
}
.button {
    -fx-base: blue;
}

Another way to access a component (for example if it doesn't have a fx:id), you could start from the parent and, knowing the child node index, or iterating through them, you could get the interested node and cast it.
Example:

Node n = base.getChildren().get(0);
TextField tf = (TextField) n;
System.out.println(tf);
tf.setText("Hello JavaFX");

Output:

TextField[id=textFieldTest, styleClass=text-input text-field]

here the TextField is the 1st child of AnchorPane:

enter image description here

mikyll98
  • 1,195
  • 3
  • 8
  • 29
  • I can only do this in the controller right? Is there anyway to get a different class to access it using @FXML other than the one controller? – TookLong Aug 14 '22 at 23:15
  • exactly, you could write an initialize() method where you put all the logic to initialize components (for example you might want to dynamically popolate a ListView) – mikyll98 Aug 14 '22 at 23:18
  • @TookLong I've updated the answer with a very simple project which implements the MVC pattern (Model, View, Controller) using JavaFX and .fxml files – mikyll98 Aug 14 '22 at 23:27
  • 1
    @TookLong I missed the second part of your comment. Honestly I can't find a good reason why someone would want to access the view elements outside the controller, but in case you wanna dirty your hands, here's an answer that I think might help you: https://stackoverflow.com/a/29258336/19544859 – mikyll98 Aug 14 '22 at 23:59
  • 1
    @TookLong If you are trying to access the view elements outside the controller, you are simply doing things wrong. – James_D Aug 15 '22 at 04:21
  • 1
    @mikyll98 The answer linked in your comment doesn’t access UI elements outside the controller though. (And you should never do this, as you imply.). Also, if I’m being picky, your answer isn’t really an example of MVC; there is no separate model class. – James_D Aug 15 '22 at 04:22