Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiple FXML with Controllers, share object

Goodevening everyone,

I have found a bunch of posts already on this topic but I still can not manage to pass an object from Controller1 to Controller2. Is there somewhere a full tutorial or some example project that does this?

I've gotten this far until I got stuck:

Country class

public class Country { private SimpleStringProperty country = new SimpleStringProperty("");  //Constructor public Country() { }  //GETTERS public String getCountry() {     return country.get(); }  //SETTERS public void setCountry(String value) {     country.set(value); }  @Override public String toString() {     return getCountry(); } } 

When the program starts, the main FXML gets loaded (Sample.fxml). This contains a border pane with a menu bar in the top panel and a content pane in the center. On initialize I create a new Country object and store it in a global variable. I have a method that loads another FXML into the content pane when a menu item is clicked:

SampleController.java

public class SampleController implements Initializable {  @FXML private Pane pContent;  private Country c;  @FXML private void handleButtonAction(ActionEvent event) throws IOException {     System.out.println(c); //this prints Belgium, which is correct      URL url = getClass().getResource("Sub1.fxml");      FXMLLoader fxmlloader = new FXMLLoader();     fxmlloader.setLocation(url);     fxmlloader.setBuilderFactory(new JavaFXBuilderFactory());      pContent.getChildren().clear();     pContent.getChildren().add((Node) fxmlloader.load(url.openStream())); }  @Override public void initialize(URL url, ResourceBundle rb) {     c = new Country();     c.setCountry("Belgium"); }  public Country getCountryFromSampleController(){     return c; } } 

Now I wish to capture the Country object when the Sub1.fxml gets loaded, which means I need to fetch the country object on initialize():

Sub1Controller.java

public class Sub1Controller implements Initializable {  /**  * Initializes the controller class.  */ @Override public void initialize(URL url, ResourceBundle rb) {     SampleController sp = new SampleController(); //I don't know how to fetch the original SampleController object     System.out.println(sp.getCountryFromSampleController());      //this prints null, which is ofcourse logical because I make a new SampleController object.          }     } 

The question that I have, how can I get the 'original' SampleController object so I can use the getCountryFromRoot() method to fetch the Country object with value Belgium? I've been searching on this issue for hours and hours and have read every post on StackOverflow about this, but it seems I do not find the missing link... any help (preferably with this code) is appreciated!

Sorry for the long post, I tried to be as complete as possible else I'll never understand...

like image 250
Perneel Avatar asked Aug 28 '12 20:08

Perneel


People also ask

Can two FXML files have the same controller?

It's possible to use the same controller class for multiple FXML files, but your code will be really hard to follow, and it's a very bad idea. (Also note that using the fx:controller attribute, you will have a different controller instance each time you call FXMLLoader.

How do I add FXML to another FXML?

The <fx:include> tag can be used to include one fxml file into another. The controller of the included fxml can be injected into the controller of the including file just as any other object created by the FXMLLoader . This is done by adding the fx:id attribute to the <fx:include> element.

How do I join two FXML files?

The classic way to instanciate components via FXMLLoader with nested controllers is: FXML first, then controller for each part. With this technique this is: controller first, then FXML for each component. And you won't load FXML in FXML directly, you will import your custom java classes in the FXML.

How do controllers work in JavaFX using FXML?

Specifying Controller Class in FXML Notice the fx:controller attribute in the root element (the VBox element). This attribute contains the name of the controller class. An instance of this class is created when the FXML file is loaded. For this to work, the controller class must have a no-argument constructor.


1 Answers

FXML is a simple form of MVC pattern. FXML file is a view, Controller is obvious, what's missed? The model -- a place where you store all data relative to your current view and, thus, which you can use to share Country data between controllers.


1. One of the possible approach to introduce model is "context". Let's consider a case, then you have only one model for the whole project so you can have a global context in a form of Singleton

public class Context {     private final static Context instance = new Context();      public static Context getInstance() {         return instance;     }      private Country country = new Country();      public Country currentCountry() {         return country;     } } 

Your SampleController will have next changes:

@Override public void initialize(URL url, ResourceBundle rb) {     Context.getInstance().currentCountry().setCountry("Belgium"); } 

And SubController1 can access it the same way:

@Override public void initialize(URL url, ResourceBundle rb) {     System.out.println(Context.getInstance().currentCountry().getCountry()); } 

2. Another way is to pass context to SubController1 then you load it's xml. It will work better if you don't want to have application global model. So create similar Context class but without instance fields, and:

public class Sub1Controller implements Initializable {     private Context context;     public void setContext(Context context) {         this.context = context;         // initialize country dependent data here rather then in initialize()     } } 

Setting context in SampleController:

Context currentContext = new Context();  @Override public void initialize(URL url, ResourceBundle rb) {     currentContext.currentCountry().setCountry("Belgium"); }  @FXML private void handleButtonAction(ActionEvent event) throws IOException {     URL url = getClass().getResource("Sub1.fxml");      FXMLLoader fxmlloader = new FXMLLoader();     fxmlloader.setLocation(url);     fxmlloader.setBuilderFactory(new JavaFXBuilderFactory());      pContent.getChildren().clear();     pContent.getChildren().add((Node) fxmlloader.load(url.openStream()));             // here we go     ((Sub1Controller)fxmlloader.getController()).setContext(currentContext); } 
like image 107
Sergey Grinev Avatar answered Oct 15 '22 14:10

Sergey Grinev