Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I access a UI element from another controller class in JavaFX?

I have a JavaFX / Java 8 application written with NetBeans 8 (no SceneBuilder).

My application has a main window that has its own FXML file (primary.fxml) and its own controller class (FXMLPrimaryController.java). One of the items in the FXML is a TextArea. Some of the methods in FXMLPrimaryController.java are about appending to that TextArea.

This application now spawns a second window (another "stage") with its own FXML (second.fxml) and its own controller class (FXMLsecondController.java).

Within the second controller class, how can I access the TextArea in the primary?

Here's a sample of the relevant code:

primary.fxml:

<Button text="press me!" onAction="#openSecondWindow" />
<TextArea fx:id="myArea" />

FXMLPrimaryController.java:

public class FXMLPrimaryController implements Initializable {

    @Override
    public void initialize(URL url, ResourceBundle rb) {
    }

    @FXML private TextArea myArea;

    final public void writeToTextArea() {
        myArea.appendText("hi!");
    }

    @FXML
    private void openSecondWindow(ActionEvent event) throws Exception {

        Group root = new Group();
        Stage stage = new Stage();

        AnchorPane frame = FXMLLoader.load(getClass().getResource("second.fxml"));
        root.getChildren().add(frame);
        Scene scene = new Scene(root);

        stage.setScene(scene);
        stage.show();
    }

}

There is nothing fancy about second.fxml. Assume there is a button with onAction="#writeSomething".

In FXMLsecondController.java, I would like a function that references the above TextArea.

like image 953
adeena Avatar asked Feb 24 '14 17:02

adeena


People also ask

Which interface should be used with controller class of JavaFX application?

JavaFX controller works based on MVC(Model-View-Controller) JavaFX MVC can be achieved by FXML (EFF-ects eXtended Markup Language). FXML is an XML based language used to develop the graphical user interfaces for JavaFX applications as in the HTML.

How do controllers work in JavaFX using FXML?

An application's View should be linked with its Controller inside the start() method of the JavaFX application. Links should be created by storing a reference to the Java controller inside the FXML file. This is then automatically linked when the FXMLLoader creates the View.

What is FXMLLoader in JavaFX?

public class FXMLLoader extends Object. Loads an object hierarchy from an XML document. Since: JavaFX 2.0.

What is initialize in JavaFX?

initialize. void initialize(URL location, ResourceBundle resources) Called to initialize a controller after its root element has been completely processed. Parameters: location - The location used to resolve relative paths for the root object, or null if the location is not known.


1 Answers

When you load the FXML for the secondary controller, do this:

FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.setLocation(getClass().getResource("second.fxml"));
AnchorPane frame = fxmlLoader.load();
FXMLSecondaryController c = (FXMLSecondaryController) fxmlLoader.getController();

Then you can pass references to the second controller. This could just be the TextArea.

EDIT:

Replaced the load() call in the snippet above and added the setLocation() call. The old line AnchorPane frame = fxmlLoader.load(getClass().getResource("second.fxml")); was wrong as it called the static load function, which is useless here.

EDIT:

(Changed the code snippet above to better match your variable names.) The code snippet above replaces this part of your code:

AnchorPane frame = FXMLLoader.load(getClass().getResource("second.fxml"));

That line uses the FXMLLoader to load the view and also creates the instance of the controller - in this case FXMLSecondaryController. However, you cannot use the static FXMLLoader.load method for that, you need an instance of FXMLLoader. That instance holds a reference to the controller after load and you can retrieve that with getController(). And you need to cast the returned value to your controller class (with (FXMLSecondaryController)).

Your primary controller has a field:

@FXML private TextArea myArea;

This holds a reference to your TextArea and is initialized by the fxml loader. (If you remove the @FXML annotation, the loader won't touch it.)

In your secondary controller, add this field:

public TextArea primaryTextArea;

Note that @FXML is gone (the fxml loader shall not touch the field) as well as private (you want to set the field, hence others must see it). Now you can set this field after the controller has been loaded. So back to the loading code:

FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.setLocation(getClass().getResource("second.fxml"));
AnchorPane frame = fxmlLoader.load();
FXMLSecondaryController c = (FXMLSecondaryController) fxmlLoader.getController();
// Add this too:
c.primaryTextArea = myArea;

EDIT: Replaced the load() call in the snippet above and added the setLocation() call. The old line AnchorPane frame = fxmlLoader.load(getClass().getResource("second.fxml")); was wrong as it called the static load function, which is useless here.

The FXMLSecondaryController now has a reference to the TextArea of the primary controller. In one of its methods you should be able to access its content:

public class FXMLSecondaryController implements Initializable {
    // ...

    public TextArea primaryTextArea;

    // ...

    @FXML
    private void doSomething(ActionEvent event) throws Exception {
        primaryTextArea.appendText("Hi ho!");
    }

}

Note that this solution is not the best approach, but it is simple and can serve as a start. Using binding is certainly recommended. I would try to create a class which holds the data and on which the other controllers bind. But if you take the simple approach for now, it should be sufficient.

like image 116
Rainer Schwarze Avatar answered Sep 18 '22 11:09

Rainer Schwarze