1

I would like to add a custom element into a VBox.

For example: being able to write VBox.getChildren().add(element) and element is a custom node created by me in FXML.

I already followed this tutorial: https://docs.oracle.com/javafx/2/fxml_get_started/custom_control.htm but the example only shows how to do this inside the same controller (a single controller, i already have my "big" controller, which is WinnerController, I would like to split the two classes, one that manages the single element, and one that manages the whole scene Winner.fxml).

I already have a class WinnerController which is the controller of my FXML Winner.fxml.

Here the code of my Winner.fxml:

<AnchorPane id="paneWinner" fx:id="paneWinner" prefHeight="800.0" prefWidth="1280.0" stylesheets="@winner.css" xmlns="http://javafx.com/javafx/9.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="client.gui.WinnerController">

<children>
  <VBox layoutX="434.0" layoutY="125.0" prefHeight="250.0" prefWidth="413.0" spacing="10.0">
     <children>
        <AnchorPane id="leaderBoardElement" prefHeight="80.0" prefWidth="413.0" stylesheets="@leaderBoard.css">
           <children>
              <Text layoutX="49.0" layoutY="45.0" strokeType="OUTSIDE" strokeWidth="0.0" text="Text" wrappingWidth="107.13671875" />
           </children></AnchorPane>
     </children>
  </VBox>

I would like to dynamically add element with the id "leaderBoardElement" (so an AnchorPane + Text) into my VBox.

How can i do that?

Edit: I also tried with this solution:How to understand and use `<fx:root>` , in JavaFX? but i keep getting nothing. When i do vbox.getChildren().add(new MyComponent()); called in my WinnerControlleri get nothing.

WinnerController class:

public class WinnerController implements Initializable {

@FXML
private VBox leaderBoard;

@FXML
public void initialize(URL location, ResourceBundle resources)  {
    System.out.println("Winner Init");

    MyComponent test = new MyComponent();
    System.out.println(test);


    // leaderBoard.getChildren().add(new MyComponent());
}



}

My Component class:

public class MyComponent extends AnchorPane {
@FXML
private TextField textField ;
@FXML
private Button button ;
public MyComponent() {
    try {
        FXMLLoader loader = new FXMLLoader(getClass().getResource("MyComponent.fxml"));
        loader.setController(this);
        loader.setRoot(this);
        loader.load();
        textField.setText("HELLO!");
    } catch (IOException exc) {
        // handle exception
        System.out.println("ELEMENT NOT CREATE!!!");

    }
  }
}

Winner.fxml:

<AnchorPane id="paneWinner" fx:id="paneWinner" prefHeight="800.0" 
    prefWidth="1280.0" stylesheets="@winner.css" xmlns="http://javafx.com/javafx/9.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="client.gui.WinnerController">
     <children>
  <VBox fx:id="leaderBoard" layoutX="434.0" layoutY="125.0" prefHeight="250.0" prefWidth="413.0" spacing="10.0" />

MyComponent.fxml:

<fx:root type="javafx.scene.layout.AnchorPane" fx:id="leaderBoardElement" id="leaderBoardElement">
<TextField fx:id="textField" />
<Button fx:id="button" />

And i call the creation and loading of Winner.fxml from another class like this:

 FXMLLoader loader = new FXMLLoader(getClass().getResource("/Winner.fxml"));
                if (loader!=null)
                    System.out.println("LOADER NOT NULL!!");

                try{
                    System.out.println("TRY!");

                    Parent root = (Parent) loader.load();
                    if(root!=null)
                        System.out.println("ROOT NOT NULL!!");

                    Scene startedGame = new Scene(root, 1280, 800, Color.WHITE);
                    if(startedGame!=null)
                        System.out.println("SCENE NOT  NULL!");

                    Stage window = (Stage) paneCarta0.getScene().getWindow();
                    if (window!=null)
                        System.out.println("WINDOW NOT NULL!!");

                    window.setScene(startedGame);
                    window.show();
                    catch (IOException Exception) {
                        System.out.println("View not found. Error while loading");

                }

The problem is inside the new MyComponent(), where probably i get an exception and it propagates to my main caller. I've tried everything but i can't figure out why it can't create the MyComponent object.

Mattia Surricchio
  • 1,362
  • 2
  • 21
  • 49
  • 1
    Is it your `leaderBoardElement` that you want controlled separately? – Zephyr Jun 16 '18 at 14:31
  • Possible duplicate of [JavaFx Nested Controllers (FXML )](https://stackoverflow.com/questions/12543487/javafx-nested-controllers-fxml-include) – Zephyr Jun 16 '18 at 14:35
  • @Zephyr Yes, i would like to add that element into my VBox when i need it. Like with javascript and append, where you can add elements into your HTML via JS. I would like to do the same thing, but i don't know how to do it in JavaFX – Mattia Surricchio Jun 16 '18 at 14:39
  • Check out this answer: https://stackoverflow.com/questions/12543487/javafx-nested-controllers-fxml-include#12572450, or the actual Oracle tutorial on nested controllers: https://docs.oracle.com/javafx/2/api/javafx/fxml/doc-files/introduction_to_fxml.html#nested_controllers – Zephyr Jun 16 '18 at 14:40
  • @Zephyr i checked the answers but they don't explain how to add dynamically the custom nodes into my "bigger" FXML – Mattia Surricchio Jun 16 '18 at 14:50
  • The Oracle document shows how to do exactly that. Read the "Custom Components" section... – Zephyr Jun 16 '18 at 14:54
  • I don't get why the controller has to extend the VBox class. If i use a Anchor pane as wrapper for my content, do i have to make my controller extend the Anchor Pane class? – Mattia Surricchio Jun 16 '18 at 15:05
  • 1
    It doesn't. It can extend any container you wish; the VBox was just for that sample. – Zephyr Jun 16 '18 at 15:05

1 Answers1

2

If your custom component is in a separate FXML file you can do this.

In your WinnerController class:

@FXML
private void initialize() throws IOException {
    FXMLLoader fxmlLoader = new FXMLLoader(getClass().getRessource("customElement.fxml"));
    fxmlLoader.setController(new CustomElementController()); //Or just specify the Controller in the FXML file
    myVBox.getChildren().add(fxmlLoader.load());
}

Edit: In this solution there should be NO <fx:include> tags in your main fxml

staad
  • 796
  • 8
  • 20
  • I'm doing as you suggested, but i have some problems with the CustomElementController ( or mine ). Following this tutorial https://docs.oracle.com/javafx/2/api/javafx/fxml/doc-files/introduction_to_fxml.html#nested_controllers i added the loader of custom_control (changing the name with my fxml) in the costructor. But i get some problems, i think i am kind of assigning multiple times the controller to the fxml, how do i solve that? – Mattia Surricchio Jun 16 '18 at 16:39
  • Not sure if my edit (remove ``) fixed your issue. Another solution might be to define the controller in the fxml (`fx:controller="com.foo.CustomElementController"`) instead of using `setController()`. – staad Jun 17 '18 at 20:01