Reputation: 65811
I have a custom control defined like this:
<fx:root type="javafx.scene.Group" xmlns:fx="http://javafx.com/fxml" fx:controller="com.theCorp.theTool.controllers.LimitsController">
<Group id="Group" layoutX="0.0" layoutY="0.0">
<children>
<Group id="Count" layoutX="0.0" layoutY="20.0">
<children>
<Label layoutX="0.0" layoutY="3.0" prefWidth="59.0" text="Count" />
<TextField id="Count.Min" fx:id="countMin" layoutX="59.0" layoutY="0.0" prefWidth="132.0" promptText="Min" />
<TextField id="Count.Max" fx:id="countMax" layoutX="191.0" layoutY="0.0" prefWidth="132.0" promptText="Max" />
</children>
</Group>
<Button id="SaveButton" fx:id="saveButton" layoutX="12.0" layoutY="85.0" mnemonicParsing="false" onAction="#save" text="Save" />
</children>
</Group>
</fx:root>
I have a Control
class that loads this in the usual way:
public class LimitsControl extends Group {
private static final String fxmlFileName = "Limits.fxml";
public LimitsControl() {
super();
URL fxml = getClass().getResource(fxmlFileName);
if (fxml == null) {
fxml = getClass().getResource("/fxml/" + fxmlFileName);
}
// Create the loader.
FXMLLoader loader = new FXMLLoader(fxml);
loader.setRoot(this);
try {
// Load the fxml.
loader.load();
// Pull back the controller.
LimitsController controller = loader.getController();
// Tell it about me.
controller.setControl(this);
} catch (IOException exception) {
throw new RuntimeException(exception);
}
}
}
I have a Controller
class that looks a bit like this:
public class LimitsController implements Initializable {
// Control.
private LimitsControl control;
@FXML
private TextField countMin;
@FXML
private TextField countMax;
@FXML
private Button saveButton;
/**
* Initializes the controller class.
*
* @param url
* @param rb
*/
@Override
public void initialize(URL url, ResourceBundle rb) {
log.info("Loading: " + url + " rb=" + rb+" control="+control);
// Make the save button call my save.
saveButton.setOnAction((ActionEvent e) -> {
save(e);
});
}
public void setControl(LimitsControl control) {
log.info("Control="+control);
this.control = control;
}
@FXML
private void save(ActionEvent event) {
// What is the ID of me?
Parent myGroup = saveButton.getParent();
// Pull the id of the control that enclosed my group.
String myId = myGroup.getParent().getId();
log.info("Saving: " + myId);
}
}
And I insert these controls in my scene in several places, each time with a different id
like this:
<TitledPane fx:id="Today" animated="false" text="Today">
<tooltip>
<Tooltip text="Parameters will only be used for today." />
</tooltip>
<content>
<LimitsControl id="Today.Limits" />
</content>
</TitledPane>
My problem is that these controls must be populated from data that requires the id
of the control but when the controller is initialized it does not have a parent.
The id
is available when the save button is pressed - see the save
method climbing up the parent tree to gather the id of the control. Sadly the parent is null at initialize time.
How can I correctly initialize the fields at a time when the parent id is available? Or am I doing it wrong and there is a right
way?
Upvotes: 0
Views: 832
Reputation: 209340
First, inject the control instead of coupling everything with set methods:
<fx:root type="javafx.scene.Group" xmlns:fx="http://javafx.com/fxml" fx:controller="com.theCorp.theTool.controllers.LimitsController" fx:id="control">
and
// Control.
@FXML
private LimitsControl control;
If I understand correctly, the id you are trying to get is the id of the LimitsControl itself. To make this available "early", define a parameter in the constructor of LimitsControl and set the id there:
public LimitsControl(String id) {
super();
setId(id);
// code as before...
}
Now, because you no longer have a default constructor, you need to make a Builder for LimitsControl:
public class LimitsControlBuilder {
private String id ;
private LimitsControlBuilder() {
this.id = "" ;
}
public static LimitsControlBuilder create() {
return new LimitsControlBuilder();
}
public LimitsControlBuilder id(String id) {
this.id = id ;
return this ;
}
public LimitsControl build() {
return new LimitsControl(id);
}
}
Now you can do
<LimitsControl id="Today.Limits" />
and it will be set as soon as the constructor is invoked.
You can of course create your own properties here instead of using the id, which might be a more appropriate way for the fxml to pass information to the custom control instance than using the css id.
Upvotes: 2