Accessing FXML controller class
Asked Answered
J

4

80

I would like to communicate with a FXML controller class at any time, to update information on the screen from the main application or other stages.

Is this possible? I havent found any way to do it.

Static functions could be a way, but they don't have access to the form's controls.

Any ideas?

Jink answered 25/5, 2012 at 8:47 Comment(0)
B
118

You can get the controller from the FXMLLoader

FXMLLoader fxmlLoader = new FXMLLoader();
Pane p = fxmlLoader.load(getClass().getResource("foo.fxml").openStream());
FooController fooController = (FooController) fxmlLoader.getController();

store it in your main stage and provide getFooController() getter method.
From other classes or stages, every time when you need to refresh the loaded "foo.fxml" page, ask it from its controller:

getFooController().updatePage(strData);

updatePage() can be something like:

// ...
@FXML private Label lblData;
// ...
public void updatePage(String data){
    lblData.setText(data);
}
// ...

in the FooController class.
This way other page users do not bother about page's internal structure like what and where Label lblData is.

Also look the https://mcmap.net/q/260828/-how-to-pass-object-created-in-fxml-controller1-to-controller2-of-inner-fxml-control. In JavaFX 2.2 FXMLLoader is improved.

Bellerophon answered 25/5, 2012 at 11:1 Comment(16)
I have found that using static functions is easier and works better than this solution (at least for me). The key to be able to access the controls is to have a public static instance of the class that has access to all controls and public methods.Jink
I know this is an old question... but can anyone provide more detail on the answer w.r.t. where the first 3 lines of code and where the getFooController() goes?Morel
@adeena, FooController is a controller class defined by you in "foo.fxml", and there is no getFooController() in the code.Bellerophon
@UlukBiy Hmm... maybe I'm trying to do something different than the original poster. I have an FXML application and a TextArea on the primary stage. I figured out how to access it from other FXML controllers, but not from any other type of class within my package/project.Morel
Hey guys, I also know this is old but can you explain this part: "store it in your main stage "Complicacy
@TannerSummers, once you got the controller instance, you need to put it to some common place (think of it like a repo), so other classes (modules) of your app can access to it. By "main stage" I meant the class that has an entry point of your JavaFX app, namely the one which has start(Stage stage) method. In that main class you may prefer to store the controller instances in some collection. Or create a new ControllerManager class that acts like a repo for storing and access to them. The design is up to you.Bellerophon
hmm ok, I need to read it a few times and try so omething. I been googling and all I am really trying to do is, when I have other classes doing this and that, i need those classes to refer to the fxml and modify it, then reload the scene/stage to show the changes. but nothing I find works out for me.....Complicacy
When I try this, the controller instance returned is null even when I place it well after everything should have been initialized. I don't want to create another, similar question but I can't really do SSCSE here either. Anyone else experience this?Disgust
@MaskedCoder check out #23461648 or #10240971 and or #14188463 . If you still getting the null instance, then it is better to see the code, which requires posting new question.Bellerophon
thanks @UlukBiy the third link helped - rather than explore further, I just setController before calling load() which seems to have worked.Disgust
its giving me a NullPointerException can anyone explain why ?Gelation
@Pappu, what is giving NPE? Feel free to start new Q&A entry.Bellerophon
actually im blocked from asking any new Q&AGelation
@Pappu, sorry for that, but what is the reason for being blocked? What does the site say? Try to contact moderators to fix it.Bellerophon
From where i can contact to the moderators ?Gelation
this does not work for me but it if you change it a bit like @Hedelman answer it will workNeoplasm
H
31

Just to help clarify the accepted answer and maybe save a bit of time for others that are new to JavaFX:

For a JavaFX FXML Application, NetBeans will auto-generate your start method in the main class as follows:

@Override
public void start(Stage stage) throws Exception {
    Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));

    Scene scene = new Scene(root);

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

Now, all we need to do to have access to the controller class is to change the FXMLLoader load() method from the static implementation to an instantiated implementation and then we can use the instance's method to get the controller, like this:

//Static global variable for the controller (where MyController is the name of your controller class
static MyController myControllerHandle;

@Override
public void start(Stage stage) throws Exception {
    //Set up instance instead of using static load() method
    FXMLLoader loader = new FXMLLoader(getClass().getResource("FXMLDocument.fxml"));
    Parent root = loader.load();

    //Now we have access to getController() through the instance... don't forget the type cast
    myControllerHandle = (MyController)loader.getController();

    Scene scene = new Scene(root);

    stage.setScene(scene);
    stage.show();
}
Hedelman answered 6/2, 2017 at 16:45 Comment(0)
E
9

Another solution is to set the controller from your controller class, like so...

public class Controller implements javafx.fxml.Initializable {

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        // Implementing the Initializable interface means that this method
        // will be called when the controller instance is created
        App.setController(this);
    }

}

This is the solution I prefer to use since the code is somewhat messy to create a fully functional FXMLLoader instance which properly handles local resources etc

@Override
public void start(Stage stage) throws Exception {
    Parent root = FXMLLoader.load(getClass().getResource("/sample.fxml"));
}

versus

@Override
public void start(Stage stage) throws Exception {
    URL location = getClass().getResource("/sample.fxml");
    FXMLLoader loader = createFXMLLoader(location);
    Parent root = loader.load(location.openStream());
}

public FXMLLoader createFXMLLoader(URL location) {
    return new FXMLLoader(location, null, new JavaFXBuilderFactory(), null, Charset.forName(FXMLLoader.DEFAULT_CHARSET_NAME));
}
Escallop answered 18/7, 2015 at 15:25 Comment(3)
This solution works for previous versions of JavaFX as well (<2.2).Quell
On balance I think the chosen answer has to be preferred, ingenious though this is: firstly, you can have multiple controllers, so you'd really want to have a static map in your Application class, key controller --> value loader (NB you can always get the controller from the loader). Secondly, we've been given a getController() method, so it makes sense to use it. Thirdly, static methods should be avoided when an instance operation is really involved, and probably as a general coding preference.Bernat
Not a good idea to store the controller in a static field.Darciedarcy
J
3

On the object's loading from the Main screen, one way to pass data that I have found and works is to use lookup and then set the data inside an invisible label that I can retrieve later from the controller class. Like this:

Parent root = FXMLLoader.load(me.getClass().getResource("Form.fxml"));
Label lblData = (Label) root.lookup("#lblData");
if (lblData!=null) lblData.setText(strData); 

This works, but there must be a better way.

Jink answered 25/5, 2012 at 8:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.