How do I Change FXML Controller Variable Values in JavaFX before Showing Window?


Question

I want to change the labels and text in a popup FXML window before it is shown. This is being done because all of the Help items will be very similar and I wanted to make a template FXML and then change the values of the labels and text area accordingly to reduce coding.

I'm surprised I haven't come across a template method in JavaFX though. If there's a better way to go about this, please do suggest it.

For the task at hand, I have the following:

@FXML
private void documentationAction(ActionEvent action) throws IOException {
    Stage dialogue = new Stage();
    Parent root = null;
    FXMLLoader loader = new FXMLLoader();
    root = loader.load(getClass().getResource("dialogues/dialogue.fxml"));
    Scene scene = new Scene(root);
    /*
     * change text values in title (Label) and text (TextArea)
     * and change the behavior of the findMore button
     */
    dialogue.setTitle("Documentation");
    dialogue.setScene(scene);
    dialogue.show();
}

I would like to be able to change variables inside the FXML file, for which I've made getters and setters in the controller class, but I can't figure how to do this.

Is there a way? It seems like it would be necessary for changing information between windows in multi-windowed applications, so I'm sure there's something simple that I'm missing.

UPDATE Code for controller:

public class DialogueController implements Initializable {

    @FXML
    private Button more;
    @FXML
    private Button close;
    @FXML
    private TextArea text;
    @FXML
    private Label title;

    public TextArea getText() {
        return text;
    }

    public void setText(String text) {
        this.text.setText(text);
    }

    public Label getTitle() {
        return title;
    }

    public void setTitle(String text) {
        this.title.setText(text);
    }

    @FXML
    public void closeAction(ActionEvent action) {
        // get a handle to the stage
        Stage stage = (Stage) close.getScene().getWindow();
        // do what you have to do
        stage.close();
    }

    @FXML
    public void moreAction(ActionEvent action) {
    }

    @FXML
    public void makeDocumentation(ActionEvent action) {
        this.title.setText("Documentation");
    }

    /**
     * Initializes the controller class.
     */
    @Override
    public void initialize(URL url, ResourceBundle rb) {
    }
}

Full Dialogue FXML

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

<?import java.lang.*?>
<?import java.net.*?>
<?import java.util.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<AnchorPane id="AnchorPane" padding="$x1" prefHeight="500.0" prefWidth="400.0" styleClass="mainFxmlClass" xmlns:fx="http://javafx.com/fxml" fx:controller="pdbpro.dialogues.DialogueController">
  <children>
    <GridPane alignment="CENTER" prefHeight="400.0" prefWidth="600.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
      <children>
        <Label fx:id="title" alignment="CENTER_LEFT" prefWidth="-1.0" styleClass="title" text="Title" underline="false" visible="true" GridPane.columnIndex="0" GridPane.rowIndex="0">
          <GridPane.margin>
            <Insets fx:id="x1" />
          </GridPane.margin>
        </Label>
        <TextArea fx:id="text" editable="false" focusTraversable="false" prefWidth="200.0" wrapText="true" GridPane.columnIndex="0" GridPane.rowIndex="1">
          <GridPane.margin>
            <Insets bottom="24.0" left="15.0" right="15.0" />
          </GridPane.margin>
        </TextArea>
        <ToolBar GridPane.columnIndex="0" GridPane.rowIndex="2">
          <items>
            <Button fx:id="more" mnemonicParsing="false" text="Find more" />
            <Button fx:id="close" mnemonicParsing="false" onAction="#closeAction" text="Close" />
          </items>
          <GridPane.margin>
            <Insets bottom="14.0" />
          </GridPane.margin>
        </ToolBar>
      </children>
      <columnConstraints>
        <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
      </columnConstraints>
      <padding>
        <Insets fx:id="x1" />
      </padding>
      <rowConstraints>
        <RowConstraints maxHeight="60.0" minHeight="60.0" prefHeight="60.0" valignment="CENTER" vgrow="NEVER" />
        <RowConstraints maxHeight="1.7976931348623157E308" minHeight="50.0" prefHeight="0.0" vgrow="ALWAYS" />
        <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
      </rowConstraints>
    </GridPane>
  </children>
  <stylesheets>
    <URL value="@dialogue.css" />
  </stylesheets>
</AnchorPane>

Full stack trace of error at these lines

FXMLLoader loader = new FXMLLoader();
Parent root = loader.load(getClass().getResource("dialogues/dialogue.fxml"));
DialogueController dc = loader.getController();
dc.makeDocumentation(); //ERROR
Scene scene = new Scene(root);


java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
    at javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(FXMLLoader.java:1440)
    at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:69)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:217)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:170)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:37)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:92)
    at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:53)
    at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:28)
    at javafx.event.Event.fireEvent(Event.java:171)
    at javafx.scene.control.MenuItem.fire(MenuItem.java:456)
    at com.sun.javafx.scene.control.skin.ContextMenuContent$MenuItemContainer.doSelect(ContextMenuContent.java:1188)
    at com.sun.javafx.scene.control.skin.ContextMenuContent$MenuItemContainer$6.handle(ContextMenuContent.java:1139)
    at com.sun.javafx.scene.control.skin.ContextMenuContent$MenuItemContainer$6.handle(ContextMenuContent.java:1137)
    at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:69)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:217)
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:170)
    at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:38)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:37)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:92)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:35)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:92)
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:35)
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:92)
    at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:53)
    at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:33)
    at javafx.event.Event.fireEvent(Event.java:171)
    at javafx.scene.Scene$MouseHandler.process(Scene.java:3324)
    at javafx.scene.Scene$MouseHandler.process(Scene.java:3164)
    at javafx.scene.Scene$MouseHandler.access$1900(Scene.java:3119)
    at javafx.scene.Scene.impl_processMouseEvent(Scene.java:1559)
    at javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2261)
    at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:228)
    at com.sun.glass.ui.View.handleMouseEvent(View.java:528)
    at com.sun.glass.ui.View.notifyMouse(View.java:922)
    at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at com.sun.glass.ui.win.WinApplication.access$100(WinApplication.java:29)
    at com.sun.glass.ui.win.WinApplication$3$1.run(WinApplication.java:73)
    at java.lang.Thread.run(Thread.java:722)
Caused by: java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(FXMLLoader.java:1435)
    ... 37 more
Caused by: java.lang.NullPointerException
    at pdbpro.MainController.documentationAction(MainController.java:82)
    ... 42 more
1
5
4/8/2013 6:11:53 PM

One way I can see is to make your controller implement initializable and implement the initialize method. The initialize method will be called automatically when the controller is created by the fxml loader. When the inititialize method is called all the fxml object declared should be initialized and ready to be customized.

[edit] After reading the docs for initializable, it says that if a method called initialize with no args is created that it will be called automatically with the fxml loader. So from what I understand, you don't need to implement initializable. Just create an initialize method with no args.

If needed, further info may be found in an introduction to fxml.

4
12/24/2012 9:06:32 AM

Licensed under: CC-BY-SA with attribution
Not affiliated with: Stack Overflow
Icon