0

I have two FXML files. The first describes the first pane to show up, which contains a tab pane, a menu and a menu item, which is supposed to open a new tab in the tab pane and draw a new set of Nodes in it. This is the code:

public class Main extends Application {

private Pane mainRoot;
private Pane secondaryRoot;

@Override
public void start(Stage primaryStage) {
    try {
        mainRoot = (Pane) ResourceLoader
                .load("MainFXML.fxml");

        secondaryRoot = (Pane) ResourceLoader.load(("SecondaryFXML.fxml"));

        Scene scene = new Scene(mainRoot);

        primaryStage.setScene(scene);
        primaryStage.show();

        thisMain = this;

    } catch (Exception e) {
        e.printStackTrace();
    }
}

public static void main(String[] args) {
    launch(args);
}

private static Main thisMain;

public static Main getMain() {
    return thisMain;
}
public Pane getSecondaryRootPane() {
    return secondaryRoot;
}

Then I have a single controller associated to both FXML files. So, it has the tab pane as a FXML-annotated field. Furthermore, it handles the click event on the menu item, which is the one creating the new tab:

public class GUIController {
@FXML
private TabPane tabPane;

public void newTabRequest(Event e) {
    // create the new tab
    Tab newTab = new Tab("New tab");

    // set the content of the tab to the previously loaded pane
    newTab.setContent(Main.getMain().getSecondaryRootPane());

    // add the new tab to the tab pane and focus on it
    tabPane.getTabs().add(newTab);
    tabPane.getSelectionModel().select(newTab);

    initializeComboBoxValues(); // HERE I HAVE A PROBLEM
}}

Last line of the controller invokes a method, whose code is as follow:

private void initializeComboBoxValues() {
    myComboBox.getItems().add(MyEnum.myEnumValue1);
    myComboBox.getItems().add(MyEnum.myEnumValue2);
}

and I have a field @FXML ComboBox having the same name as the corresponding component declared in the FXML and that I am trying to fill up with values. The issue is that myComboBox results null. Where am I wrong? Where am I designing it wrong?

If it can help, I want to make this point: I created and added a test button in the new tab. The event associated to this button invokes the same initializeComboBoxValues method. Well, that works (given I remove its invocation from the newTabRequest handler, so to avoid the NPE).

Manu
  • 4,019
  • 8
  • 50
  • 94
  • What is `ResourceLoader` and why don't you use `FXMLLoader`? – fabian Jul 12 '14 at 17:38
  • It's just a class I created that uses FXMLLoader: its load method is return FXMLLoader.load(getClass().getResource(filePath)); – Manu Jul 12 '14 at 17:39

1 Answers1

1

Every time you use FXMLLoader, you create a new controller. Since you don't do anything to connect both controllers, every controller has only the @FXML annotated fields set, that were in it's own Pane at the time it was inflated.

You'll have to establish some kind of communication between both controllers to get it working.

You can get the controller from the FXMLLoader, if you use a instance of the FXMLLoader and a non-static load function, e.g. like this (don't reuse a FXMLLoader to load a second fxml file):

FXMLLoader loader = new FXMLLoader();
// be sure to use a non-static load method, like load(InputStream)
secondaryRoot = loader.load(getClass().getResourceAsStream("SecondaryFXML.fxml"));
FXMLDocumentController secondaryController = loader.getController(); // Change type to your controller type here

You can then add a method to your controller that allows you to pass the other controller to the controller. That way you can access the fields from the other controller.

I strongly advise you to create different controller classes for both layouts. Otherwise that only adds confusion.

fabian
  • 80,457
  • 12
  • 86
  • 114
  • Thanks, the fact that a new controller is instantiated is eye-opening. Then yes, different controller classes are appropriate. Rather than coupling the controllers directly, though, I will create a catalogue of controllers to query in order to access other controller services. – Manu Jul 13 '14 at 16:04