6

A JavaFX application exists and the application is starting from the Main.class file which extends Application:

  public class Main extends Application {

    /**
     * Keep a reference to the main Stage
     */
    public static Stage                 stage;
    /**
     * MainScene Controller
     */
    public static MainScene             mainSceneController;
    /**
     * The Capture Window of the application
     */
    public static CaptureWindow         captureWindowController;
    /**
     * Settings Scene Controller
     */
    public static SettingsController    settingsController;

    @Override
    public void start(Stage primary) throws Exception {

        stage = primary;
        ..........

        // CaptureWindow
        FXMLLoader loader1 = new FXMLLoader(getClass().getResource("/fxml/CaptureWindow.fxml"));
        loader1.load();
        captureWindowController = loader1.getController();

        // MainScene
        mainSceneController = new MainScene();

        .........   
    }

  }

Description

As you can see above I have 3 FXMLControllers(one is reusable[extends StackPane],others not).I have declared all of them static cause i want to access variables from one FXMLController from the other FXMLControllers.I use this strategy every time I use JavaFX and I don't think is good...

How I can change the code below so I can access variables or methods of one FXMLController from other FXMLController? Is that possible without using static keyword?

Consider that the Controllers are represented from different classes which can be in different packages.


Also before writing this question I had a look Static @FXML variables in FXML Controller

Community
  • 1
  • 1
GOXR3PLUS
  • 6,877
  • 9
  • 44
  • 93
  • 1
    "so I can access variables or methods of one `FXMLController` from other `FXMLController`". This is where you're going wrong (in my opinion, and all of this is about code style, so it is necessarily somewhat opinion based). Use a MVC (or MVP, MVVM, etc) approach, so you don't need to do this. See http://stackoverflow.com/questions/32342864/applying-mvc-with-javafx for a simple example of this approach. – James_D Oct 18 '16 at 21:16
  • @James_D I had read about MVC Pattern,Model Presenter Viewer pattern etc...What i need to know is..1)`Do i have to separate all the packages of the application like' , package model,package viewer,package controller and this three packages will contain all the code for the application?` 2)`Again i can't seem to find a solution on this current question.I have the same problems,something has to be static to access it`. If you don't mind add a theoretical way of avoiding this,and how you implement an application that you are making,i mean the packages and the logic , it seems strange. – GOXR3PLUS Oct 26 '16 at 15:09
  • 1. Which package things are in is completely irrelevant. Packages are simply a way of organizing your classes and other resources. Some people like to put all views in one package, all controllers in one package, etc. Others (like me), will put the view and controller associated with it in one package, and use a different package for each part of the application. 2. You're still starting from the wrong place: if you need to access something defined in one controller in another controller, you are simply doing it wrong. Just give all the controllers access to the same model. – James_D Oct 26 '16 at 15:14
  • Again, see the [example](http://stackoverflow.com/questions/32342864/applying-mvc-with-javafx) I linked above. Doing something in one view (selecting an item in the `ListView` for example) triggers code in the corresponding controller. The controller updates the model. Other controllers observe the model, so when the model changes, it triggers code in those other controllers. So code in one controller cause code in another controller to execute, but they are decoupled via the model, so no controller has any knowledge of any other controller. – James_D Oct 26 '16 at 15:18
  • 1
    Complete code for the post linked above is at https://github.com/james-d/SimpleMVP. Note that it does not have the package structure you describe but a different one. Again, that really has nothing to do with what you are asking. – James_D Oct 26 '16 at 15:21
  • @James_D The View and The Controller in Java are not the one and the same?I see no difference...Controller Class contains the user interface elements,controller has the listeners(maybe in `initialize` method,maybe in `@FXML methods`) , so as the pattern is saying the controller is getting the input from the view,but the view is actually the controller class. Although after reading your code it is to me more clear.`A possible solution i see here is in every controller i have to pass a reference of the controllers it may needs.Am i right?So i don't need to define static instances`. – GOXR3PLUS Oct 26 '16 at 15:30
  • To get an answer to this question, I think you really need to explain *why* you *think* you need to access one controller directly from another. This is really an [X-Y problem](http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem): you are essentially asking for ways to fix the *wrong solution* to the problem you have, which is really about how one controller/view responds to changes initiated by another controller/view. You're assuming the only way to do this is by one controller calling a method on another controller, but that assumption is false. – James_D Oct 26 '16 at 15:31
  • 2
    The boundary between view and controller is tricky: there are several variants of the pattern, and in any given application you'd probably interpret this slightly differently. In JavaFX the view is often interpreted as a "passive view", which simply lays out the components and does nothing else. The "controller" is really a "presenter", which has intimate knowledge of the view, invokes methods directly on the view components, and updates the model in response to user input. Far more important than the distinction between view and controller is the use of a model: so you need to start there. – James_D Oct 26 '16 at 15:35
  • If you really want to get into this, start by reading some of Martin Fowler's writing (e.g [this](http://martinfowler.com/eaaDev/uiArchs.html)). But again, the most important part in any of MVC, MVP, MVVM, Passive View, Supervising Controller, etc etc etc is that you define a model, and some part of the view-controller/presenter observes the model and receives notifications from it. – James_D Oct 26 '16 at 15:38
  • @James_D I am reading about the MVC Pattern etc from this book http://www.apress.com/gp/book/9781484211434 . Seems to explain them good but showing only one general situation.I am checking your recommendation. – GOXR3PLUS Oct 26 '16 at 15:41
  • And I guess one last(?) comment: you don't *have* to use a M-anything pattern, as you can see from @cdaiga's answer and example. There (s)he does more like you describe: " A possible solution I see here is in every controller I have to pass a reference of the controllers it may need." That works, and is better (imho) than using static fields, but the traditional criticism is that is becomes too tightly coupled and therefore too difficult to extend the functionality. But again, these are all opinions. – James_D Oct 26 '16 at 15:46
  • @James_D I want to mention something hear after reading the half of the article.Before `fxml` came actually you could device controller from viewer.Viewer can be in a different class,and extend for example GridPane and Controller be on a different class and have a reference of the Viewer.When `fxml` came they are tightly coupled i see.So we are passing to the MVP pattern? – GOXR3PLUS Oct 26 '16 at 16:19
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/126751/discussion-between-james-d-and-goxr3plus). – James_D Oct 26 '16 at 16:33

5 Answers5

3

Actually the answer for this question seems a little bit complicated it has to do with MVC pattern and it's evolution until now.We will use MVP Pattern.

After a long discussion i got a link on this website http://martinfowler.com/eaaDev/uiArchs.html defining the historical evolution of the different patterns used from the old ages of Smalltalk until now.


The actually solution is using Model Viewer Presenter Pattern(MVP) which can be visually described using these images:

enter image description here

enter image description here

For more you can read(http://www.wildcrest.com/Potel/Portfolio/mvp.pdf)


For an example on JavaFX have a look on James_D answer here (Applying MVC With JavaFx)


Last but not least have a look at the comments here:

Finally:

If anything is inaccurate feel free to edit.

Community
  • 1
  • 1
GOXR3PLUS
  • 6,877
  • 9
  • 44
  • 93
1

I think you are not supposed to declare @FXML annotated attributes with the static keyword. See this GitHub project on how you should do it. Like that you instantiate a controller only when it is needed, and your app will be stateless.

cdaiga
  • 4,861
  • 3
  • 22
  • 42
  • All the controllers are needed,but i want to be able to access one controller variables from another controller.And in this case all the instances of controllers are into the `Main.class`. – GOXR3PLUS Oct 18 '16 at 21:13
  • See the github project. I am responding from my phone, can't say much now. May be tomorrow I will update my answer if by then there is no better answer. – cdaiga Oct 18 '16 at 21:19
  • @cdaiga I didn't look at that whole project, but from what I could see the application only displays one view at a time. As I understand this question, the OP wants multiple views displayed at the same time, each with their own controller, so the controllers need to share data or otherwise communicate. Your example (at least what I saw from just scanning it) doesn't do that. Not all applications can be stateless. – James_D Oct 18 '16 at 21:51
  • No @James_D, it displays different UI blocks at once. If you look again closely you will see that there is the Application controller that manages the application layout and main menu. Now each menu button of the main menu triggers a UI which is loaded into the central stackpane. So at some point there are more one UI and controllers running. I believe he can use this pattern to solve his problem. – cdaiga Oct 18 '16 at 22:15
  • @cdaiga OK, I see now. You're imperatively calling methods on the different controllers to update them with various data. So effectively all the data is just managed by a central controller and there is no independent data model class, is that right? Then the other controllers are effectively just passive and wait until they're told what to do. I wonder how maintainable that is compared to a MVC/MVP type of approach, where the view (MVC) or controller/presenter (MVP) observe properties in a model and respond to changes in the data without direct method invocation. – James_D Oct 18 '16 at 22:27
  • Bro that application is amazing!Although i can't connect to server :).Right now i am gonna learn mysql! – GOXR3PLUS Oct 19 '16 at 02:32
1

JavaFx is mostly composed of set of [well designed] tools but unfortunately by itself does not provide a good framework for creating complex UI designs e.g. MVC/MVP patterns, view flows and actions on multiple controllers, So you have to rely on third-party application frameworks for those for example:

In my opinion none of them are widely used or mature enough to be considered a de facto standard but using them is encouraged.


Example Using DataFx

DataFx uses a concept named Flow to associate views sharing a flow of events (and possibly data) among themselves. By using Flow combined with EventSystem you can define and access methods on other controllers and assign custom event listeners to various events associated with JavaFx Nodes in different controllers sharing a flow.

Here is an example from DataFx samples which represents two separate sender and receiver views with distinct FXML files and controllers:

enter image description here

public class EventSystemDemo extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception {

    HBox box = new HBox();
    box.setSpacing(12);
    box.setPadding(new Insets(12));
    box.setFillHeight(true);
    box.setAlignment(Pos.CENTER);

    Flow senderFlow = new Flow(ProducerController.class);
    box.getChildren().add(senderFlow.start());

    Flow receiverFlow = new Flow(ReceiverController.class);
    box.getChildren().add(receiverFlow.start());

    primaryStage.setScene(new Scene(box));
    primaryStage.show();

    }

    public static void main(String... args) {
        launch(args);
    }
}

Sender view controller:

@ViewController("producer.fxml")
public class ProducerController {

    @FXML
    @EventTrigger()
    private Button sendButton;

    @FXML
    private TextField textField;

    @EventProducer("test-message")
    private String getMessage() {
        return textField.getText();
    }

}

Receiver view controller:

@ViewController("receiver.fxml")
public class ReceiverController {

    @FXML
    private Label messageLabel;

    @OnEvent("test-message")
    private void onNewChatMessage(Event<String> e) {
        messageLabel.setText(e.getContent());
    }
}

Sender View:

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

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

<VBox alignment="TOP_CENTER" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" spacing="12.0" style="-fx-border-color: darkgrey; -fx-border-width: 2;" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
   <children>
      <Label text="Sender">
         <font>
            <Font name="System Bold" size="24.0" />
         </font>
      </Label>
      <TextField fx:id="textField" />
      <Button fx:id="sendButton" mnemonicParsing="false" text="send" />
   </children>
   <padding>
      <Insets bottom="12.0" left="12.0" right="12.0" top="12.0" />
   </padding>
</VBox>

Receiver View:

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

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

<VBox alignment="TOP_CENTER" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" spacing="12.0" style="-fx-border-color: darkgrey; -fx-border-width: 2;" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
    <children>
        <Label text="Receiver">
         <font>
            <Font name="System Bold" size="24.0" />
         </font></Label>
      <Label fx:id="messageLabel" />
    </children>
    <padding>
        <Insets bottom="12.0" left="12.0" right="12.0" top="12.0" />
    </padding>
</VBox>

For further reading:

http://www.oracle.com/technetwork/java/javafx/community/3rd-party-1844355.html

http://www.guigarage.com/2015/02/quick-overview-datafx-mvc-flow-api/

http://www.guigarage.com/2014/05/datafx-8-0-tutorials/

http://jacpfx.org/2014/11/03/JacpFX_DataFX_a_perfect_match.html

Passing Parameters JavaFX FXML

Community
  • 1
  • 1
Omid
  • 5,823
  • 4
  • 41
  • 50
1

You can implement your own controller factory so you would be able of creating your own controllers context.

I have used spring with javaFX, and I have implemented my own controllers factory using spring, in that way you can inject one controller in other (you shouldn't need it as those connections should be on the model)

If you want to try spring with Java fx: How to wire multiple fxml controllers using spring dependency-injection?

Community
  • 1
  • 1
David Mantilla
  • 228
  • 1
  • 10
1

Definitely you should use an Application Framework that will help you to structure your code.

If you want to try JRebirth, just cut your application in 4 parts:

  • MainModel composed by:
    • MainSceneModel
    • CaptureWindowModel
    • SettingsModel

You can access to one from anyone by using getModel(Model.class) or send async message using sendWave(Wave).

MainModel can use innerComponent to tightly link child-model to it.

It's pretty simple to use, if you are interested in I can prototype you a sample app just let me know.

Sébastien B.
  • 545
  • 4
  • 9