Using FXML, can I only put a child.fxml
into the parent.fxml
, and then fill out the values from the parent? For example:
child.fxml
:
<HBox xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
spacing="10" alignment="CENTER"
>
<padding>
<Insets top="10" right="25" bottom="10" left="10"/>
</padding>
<Text text="$text" textAlignment="CENTER" lineSpacing="2"/>
<TextField text="0" fx:id="number" />
</HBox>
parent.fxml
<VBox xmlns:fx="http://javafx.com/fxml"
fx:controller="my.package.MainController"
spacing="20">
<Label text="Main"/>
<fx:include fx:id="subComponent" source="child.fxml"/>
</VBox>
Here, if you look into the child.fxml
, there is a Text
field with a text
attribute, I want to fill this dynamically, i.e, something like this
<fx:include fx:id="subComponent" source="child.fxml" text="mytext"/>
And then the child component should be rendered with myText
as its text. Is it possible? If yes, how?
NOTE <fx:include>
is not what I want
As far as I am aware, there is no way to directly pass parameters to included FXML files using the <fx:include>
element. You can create a controller for the included FXML, and pass the data from the parent controller to the child controller.
A more convenient approach that is closer to what you're trying to achieve in this code is instead to use the FXML Custom Component pattern.
In this pattern, you essentially create a wrapper class for the FXML file, specifying it both as the controller and as a dynamic root for the FXML:
child.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.text.Text?>
<fx:root type="HBox" xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
spacing="10" alignment="CENTER"
>
<padding>
<Insets top="10" right="25" bottom="10" left="10"/>
</padding>
<Text fx:id="textComponent" textAlignment="CENTER" lineSpacing="2"/>
<TextField text="0" fx:id="number" />
</fx:root>
Child.java:
package org.jamesd.examples.customchild;
import javafx.beans.property.StringProperty;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.layout.HBox;
import javafx.scene.text.Text;
import java.io.IOException;
public class Child extends HBox {
@FXML
private Text textComponent;
public Child() throws IOException {
FXMLLoader loader = new FXMLLoader(getClass().getResource("child.fxml"));
loader.setController(this);
loader.setRoot(this);
loader.load();
}
public StringProperty textProperty() {
return textComponent.textProperty();
}
public final String getText() {
return textProperty().get();
}
public final void setText(String text) {
textProperty().set(text);
}
}
Note we can create properties in the wrapper class, as it is just regular Java. In this case we just expose the existing text property of the Text
UI component as a JavaFX property.
Now we can refer to the custom component just like any other UI element:
parent.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.VBox?>
<?import org.jamesd.examples.customchild.Child?>
<VBox xmlns:fx="http://javafx.com/fxml"
fx:controller="org.jamesd.examples.customchild.MainController"
spacing="20">
<Label text="Main"/>
<Child fx:id="subComponent" text="Hello World"/>
</VBox>
For completeness, here is a (trivial) controller and application class:
MainController.java:
package org.jamesd.examples.customchild;
public class MainController {
}
and
HelloApplication.java
package org.jamesd.examples.customchild;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.io.IOException;
public class HelloApplication extends Application {
@Override
public void start(Stage stage) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("parent.fxml"));
Scene scene = new Scene(fxmlLoader.load(), 320, 240);
stage.setTitle("Hello!");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}