0

I'm working on a project that requires a "tabled" representation of VBoxes. My hierarchical layout of the application is GridPane -> VBox (in one of the Cells) -> VBoxes (that display different datasets on top of each other) -> Data. I have two Scenes.

Data is displayed on Scene 1. The user can add data through a form and by clicking a button on Scene 2. Then, the added data should be displayed below the existing data as a VBox within the parent-VBox on Scene 1 again.

Here is the code that will make it clear:

My Scene 1 .fxml file looks the following (Simplified):

<GridPane fx:id="grid" fx:controller="application.Controller">
  [here: ColumnConstraints]
  <children>
    <VBox fx:id="parentBox" GridPane.columnIndex="0" GridPane.rowIndex="1"/>
    <Button fx:id="goToScene2" text="+" onAction="#goToScene2"/> 
  </children>  
</GridPane>

Scene 2 just has a button and a TextField:

<GridPane fx:id="grid" fx:controller="application.AddDataController">
  [here: ColumnConstraints]
  <children>
    <Button fx:id="addData" text="add" onAction="#bAddData"/> 
    <TextField fx:id="data"/>
  </children>  
</GridPane>

My Scene 1 controller (controller) looks like this:

public class Controller implements Initializable  {
  @FXML Button goToScene2;
  @FXML VBox parentBox;

  @Override
  public void initialize(URL location, ResourceBundle resources) {
  }

  public void addData(String s) {
    Label lbl = new Label(s);
    VBox dataBox = new VBox();
    dataBox.setPadding(new Insets(15, 5, 15, 5));
    dataBox.setSpacing(5);
    dataBox.setMaxHeight(80);
    dataBox.getChildren().add(lbl);
    parentBox.getChildren().add(dataBox);
  }
}

This is designed as it is because the dataBox contains more elements than the label, but that doesn't seem relevant to me in this context.

My Scene 2 controller (addDataController) looks like this:

@FXML Button addData;
@FXML TextField data;

@FXML protected void bAddData(){
  String content = data.getText();    
  FXMLLoader fxmlLoader = new FXMLLoader();
  Pane p = fxmlLoader.load(getClass().getResource("scn1.fxml").openStream());
  Controller cont = (Controller) fxmlLoader.getController();

  cont.addData(content);
}

So, when one clicks on the Add-Data-Button in Scene 2, the triggered method passes the entered data to the Controller of Scene 1. This is because the new data should be displayed in Scene 1 now.

I feel like the logic does not work (edited here), because when I ask for

System.out.println(parentBox.getChildren().size();

before and after the data was added, it always has one single Child, even though it should have one more...

If I artificially fill a String-Array and move everything from addData to(String s) to Initialize(...), it does work and the data shows up as VBoxes in the parent-VBox.

I didn't post the main class, because loading Controllers and Scene change is not an issue.

Thank you all very very much for your help! :)

OttPrime
  • 1,878
  • 1
  • 15
  • 24
s4inz
  • 123
  • 10
  • 1
    I don't believe that the `Controller const` instance that you are using to call `AddData(content)` with is the same instance of `Controller` that your main `Scene` is using. The `FXMLLoader` is creating a new instance that isn't in the scene graph. A simple fix may be to pass the main scene controller to the second scene. What does your `goToScene` method look like? – OttPrime Jul 06 '16 at 19:21
  • That's actually something I haven't seen that way. However, the used Controller is defined in the .fxml file, so I can't access it from any other class. It has never been loaded within the JavaFX code. Making the Controller classes static hasn't worked either. – s4inz Jul 07 '16 at 07:55
  • Sorry for that. The `goToScene` method calls a method in the main program class and passes a String. This String is handled in a switch-case statement and triggers `currentStage.setScene(newScene)`. – s4inz Jul 07 '16 at 07:58
  • @OttPrime is correct. Every time you call `load` on the `FXMLLoader`, it creates a new controller instance. Since you never display `p`, the controller whose data you are changing is connected to a UI element that is never displayed. You need to get the controller from the `FXMLLoader` when you load *and display* it. – James_D Jul 07 '16 at 08:15
  • Yeah that sounds plausible. But isn't that like chasing one's own tail....the `.fxml` file contains all information about displaying the data. I'd just like to append a child to a node in there, which I need the `Controller` for. How is a `Controller` class instantiated when it is defined in the `.fxml`? Can I access that same intance (without always creating a new one)? – s4inz Jul 07 '16 at 08:23

1 Answers1

0

Just to provide more detailed information, together with an idea, that I don't know how to implement.

This is my main class:

@Override
public void start(Stage primaryStage) {
  currentStage = primaryStage;
  Parent root = FXMLLoader.load(getClass().getResource("Scene1.fxml"));
  scene1 = new Scene(root);
}

Could I load the controller at this point and make a getter that passes the Controller? So I'd only have one single instance of it throughout the program.

I tried inserting:

FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.load(getClass().getResource("Scene1.fxml").openStream());
Controller controller = (Controller) fxmlLoader.getController();

after the

Parent root ...

line. But that's loading the .xml file twice. How can I nicely connect both parts?

Thanks so much for you patience!

EDIT::::::::: The following code worked for me:

@Override
public void start(Stage primaryStage) {
  currentStage = primaryStage;
  FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("Scene1.fxml"));
  Parent root =(Parent) fxmlLoader.load();
  controller = (Controller) fxmlLoader.getController();
}

Now, I can ask the main class for controller and it always passes that single instance.

Thank you for pointing me to the right direction! :)

s4inz
  • 123
  • 10