75

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?

betaman
  • 1,775
  • 4
  • 23
  • 29

4 Answers4

113

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://stackoverflow.com/a/10718683/682495. In JavaFX 2.2 FXMLLoader is improved.

Community
  • 1
  • 1
Uluk Biy
  • 46,036
  • 11
  • 134
  • 150
  • 2
    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. – betaman Jun 08 '12 at 16:28
  • 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? – adeena Mar 22 '14 at 17:44
  • @adeena, FooController is a controller class defined by you in "foo.fxml", and there is no getFooController() in the code. – Uluk Biy Mar 22 '14 at 18:02
  • @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. – adeena Mar 22 '14 at 19:08
  • Hey guys, I also know this is old but can you explain this part: "store it in your main stage " – Tanner Summers Jul 22 '15 at 11:00
  • 1
    @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. – Uluk Biy Jul 22 '15 at 11:22
  • 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..... – Tanner Summers Jul 22 '15 at 11:32
  • 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? – Masked Coder Sep 09 '15 at 14:18
  • @MaskedCoder check out http://stackoverflow.com/questions/23461148/fxmlloader-getcontroller-returns-null or http://stackoverflow.com/questions/10240471/how-can-i-access-a-controller-class-in-javafx-2-0 and or http://stackoverflow.com/questions/14187963/passing-parameters-javafx-fxml . If you still getting the null instance, then it is better to see the code, which requires posting new question. – Uluk Biy Sep 09 '15 at 14:36
  • thanks @UlukBiy the third link helped - rather than explore further, I just `setController` before calling `load()` which seems to have worked. – Masked Coder Sep 09 '15 at 15:03
  • its giving me a NullPointerException can anyone explain why ? – Arpan Oct 28 '15 at 12:26
  • @Pappu, what is giving NPE? Feel free to start new Q&A entry. – Uluk Biy Oct 28 '15 at 13:09
  • actually im blocked from asking any new Q&A – Arpan Oct 29 '15 at 05:09
  • @Pappu, sorry for that, but what is the reason for being blocked? What does the site say? Try to contact moderators to fix it. – Uluk Biy Oct 29 '15 at 05:46
  • From where i can contact to the moderators ? – Arpan Oct 29 '15 at 06:57
  • this does not work for me but it if you change it a bit like @DatuPuti answer it will work – Hosseinmp76 Mar 21 '20 at 16:45
27

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();
}
DatuPuti
  • 591
  • 6
  • 16
8

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));
}
Caleb
  • 1,316
  • 2
  • 10
  • 10
  • This solution works for previous versions of JavaFX as well (<2.2). – Neil Sep 19 '17 at 09:27
  • 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. – mike rodent Mar 17 '20 at 13:00
  • Not a good idea to store the controller in a static field. – Lii Sep 03 '20 at 14:09
2

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.

betaman
  • 1,775
  • 4
  • 23
  • 29