2

Basically, i need to retrieve a VBox from an fxml file and i'm using scene.lookup. But if i search for it, it just crashes with a NullPointerException.

So i tried to print what scene.lookup() finds, and it just finds null (duh), but i can't understand why.

This is my Main class:

public class Main extends Application {

    @Override
    public void start(Stage stage) throws Exception{

        FXMLLoader loader = new FXMLLoader(getClass().getResource("MainWindow.fxml"));
        Parent root = loader.load();
        Scene scene = new Scene(root, 600, 575);
        Controller controller = new Controller();
        controller.setScene(scene);
        System.out.println(scene.lookup("#vBox"));


        stage.setScene(scene);
        stage.setTitle("Test");
        stage.setResizable(false);
        stage.show();
    }

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

And this is my fxml file:

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

<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<BorderPane fx:id="borderPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="575.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controller">
   <top>
      <ToolBar prefHeight="40.0" prefWidth="200.0" BorderPane.alignment="CENTER">
        <items>
          <Button fx:id="addBtn" mnemonicParsing="false" onAction="#addEventHandler" prefHeight="30.0" prefWidth="75.0" text="Add" />
            <Button fx:id="editBtn" mnemonicParsing="false" prefHeight="30.0" prefWidth="75.0" text="Edit..." />
            <Button fx:id="delBtn" mnemonicParsing="false" prefHeight="30.0" prefWidth="75.0" text="Delete" />
        </items>
      </ToolBar>
   </top>
   <center>
      <ScrollPane fx:id="scrollPane" prefHeight="515.0" prefWidth="600.0" BorderPane.alignment="CENTER">
         <content>
            <VBox fx:id="vBox" fillWidth="true" />
         </content>
      </ScrollPane>
   </center>
</BorderPane>
MatZir
  • 59
  • 1
  • 3
  • 8
  • Have you tried single stepping through your code? If so, what line does it crash on? – StillLearnin Sep 25 '15 at 02:42
  • @StillLearnin You don't need to step through the code to do that. You can just read it from the stack trace. However, the OP indicates that `scene.lookup("#vBox")` is returning `null`. – James_D Sep 25 '15 at 02:43

1 Answers1

5

First, you really shouldn't need to do this at all. Just inject the VBox into the controller, and then do whatever you need to do to it there:

public class Controller {

    @FXML
    private VBox vBox ;

    public void initialize() {
        // do something with vBox...
    }
}

To address your question directly:

Lookups will not work until CSS has been applied, which is typically on the first rendering pulse. This will not happen until the stage has been displayed, at the earliest.

If you move the lookup code to after you show the stage, it might work:

@Override
public void start(Stage stage) throws Exception{

    FXMLLoader loader = new FXMLLoader(getClass().getResource("MainWindow.fxml"));
    Parent root = loader.load();
    Scene scene = new Scene(root, 600, 575);


    stage.setScene(scene);
    stage.setTitle("Test");
    stage.setResizable(false);
    stage.show();

    System.out.println(scene.lookup("#vBox"));
}

Lookups in general are not very robust. As indicated above, you should really just access elements of the FXML in the controller. Just to give you one other option, you can get the namespace from the FXMLLoader, which is a Map<String, Object> containing all the elements of the FXML which have fx:ids (among other things).

So you can do:

    FXMLLoader loader = new FXMLLoader(getClass().getResource("MainWindow.fxml"));
    Parent root = loader.load();
    Map<String, Object> namespace = loader.getNamespace();
    System.out.println(namespace.get("vBox"));

As an aside, note that

    Controller controller = new Controller();
    controller.setScene(scene);

doesn't do anything. Creating a new controller is not the same as getting the controller that was created for you by the FXMLLoader. (It's redundant anyway, you can always do vBox.getScene() in the controller to get the scene.)

James_D
  • 201,275
  • 16
  • 291
  • 322