JavaFX Daten zwischen Controller "austauschen"

ralfb1105

Bekanntes Mitglied
Hallo zusammen,

ich habe mal wieder eine Frage zu JavaFX und dem Austausch, sprich das auslesen von z.B. Text Feldern zwischen zwei Controller.
Ich habe dazu folgenden Thread von Euch gelesen und es versucht umzusetzen, es endet aber leider immer in einer NPE und ich weiß nicht genau wie/wo ich die " Controller Injection" machen soll.
https://www.java-forum.org/thema/javafx-in-mehrere-controller-aufteilen.180123/

Hier mal als Referenz mein einfaches kleines Beispiel:
Ich starte ein Fenster "Controller1" welches ein TextField und einen Button enthält. Bei drücken des Button wird ein weiteres Fenster -> Controller2, geöffnet. Dieses hat auch wieder ein TextField und ein Button. Beim drücken des Button in Fenster2 (Controller2) soll der Inhalt von controller1.textfield in das TextField von Controller2 geschrieben werden.

Referenz Code:
1. Main.java
Java:
package application;
   
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.stage.Stage;
import javafx.scene.Parent;
import javafx.scene.Scene;


public class Main extends Application {
    @Override
    public void start(Stage primaryStage) {
        try {
            FXMLLoader loader = new FXMLLoader(getClass().getResource("Box1.fxml"));
            Parent root = loader.load();
            Scene scene = new Scene(root);
            scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
            primaryStage.setScene(scene);
            primaryStage.setTitle("Controller1");
            primaryStage.show();
        } catch(Exception e) {
            e.printStackTrace();
        }
    }
   
    public static void main(String[] args) {
        launch(args);
    }
}
2. Controller1.java
Java:
package application;

import java.io.IOException;

import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.stage.Modality;
import javafx.stage.Stage;

public class Controller1 {
   
    Controller2 controller2;
   
    @FXML
    TextField textField1;
   
    @FXML
    Button startCtrl2;
   
   
   
    public void initialize() {
        textField1.setText("The quick brown fox jumps over the lazy dog");
    }
   
    public void startCtrl2() throws IOException  {
        FXMLLoader loaderBox2 = new FXMLLoader(getClass().getResource("Box2.fxml"));
        Parent rootBox2 = loaderBox2.load();
        Stage stageBox2 = new Stage();
        stageBox2.initModality(Modality.APPLICATION_MODAL);
        stageBox2.setOpacity(1);
        stageBox2.setTitle("Controller2");
        Scene sceneBox2= new Scene(rootBox2);
        sceneBox2.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
        stageBox2.setScene(sceneBox2);
        stageBox2.show();
    }

}
3. Controller2.java
Java:
package application;

import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;

public class Controller2 {
   
    Controller1 controller1;
   
    @FXML
    TextField textField2;
   
    @FXML
    Button setLabel;
   
    public void initialize() {
       
    }
   

    @FXML
    public void setInfoTextField2() {
        textField2.setText(controller1.textField1.getText());
    }

}
4. Box1.fxml
Java:
<?xml version="1.0" encoding="UTF-8"?>

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

<VBox prefHeight="151.0" prefWidth="429.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.Controller1">
   <children>
      <HBox alignment="CENTER" prefHeight="40.0" prefWidth="337.0">
         <children>
            <Text strokeType="OUTSIDE" strokeWidth="0.0" text="Controller1">
               <font>
                  <Font name="System Bold" size="18.0" />
               </font>
            </Text>
         </children>
         <VBox.margin>
            <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
         </VBox.margin>
      </HBox>
      <HBox alignment="CENTER_LEFT" prefHeight="72.0" prefWidth="337.0">
         <children>
            <TextField fx:id="textField1" editable="false" prefHeight="30.0" prefWidth="272.0" />
            <Button fx:id="startCtrl2" mnemonicParsing="false" onAction="#startCtrl2" text="Start Controller2">
               <HBox.margin>
                  <Insets bottom="10.0" left="30.0" top="10.0" />
               </HBox.margin>
            </Button>
         </children>
         <VBox.margin>
            <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
         </VBox.margin>
      </HBox>
   </children>
</VBox>
5. Box2.fxml
Java:
<?xml version="1.0" encoding="UTF-8"?>

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

<VBox prefHeight="173.0" prefWidth="320.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.Controller2">
   <children>
      <HBox alignment="CENTER" prefHeight="55.0" prefWidth="320.0">
         <children>
            <Text strokeType="OUTSIDE" strokeWidth="0.0" text="Controller2">
               <font>
                  <Font size="18.0" />
               </font>
            </Text>
         </children>
         <VBox.margin>
            <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
         </VBox.margin>
      </HBox>
      <HBox alignment="CENTER_LEFT" prefHeight="62.0" prefWidth="320.0">
         <children>
            <TextField fx:id="textField2" editable="false" prefHeight="25.0" prefWidth="210.0" />
            <Button fx:id="setLabel" mnemonicParsing="false" onAction="#setInfoTextField2" text="Set Label">
               <HBox.margin>
                  <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
               </HBox.margin>
            </Button>
         </children>
         <VBox.margin>
            <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
         </VBox.margin>
      </HBox>
   </children>
</VBox>

ich habe schon versucht die Deklaration "Controller2 controller2;" in die initialize() Methode zu schreiben, bringt aber auch nichts.
Die NPE kommt in der Zeile
Java:
textField2.setText(controller1.textField1.getText());
also wenn ich auf das Objekt zugreifen will.

Was ich in dem o.g. Thread nicht ganz verstanden habe ... muss ich in den .fxml Dateien noch includes einbauen? Wenn ja, wo oder geht das auch über den SceneBuilder?

Gruß

Ralf
 
Wenn du per Hand neue FXML lädst und anzeigst, musst du die selbst um die Controller kümmern 😉

In diesem Fall: in startCtrl2 den Controller2 aus dem Loader holen und dann dem Controller2 den Controller1 übergeben.


Das automatische Setzen der Controller klappt nur, wenn die Views direkt in der FXML eingebunden sind, aber auch da wird nur der Controller im Parent gesetzt, um den im Kind muss man sich selber kümmern.
 
Hallo mrBrown,
sorry, aber ich muss mal wieder im Detail nachfragen weil ich es noch nicht ganz verstanden habe.

in startCtrl2 den Controller2 aus dem Loader holen und dann dem Controller2 den Controller1 übergeben.

Ich war der Meinung das ich mit der Zeile
Java:
FXMLLoader loaderBox2 = new FXMLLoader(getClass().getResource("Box2.fxml"));
die Objekte aus "Box2.fxml" hole.
Wie muss ich den jetzt den Controller2 aus dem Loader holen und dem Controller1 übergeben?

Ich habe irgendwie jetzt einen Knoten im Kopf ... ohne zu wissen wie es zu codieren ist, hätte ich jetzt gesagt das ich in startCtrl2 den Controller1 holen muss und diesen daann dem Controller2 übergeben muss damit der dann auf die Objekte von Controller1 zugreifen kann ... ???

Könntest Du mir an Hand meines Beispiels ein Code Schnipsel zeigen bezogen auf Deine obige Aussage?

Gruß
Ralf
 
die Objekte aus "Box2.fxml" hole.
Das lädt die FXML, über den FXMLLoader kannst du dann Parent (=die View) und den Controller bekommen.
Wie muss ich den jetzt den Controller2 aus dem Loader holen und dem Controller1 übergeben?
loaderBox2.getController() zum bekommen von Controller2 und in Controller2 ein Setter für Controller1

Ich habe irgendwie jetzt einen Knoten im Kopf ... ohne zu wissen wie es zu codieren ist, hätte ich jetzt gesagt das ich in startCtrl2 den Controller1 holen muss und diesen daann dem Controller2 übergeben muss damit der dann auf die Objekte von Controller1 zugreifen kann ... ???
Ja, in startCtrl2 bist du allerdings schon Controller1 ("this"), den musst du also nicht erst holen, der Rest passt aber.
 
Ich bin zu blöd 😕
Sorry ... ich habe e simmer noch nicht ...
Was ich bis jetzt verstanden habe ..
Ich habe nun im Controller2 einen Setter für Controller1 eingebaut. Hier der Controller2.java:
Java:
package application;

import java.io.IOException;

import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;

public class Controller2 {

    @FXML
    Controller1 controller1;

    public void setController1(Controller1 controller1) {
        this.controller1 = controller1;
    }

    @FXML
    VBox box2;

    @FXML
    TextField textField2;

    @FXML
    Button setLabel;

    public void initialize() {

    }

    @FXML
    public void setInfoTextField2() throws IOException {
        textField2.setText(controller1.getTextField1Data());
    }

}
Wo und wie in Controller1 muss ich denn jetzt den Setter "setController1" von Controller2.java aufrufen?
Ich vermute doch, so wie ich Dich verstanden habe in "startCtrl2()" richtig?

Dort habe ich doch aber mit "loaderbox2" den Controller2 geholt - oder liege ich hier falsch?

Könntest Du mir noch bei der letzten Anweisung, sprich wie muss ich die Setter Methode in startCtrl2() aufrufen helfen?
DANKE!
Gruß
Ralf
 
Wo und wie in Controller1 muss ich denn jetzt den Setter "setController1" von Controller2.java aufrufen?
Ich vermute doch, so wie ich Dich verstanden habe in "startCtrl2()" richtig?
Ja.

Dort habe ich doch aber mit "loaderbox2" den Controller2 geholt - oder liege ich hier falsch?
Nein, loaderbox2 ist ein FXMLLoader, aus diesem kannst du aber den Controller erhalten

Könntest Du mir noch bei der letzten Anweisung, sprich wie muss ich die Setter Methode in startCtrl2() aufrufen helfen?
Java:
        FXMLLoader loaderBox2 = new FXMLLoader(getClass().getResource("Box2.fxml"));
        Parent rootBox2 = loaderBox2.load();
        //folgende zwei Zeilen kommen dazu:
        this.controller2=loaderBox2.getController();  //controller aus dem loader bekommen
        this.controller2.setController1(this);  //setter auf dem Controller aufrufen, um controller1 dort zu setzen
        ...
 
Hier noch einmal der komplette Code als Referenz, falls es mal jemand gebrauchen kann:
Main.java
Java:
package application;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.stage.Stage;
import javafx.scene.Parent;
import javafx.scene.Scene;

public class Main extends Application {
    @Override
    public void start(Stage primaryStage) {
        try {
            FXMLLoader loader = new FXMLLoader(getClass().getResource("Box1.fxml"));
            Parent root = loader.load();
            Scene scene = new Scene(root);
            scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
            primaryStage.setScene(scene);
            primaryStage.setTitle("Controller1");
            primaryStage.show();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        launch(args);
    }
}
Controller1.java
Java:
package application;

import java.io.IOException;

import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.stage.Modality;
import javafx.stage.Stage;

public class Controller1 {

    @FXML
    Controller2 controller2;

    @FXML
    VBox box1;

    @FXML
    private TextField textField1;

    @FXML
    Button startCtrl2;

    public void initialize() {
        textField1.setText("The quick brown fox jumps over the lazy dog");
    }

    public void startCtrl2() throws IOException {
        FXMLLoader loaderBox2 = new FXMLLoader(getClass().getResource("Box2.fxml"));
        Parent rootBox2 = loaderBox2.load();
        this.controller2 = loaderBox2.getController(); // controller aus dem Loader bekommen
        this.controller2.setController1(this); // Setter auf dem Controller (controller2) aufrufen, um controller1 dort
                                                // zu setzen
        Stage stageBox2 = new Stage();
        stageBox2.initModality(Modality.APPLICATION_MODAL);
        stageBox2.setOpacity(1);
        stageBox2.setTitle("Controller2");
        Scene sceneBox2 = new Scene(rootBox2);
        sceneBox2.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
        stageBox2.setScene(sceneBox2);
        stageBox2.show();
    }

    public String getTextField1Data() {
        return textField1.getText();
    }

}
Controller2.java
Java:
package application;

import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;

public class Controller2 {

    @FXML
    Controller1 controller1;

    public void setController1(Controller1 controller1) {
        this.controller1 = controller1;
    }

    @FXML
    VBox box2;

    @FXML
    TextField textField2;

    @FXML
    Button setLabel;

    public void initialize() {

    }

    @FXML
    public void setInfoTextField2() {
        textField2.setText(controller1.getTextField1Data());
    }

}

Gruß
Ralf
 
Nur so als Nachtrag, wenn man ein FXML in ein anderes Verschachteln will (und nicht mehr austauschen möchte), kann man das auch einfacher haben:
HTML:
<VBox xmlns:fx="http://javafx.com/fxml" fx:controller="my.package.Controller1" spacing="10.0">
    <fx:define>
        <fx:include fx:id="fmxl2" source="/ui/MyFxml2.fxml"/>
    </fx:define>

<!-- ........ -->

    <!-- fügt es den Children der umgebenden VBox hinzu (Achtung: kein "$" am Anfang!) -->
    <fx:reference source="fmxl2" />

    <!-- bei Controls, die nur ein Child haben können (Achtung: "$" am Anfang!): -->
    <!-- <ScrollPane content="$fxml2" /> -->
</VBox>

Das MyFxml2.fxml mit seinem Controller (z.B. my.package.Controller2) kannst du im my.package.Controller1 dann einfach per @FXML-Annotation bekommen:

Java:
// Du kannst dir den Root des anderen FXMLs holen, Name = <fx:id vom include-Block>
@FXML private VBox fxml2;
// auch der Controller ist einfach: Name = <fx:id vom include-Block>Controller
@FXML private Controller2 fxml2Controller;
 
Hallo dzim,

Danke für Deinen Input!! Ich habe das jetzt (noch) nicht im Code getestet, hätte dazu aber eine Frage.
Würde ich mit der von Dir beschriebenen Methode das von mir im Eingang beschriebene "Problem", sprich den Zugriff auf z.B. ein TextField welches in Controller1 - box1.fxml - erstellt/registriert ist, aus Controller2 - box2.fxml - realisieren können?

Gruß
Ralf
 
Hallo mrBrown, @All,

jetzt habe ich doch noch ein Problem die Lösung von mrBrown in meine Applikation zu implementieren 🙁

Problem:
Ich möchte in Controller2 das TextField nicht über einen Button setzen, sondern das TextFeld soll gefüllt sein/werden wenn das Fenster aufgebaut wird.

Ich war naiv davon ausgegangen das dies über die initialize() Methode funktionieren sollte
Java:
public void initialize() {
        textField2.setText(controller1.getTextField1Data());
    }
dies führt aber zu einer NPE, da anscheinend zu dem Zeitpunkt das Objekt noch nicht zur Verfügung steht.

Frage:
Wie/Wo müsste ich den Code einfügen damit er das Textfeld beim starten des Fenster sofort setzt?

Gruß
Ralf
 
Wenn du es im Controller2 "injectest" - also einen Setter machst, spricht grundsätzlich nichts dagegen.

Aber:
Bedenke, dass das eher unsauber ist. Ich arbeite i.d.R. mit einem Dependency Injection Framework und injecte mir über dieses z.B. ein zentrales App-Model, in dem ich StringProperties, etc., hinterlege. An denen kannst du nicht für Änderungen registieren, oder andere UI-Bereiche sogar davon ändern lassen. Vorteil ist, dass du nicht Teile deines UIs an andere UIs übergibst, sondern schön Abstrakt bleibst.

Injection kann man übrigens auch auf einfache Art über die FXMLLoader machen: Entweder den Controller aus dem Loader holen und dein Model aktiv setzen (Variante1) oder du übernimmst die Controller-Instanziierung für deinen FXMLLoader (Variante2).

  • Variante 1
    • dein Kontroller könnte z.B. ein von dir erstelltes Interface implementieren -> check ob Controller Interface wirklich implementiert -> Cast -> setter nutzen
    • Achtung: dein Setter wird nach der initialize-Methode aufgerufen!
  • Variante 2
    • Beispiel hier: https://github.com/bgmf/poc/blob/ma.../dzim/tests/fx/MainWithControllerFactory.java
    • Wieder wie in Variante 1 verfahren (Interface -> überprüfen, ob Interface verwendet wird -> Cast -> setter)
    • Achtung: dein Setter wird vor der initialize-Methode aufgerufen!
    • PS: Die Variante könnte dein Problem von eben lösen, da du hier vorm initialize schon die Daten da hast und im initialize nur noch setText machen musst...

Zum Model selbst: Wenn du es immer brav überall rein gibst, ist der beste Weg, dein Model-Objekt im Main anzulegen (start-Methode der Applikation-Klasse) und dann zu übergeben...
 
Hallo dzim,

leider reicht mein Know How für Deine Ausführungen absolut nicht aus um das im Moment zu verstehen, geschweige denn umzusetzen :-(

Ich habe es dann, ohne es im Detail zu verstehen, mal wie in Deinem GitHib Projekt beschrieben implementiert, und es läuft so wie vorher - wenn ich den Button drücke in Controller2 wird der Text aus dem Controller1 in das Textfeld Controller2 geschrieben.

Allerdings kommt die NPE auch hier wenn ich in der initialize() Methode versuche den Text zu setzen ...

Hier mal der Code als Refernz, vielleicht sieht Du ja einen fehler oder kannst mir einen Tipp geben wie ich das lösen könnte:
1. Main.java
Java:
package application;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.stage.Stage;
import javafx.scene.Parent;
import javafx.scene.Scene;

public class Main extends Application {
    @Override
    public void start(Stage primaryStage) throws Exception {
        try {
            // FXMLLoader loader = new FXMLLoader(getClass().getResource("Box1.fxml"));
            FXMLLoader loader = new FXMLLoader();
            loader.setControllerFactory(this::controllerForClass);
            loader.setLocation(getClass().getResource("Box1.fxml"));
            Parent root = loader.load();
            Scene scene = new Scene(root);
            scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
            primaryStage.setScene(scene);
            primaryStage.setTitle("Controller1");
            primaryStage.show();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private Object controllerForClass(Class<?> clazz) {
        try {
            Object controllerInstance = clazz.newInstance();
            // controllerInstance#someArbitraryMethodCall if necessary
            // will be triggered *before* the initialize method!
            return controllerInstance;
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
            return null;
        }
    }

    public static void main(String[] args) {
        launch(args);
    }
}
2. Controller1.java
Java:
package application;

import java.io.IOException;

import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.stage.Modality;
import javafx.stage.Stage;

public class Controller1 {

    @FXML
    private Controller2 controller2;

    @FXML
    VBox box1;

    @FXML
    private TextField textField1;

    @FXML
    Button startCtrl2;

    public void initialize() {
        textField1.setText("The quick brown fox jumps over the lazy dog");
    }

    public void startCtrl2() throws IOException {
        // FXMLLoader loaderBox2 = new FXMLLoader(getClass().getResource("Box2.fxml"));
        FXMLLoader loaderBox2 = new FXMLLoader();
        loaderBox2.setControllerFactory(this::controllerForClass);
        loaderBox2.setLocation(getClass().getResource("Box2.fxml"));
        Parent rootBox2 = loaderBox2.load();
        this.controller2 = loaderBox2.getController(); // controller aus dem Loader bekommen
        this.controller2.setController1(this); // Setter auf dem Controller (controller2) aufrufen, um controller1 dort
                                                // zu setzen
        Stage stageBox2 = new Stage();
        stageBox2.initModality(Modality.APPLICATION_MODAL);
        stageBox2.setOpacity(1);
        stageBox2.setTitle("Controller2");
        Scene sceneBox2 = new Scene(rootBox2);
        sceneBox2.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
        stageBox2.setScene(sceneBox2);
        stageBox2.show();
    }

    public String getTextField1Data() {
        return textField1.getText();
    }

    private Object controllerForClass(Class<?> clazz) {
        try {
            Object controllerInstance = clazz.newInstance();
            // controllerInstance#someArbitraryMethodCall if necessary
            // will be triggered *before* the initialize method!
            return controllerInstance;
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
            return null;
        }
    }

}
3. Controller2.java
Java:
package application;

import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;

public class Controller2 {

    public Controller2() {
        super();
    }

    @FXML
    private Controller1 controller1;

    public void setController1(Controller1 controller1) {
        this.controller1 = controller1;
    }

    @FXML
    VBox box2;

    @FXML
    TextField textField2;

    @FXML
    Button setLabel;

    public void initialize() {
        setInfoTextField2();
    }

    @FXML
    public void setInfoTextField2() {
        textField2.setText(controller1.getTextField1Data());
    }

}

Gruß
Ralf
 
Heute wird's bei mir sicher nichts mehr (es sei denn, ich hab Nachts mal Langeweile...), aber ich kann morgen früh mal versuchen, ein vollständiges Bsp. zusammenzuhacken und in das Github-Repository einpflegen.
 
Hallo dzim,

ich hoffe für Dich das Du Nachts keine Langeweile hast und Deinen Feierabend genießen kannst 🙂

Ich bin allerdings auch sehr froh das Du ein Beispiel erstellen willst, denn es hat mich schon der Ehrgeiz gepackt die Applikation so zu erstellen wie es am Besten siot, und Dein Ansatz klingt sehr logisch.

Nun hoffe ich das ich an Hand Deines Beispiels das auch noch verstehe um es dann auf andere Apps zu projezieren.

Vielleicht spart es Dir Tipparbeit wenn Du es an Hand meiner Mini-App versuchst zu erklären/darzustellen??
Ich habe das nun so ergänzt das ich auch noch eine Model Klasse habe, damit ich auch verstehe wie das Model sauber eingebunden wird.
Hier nun mal als Referenz meine Klassen und FXML Dateien - hoffe es hilft Dir vielleicht.
1. Main.java
Java:
package application;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.stage.Stage;
import javafx.scene.Parent;
import javafx.scene.Scene;

public class Main extends Application {
    @Override
    public void start(Stage primaryStage) throws Exception {
        try {
            // FXMLLoader loader = new FXMLLoader(getClass().getResource("Box1.fxml"));
            FXMLLoader loader = new FXMLLoader();
            loader.setControllerFactory(this::controllerForClass);
            loader.setLocation(getClass().getResource("Box1.fxml"));
            Parent root = loader.load();
            Scene scene = new Scene(root);
            scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
            primaryStage.setScene(scene);
            primaryStage.setTitle("Controller1");
            primaryStage.show();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private Object controllerForClass(Class<?> clazz) {
        try {
            Object controllerInstance = clazz.newInstance();
            // controllerInstance#someArbitraryMethodCall if necessary
            // will be triggered *before* the initialize method!
            return controllerInstance;
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
            return null;
        }
    }

    public static void main(String[] args) {
        launch(args);
    }
}
2. Controller1.java
Java:
package application;

import java.io.IOException;

import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.stage.Modality;
import javafx.stage.Stage;

public class Controller1 {

    Model model = new Model();

    @FXML
    private Controller2 controller2;

    @FXML
    VBox box1;

    @FXML
    private TextField textField1;

    @FXML
    Button startCtrl2;

    @FXML
    Button setFromModel;

    public void initialize() {
        textField1.setText("The quick brown fox jumps over the lazy dog");
    }

    public void startCtrl2() throws IOException {
        // FXMLLoader loaderBox2 = new FXMLLoader(getClass().getResource("Box2.fxml"));
        FXMLLoader loaderBox2 = new FXMLLoader();
        loaderBox2.setControllerFactory(this::controllerForClass);
        loaderBox2.setLocation(getClass().getResource("Box2.fxml"));
        Parent rootBox2 = loaderBox2.load();
        this.controller2 = loaderBox2.getController(); // controller aus dem Loader bekommen
        this.controller2.setController1(this); // Setter auf dem Controller (controller2) aufrufen, um controller1 dort
                                                // zu setzen
        Stage stageBox2 = new Stage();
        stageBox2.initModality(Modality.APPLICATION_MODAL);
        stageBox2.setOpacity(1);
        stageBox2.setTitle("Controller2");
        Scene sceneBox2 = new Scene(rootBox2);
        sceneBox2.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
        stageBox2.setScene(sceneBox2);
        stageBox2.show();
    }

    public String getTextField1Data() {
        return textField1.getText();
    }

    private Object controllerForClass(Class<?> clazz) {
        try {
            Object controllerInstance = clazz.newInstance();
            // controllerInstance#someArbitraryMethodCall if necessary
            // will be triggered *before* the initialize method!
            return controllerInstance;
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
            return null;
        }
    }

    public void setFromModelTapped() {
        textField1.setText(model.returnString());
    }

}
3. Controller2.java
Java:
package application;

import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;

public class Controller2 {

    public Controller2() {
        super();
    }

    Model model = new Model();

    @FXML
    private Controller1 controller1;

    public void setController1(Controller1 controller1) {
        this.controller1 = controller1;
    }

    @FXML
    VBox box2;

    @FXML
    TextField textField2;

    @FXML
    Button setLabel;

    @FXML
    Button setFromModel;

    public void initialize() {
    }

    @FXML
    public void setInfoTextField2() {
        textField2.setText(controller1.getTextField1Data());
    }

    @FXML
    public void setFromModelTapped() {
        textField2.setText(model.returnString());
    }

}
4. Model.java
Java:
package application;

public class Model {

    public String returnString() {
        String returnString = "String from Model. Number is: " + randomNumberRange(1, 100);
        return returnString;
    }

    public static int randomNumberRange(int min, int max) {
        int n = (max + 1 - min) + min;
        return (int) (Math.random() * n);
    }

}
5. Box1.fxml
Java:
<?xml version="1.0" encoding="UTF-8"?>

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

<VBox fx:id="box1" prefHeight="194.0" prefWidth="429.0"
    xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1"
    fx:controller="application.Controller1">
    <children>
        <HBox alignment="CENTER" prefHeight="40.0" prefWidth="337.0">
            <children>
                <Text strokeType="OUTSIDE" strokeWidth="0.0" text="Controller1">
                    <font>
                        <Font name="System Bold" size="18.0" />
                    </font>
                </Text>
            </children>
            <VBox.margin>
                <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
            </VBox.margin>
        </HBox>
        <HBox alignment="CENTER_LEFT" prefHeight="72.0" prefWidth="337.0">
            <children>
                <TextField fx:id="textField1" editable="false"
                    prefHeight="30.0" prefWidth="410.0" />
            </children>
            <VBox.margin>
                <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
            </VBox.margin>
        </HBox>
        <HBox alignment="CENTER" prefHeight="53.0" prefWidth="429.0">
            <VBox.margin>
                <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
            </VBox.margin>
            <children>
                <Button fx:id="setFromModel" mnemonicParsing="false"
                    onAction="#setFromModelTapped" text="Set from Model" />
                <Button fx:id="startCtrl2" mnemonicParsing="false"
                    onAction="#startCtrl2" text="Start Controller2">
                    <HBox.margin>
                        <Insets bottom="10.0" left="30.0" top="10.0" />
                    </HBox.margin>
                </Button>
            </children>
        </HBox>
    </children>
</VBox>
6. Box2.fxml
Java:
<?xml version="1.0" encoding="UTF-8"?>

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

<VBox fx:id="box2" prefHeight="208.0" prefWidth="320.0"
    xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1"
    fx:controller="application.Controller2">
    <children>
        <HBox alignment="CENTER" prefHeight="55.0" prefWidth="320.0">
            <children>
                <Text strokeType="OUTSIDE" strokeWidth="0.0" text="Controller2">
                    <font>
                        <Font size="18.0" />
                    </font>
                </Text>
            </children>
            <VBox.margin>
                <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
            </VBox.margin>
        </HBox>
        <HBox alignment="CENTER_LEFT" prefHeight="62.0" prefWidth="320.0">
            <children>
                <TextField fx:id="textField2" editable="false"
                    prefHeight="25.0" prefWidth="301.0" />
            </children>
            <VBox.margin>
                <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
            </VBox.margin>
        </HBox>
        <HBox alignment="CENTER" prefHeight="47.0" prefWidth="320.0">
            <children>
                <Button fx:id="setFromModel" mnemonicParsing="false"
                    onAction="#setFromModelTapped" text="Set from Model" />
                <Button fx:id="setLabel" mnemonicParsing="false"
                    onAction="#setInfoTextField2" text="Set from Ctrl1">
                    <HBox.margin>
                        <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
                    </HBox.margin>
                </Button>
            </children>
        </HBox>
    </children>
</VBox>

Gruß
Ralf
 
Allerdings kommt die NPE auch hier wenn ich in der initialize() Methode versuche den Text zu setzen ...
Du musst über die Factory injecten, dann wird der Setter noch vor initialize aufgerufen. D. h. in startCtrl2() die Zeilen
Java:
        this.controller2.setController1(this); // Setter auf dem Controller (controller2) aufrufen, um controller1 dort
                                                // zu setzen
entfernen und dafür in die Methode controllerForClass "verschieben":
Java:
    private Object controllerForClass(Class<?> clazz) {
        try {
            Object controllerInstance = clazz.newInstance();
            if (clazz.equals(Controller2.class)) {
                Controller2 ctrl2 = (Controller2) controllerInstance;
                ctrl2.setController1(this);
            }
            // controllerInstance#someArbitraryMethodCall if necessary
            // will be triggered *before* the initialize method!
            return controllerInstance;
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
            return null;
        }
    }
Nicht getestet, sollte aber funktionieren, d. h. Du solltest dann in Controller2#initialize das machen können, was Du ursprünglich tun wolltest:
Java:
    public void initialize() {
        setInfoTextField2();
    }
 
Hallo mihe7,

🙂 Danke - damit funktioniert es in meinem kleinen Beispiel wie von dzim beschrieben.
Genau genommen bin ich wirklich ein Depp, denn der Kommentar von dzim
// controllerInstance#someArbitraryMethodCall if necessary
// will be triggered *before* the initialize method!
sagt es ja eigentlich schon, nur das ich es nicht im Detail verstanden habe was genau die Factory nun macht/bewirkt und somit auch nicht kapiert habe das genau dier die Übergabe stattfinden muss 😵

Mal shen was dzim nun zu meiner Implementierung sagt und vielleicht macht er ja noch ein Beispiel, z.B. mit dem erwähnten Interface das es ggf. noch deutlicher macht.

Dann ist noch die Frage zu folgender Aussage
Zum Model selbst: Wenn du es immer brav überall rein gibst, ist der beste Weg, dein Model-Objekt im Main anzulegen (start-Methode der Applikation-Klasse) und dann zu übergeben...
Da bin ich mir auch nicht ganz sicher ... ich habe das Model in Controller1 und Controller2 erstellt:
Java:
Model model = new Model();
aber so wie ich dzim verstehe sollte man das in der Main start() Methode machen und dann übergeben .. ?? ist das dann auch wieder mit einem Setter in den Controllern zu machen?

Danke nochmal für Eure Geduld und die professionelle Unterstützung!!!
Gruß
Ralf
 
Hallo Robat,

Deswegen das Model zentral in der Main erstellen und dann in die jeweiligen Controller geben.

Das mit dem Model erstellen in der Main würde ich dann so machen
Java:
private Model Model = new Model();

Dann aber die Frage wie ich in der start() Methode von Main die Controller am Besten bekomme. Würde ich das dann auch für die beiden Controller aus einen FXMLLoader machen, wie z.B. in Controller1 wo ich Controller2 hole:
Java:
this.controller2 = loaderBox2.getController();
.

Gruß
Ralf
 
Hallo,
ich habe es mal über den FXMLLoader folgendermassen probiert.
Java:
FXMLLoader loaderBox1 = new FXMLLoader();
            loaderBox1.setControllerFactory(this::controllerForClass);
            loaderBox1.setLocation(getClass().getResource("Box1.fxml"));
            this.controller1 = loaderBox1.getController(); // controller aus dem Loader bekommen
            Parent rootBox1 = loaderBox1.load();
Danach in der Factory, folgendes gemacht:
Java:
if (clazz.equals(Controller1.class)) {
                System.out.println("Clazz: " + clazz);
                Controller1 ctrl1 = (Controller1) controllerInstance;
                ctrl1.setModel(model);
            }
Das funktioniert ohne NPE oder anderer Excpetion für den Controller1, sprich ich kann über den Button auf das Model zugreifen und der erzeugte String wird im TextField gesetzt.

Wenn ich das allerding für Controller2 so versuche gibt es eine NPE im Controller2. Ich habe es genauso wie bei Controller1 probiert - in der Main:
Java:
FXMLLoader loaderBox2 = new FXMLLoader();
            loaderBox2.setControllerFactory(this::controllerForClass);
            loaderBox2.setLocation(getClass().getResource("Box2.fxml"));
            this.controller2 = loaderBox2.getController();
            Parent rootBox2 = loaderBox2.load();
und dann in der Factory ergänzt:
Java:
if (clazz.equals(Controller1.class)) {
                System.out.println("Clazz: " + clazz);
                Controller1 ctrl1 = (Controller1) controllerInstance;
                ctrl1.setModel(model);
            } else if (clazz.equals(Controller2.class)) {
                System.out.println("Clazz: " + clazz);
                Controller2 ctrl2 = (Controller2) controllerInstance;
                ctrl2.setModel(model);
            }
Beim starten kommt dann auch in der Console der Text aus der Factory:
Code:
Clazz: class application.Controller1
Clazz: class application.Controller2
aber gefolgt von einer Exception:
Code:
javafx.fxml.LoadException:
/C:/pdbrboer/GIT/Udeny-JavaGrundlagen/TestTwoController/bin/application/Box2.fxml

    at javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2601)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2571)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2441)
    at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2409)
    at application.Main.start(Main.java:32)
    at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$162(LauncherImpl.java:863)
    at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$175(PlatformImpl.java:326)
    at com.sun.javafx.application.PlatformImpl.lambda$null$173(PlatformImpl.java:295)
    at java.security.AccessController.doPrivileged(Native Method)
    at com.sun.javafx.application.PlatformImpl.lambda$runLater$174(PlatformImpl.java:294)
    at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
    at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at com.sun.glass.ui.win.WinApplication.lambda$null$148(WinApplication.java:191)
    at java.lang.Thread.run(Unknown Source)
Caused by: java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at sun.reflect.misc.Trampoline.invoke(Unknown Source)
    at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at sun.reflect.misc.MethodUtil.invoke(Unknown Source)
    at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2566)
    ... 12 more
Caused by: java.lang.NullPointerException
    at application.Controller2.setInfoTextField2(Controller2.java:46)
    at application.Controller2.initialize(Controller2.java:41)
    ... 22 more
Es sieht so aus als wenn das mit dem FXMLLoader nicht klappen würde ??
Code:
javafx.fxml.LoadException:
/C:/pdbrboer/GIT/Udeny-JavaGrundlagen/TestTwoController/bin/application/Box2.fxml

So einfach wie ich es mir gedacht habe ist es dann doch wieder nicht ...
Gruß
Ralf
 
Hier noch mal ein kleines Beispiel. Du darfst die zweite FXML nicht schon in deiner Hauptklasse laden.
Java:
public class Test extends Application {

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

    private Model model;

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

        model = new Model();

        FXMLLoader loader = new FXMLLoader();
        loader.setControllerFactory(clazz -> controllerForClass(clazz, model));
        loader.setLocation(getClass().getResource("view1.fxml"));
        Parent root = loader.load();

        primaryStage.setScene(new Scene(root));
        primaryStage.show();
    }

    public static Object controllerForClass(Class<?> clazz, Model model) {
        try {
            Object controllerInstance = clazz.newInstance();

            if(ModelInjectable.class.isAssignableFrom(clazz)) {
                ModelInjectable c = (ModelInjectable) controllerInstance;
                c.setModel(model);
            }
            return controllerInstance;
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
            return null;
        }
    }
}
Controller:
Java:
public class View1Controller implements ModelInjectable {
    private Model model;

    public void showView2() {
        try {
            FXMLLoader loader = new FXMLLoader();
            loader.setControllerFactory(clazz -> Test.controllerForClass(clazz, model));
            loader.setLocation(getClass().getResource("view2.fxml"));

            Stage stage = new Stage();
            stage.setScene(new Scene(loader.load()));
            stage.show();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    @Override
    public void setModel(Model model) {
        this.model = model;
    }
}

public class View2Controller implements ModelInjectable {

    private Model model;

    @Override
    public void setModel(Model model) {
        this.model = model;
    }
}
Sonstiges:
Java:
public class Model {
    // model class ...
}

public interface ModelInjectable {
    void setModel(Model model);
}
Edit:
controllerForClass(Class<?> clazz, Model model) ggf noch in eine eigene (Utils) Klasse auslagern
 
Hallo,

ich habe es jetzt einmal folgendermassen probiert:
1. In der Main.java das Model erstellt und dort dem Controller1 übergeben:
Java:
private Model model = new Model();

public void start(Stage primaryStage) throws Exception {
        try {
            // FXMLLoader loader = new FXMLLoader(getClass().getResource("Box1.fxml"));
            FXMLLoader loaderBox1 = new FXMLLoader();
            loaderBox1.setControllerFactory(this::controllerForClass);
            loaderBox1.setLocation(getClass().getResource("Box1.fxml"));
            this.controller1 = loaderBox1.getController(); // controller aus dem Loader bekommen
            Parent rootBox1 = loaderBox1.load();   
...
...
}

private Object controllerForClass(Class<?> clazz) {
        try {
            Object controllerInstance = clazz.newInstance();
            if (clazz.equals(Controller1.class)) {
                Controller1 ctrl1 = (Controller1) controllerInstance;
                System.out.println("Model instance Main:" + model);
                ctrl1.setModel(model);
            }
...
...
}
2. Im Controller1 das Model, sollte ja das aus Main sein, an Controller2 übergeben:
Java:
private Model model;

    public void setModel(Model model) {
        this.model = model;
        System.out.println("Model instance Controller1:" + model);
    }

public void startCtrl2() throws IOException {
        // FXMLLoader loaderBox2 = new FXMLLoader(getClass().getResource("Box2.fxml"));
        FXMLLoader loaderBox2 = new FXMLLoader();
        loaderBox2.setControllerFactory(this::controllerForClass);
        loaderBox2.setLocation(getClass().getResource("Box2.fxml"));
        Parent rootBox2 = loaderBox2.load();
        this.controller2 = loaderBox2.getController(); // controller aus dem Loader bekommen
...
...
}

private Object controllerForClass(Class<?> clazz) {
        try {
            Object controllerInstance = clazz.newInstance();
            if (clazz.equals(Controller2.class)) {
                Controller2 ctrl2 = (Controller2) controllerInstance;
                ctrl2.setController1(this); // Setter auf dem Controller (controller2) aufrufen, um controller1 dort zu setzen
                ctrl2.setModel(this.model);
            }
...
...
}

Damit funktioniert es erst einmal - bin mir aber nicht 100% sicher ob das so richtig ist im Sinne von JavaFX.
Die Ausgaben zeigen das ich überall die gleiche Model Instanz haben sollte:
Code:
Model instance Main:application.Model@2421c53b
Model instance Controller1:application.Model@2421c53b
Model instance Controller2:application.Model@2421c53b

Gruß

Ralf
 
Hallo Robat,

oh, da haben sich die Inputs überschnitten 😉 ich schaue mir Deine Ausführungen gleich mal an .. mit dem laden der 2 FXML (Controller2) lag ich ja schon mal nicht schlecht 🙄

Ich melde mich dann nochmal ...

Gruß
Ralf
 
Hallo Robat,

beim lesen habe ich eine Frage zu Deinem Beispiel.
Frage:
Müsste man das gleiche Interface, incl. der Implementierung in controllerForClass(), dann ja auch noch für ControllerInjectible erstellen um die Controller weiter zu geben? Richtig?
Java:
public interface ControllerInjectable {
    void setController(Controller ctrl);
}

Gruß
Ralf
 
Genau so ist es. Du könntest also Controller1 ControllerInjectable und ModelInjectable implementieren lassen. Dann kannst du in controllerForClass() einen weiteren if-Zweig einfügen für ControllerInjectable und dann dort den Controller setzen.
 
Hallo Robat,

generell sieht es schon mal nicht schlecht aus, nun überlege ich aber noch wie ich Deinen Vorschlag
controllerForClass(Class<?> clazz, Model model) ggf noch in eine eigene (Utils) Klasse auslagern

implementieren muss.
Ist das auch wieder als Injectable zu realisieren mit einem Interface IUtilsInjectable?
Wenn ich es "einfach" als neues Object in Main und Controller1 mache, gibt es beim starten von Controller2 aus Controller1 die bekannte NPE in "textField2.setText(controller1.getTextField1Data());".

Gruß
Ralf
 
Der Vorschlag dient einfach nur dazu, dass du diese Methode in einer eigenen Klasse definierst. Dafür brauchst du dann kein Interface. Zum Beispiel (hab deinen Namen einfach mal übernommen)
Java:
public class InjectionUtils {
    public static Object controllerForClass(Class<?> clazz, Model model) {
        try {
            Object controllerInstance = clazz.newInstance();

            if(ModelInjectable.class.isAssignableFrom(clazz)) {
                ModelInjectable c = (ModelInjectable) controllerInstance;
                c.setModel(model);
            }
            return controllerInstance;
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
            return null;
        }
    }
}
Dann benutzt die Methode jeweils wie folgt
Java:
loader.setControllerFactory(clazz -> InjectionUtils.controllerForClass(clazz, model));
 
@Robat wie immer sehr schön. Einzig andere Bezeichner würde ich wählen. Evtl. ControllerFactories für die Klasse und für die Methode newFactory.

Java:
loader.setControllerFactory(clazz -> ControllerFactories.newFactory(clazz, model));
 
Hallo Robat,

verstanden - Danke!
Hier jetzt noch mals als Zusammenfassung, und wenn möglich auch noch mal als Review, meine Klassen zu dem Beispiel:
1. Main.java
Java:
package application;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.stage.Stage;
import javafx.scene.Parent;
import javafx.scene.Scene;

public class Main extends Application {

    private Model model = new Model();

    @SuppressWarnings("unused")
    private Controller1 controller1;

    @Override
    public void start(Stage primaryStage) throws Exception {
        try {
            FXMLLoader loaderBox1 = new FXMLLoader();
            // Use Factory to inject Model in Controller1
            loaderBox1.setControllerFactory(clazz -> InjectionUtils.controllerForClass(clazz, model));
            loaderBox1.setLocation(getClass().getResource("Box1.fxml"));
            this.controller1 = loaderBox1.getController(); // controller aus dem Loader bekommen
            Parent rootBox1 = loaderBox1.load();
            Scene scene = new Scene(rootBox1);
            scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
            primaryStage.setScene(scene);
            primaryStage.setTitle("Controller1");
            primaryStage.show();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        launch(args);
    }
}
2. Controller1.java
Java:
package application;

import java.io.IOException;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.stage.Modality;
import javafx.stage.Stage;

public class Controller1 implements IModelInjectable {

    private Model model;

    @Override
    public void setModel(Model model) {
        this.model = model;
    }

    @FXML
    private Controller2 controller2;

    @FXML
    VBox box1;

    @FXML
    private TextField textField1;

    @FXML
    Button startCtrl2;

    @FXML
    Button setFromModel;

    public void initialize() {
        textField1.setText("The quick brown fox jumps over the lazy dog");
    }

    public void startCtrl2() throws IOException {
        FXMLLoader loaderBox2 = new FXMLLoader();
        // Use Factory to inject Controller1 and Model (from Main) in Controller2
        loaderBox2.setControllerFactory(clazz -> InjectionUtils.controllerForClass(clazz, model, this));
        loaderBox2.setLocation(getClass().getResource("Box2.fxml"));
        Parent rootBox2 = loaderBox2.load();
        this.controller2 = loaderBox2.getController(); // controller aus dem Loader bekommen
        Stage stageBox2 = new Stage();
        stageBox2.initModality(Modality.NONE);
        stageBox2.setOpacity(1);
        stageBox2.setTitle("Controller2");
        Scene sceneBox2 = new Scene(rootBox2);
        sceneBox2.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
        stageBox2.setScene(sceneBox2);
        stageBox2.show();
    }

    public String getTextField1Data() {
        return textField1.getText();
    }

    public void setFromModelTapped() {
        textField1.setText(model.returnString());
    }

}
3. Controller2.java
Java:
package application;

import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;

public class Controller2 implements IModelInjectable, IController1Injectable {

    public Controller2() {
        super();
    }

    private Model model;

    @Override
    public void setModel(Model model) {
        this.model = model;
    }

    @FXML
    private Controller1 controller1;

    @Override
    public void setController1(Controller1 controller1) {
        this.controller1 = controller1;
    }

    @FXML
    VBox box2;

    @FXML
    TextField textField2;

    @FXML
    Button setLabel;

    @FXML
    Button setFromModel;

    public void initialize() {
        setInfoTextField2();
    }

    @FXML
    public void setInfoTextField2() {
        textField2.setText(controller1.getTextField1Data());
    }

    @FXML
    public void setFromModelTapped() {
        textField2.setText(model.returnString());
    }

}
4. IModelInjectable.java
Java:
package application;

public interface IModelInjectable {
   
    void setModel(Model model);

}

5. IController1Injectable.java
Java:
package application;

public interface IController1Injectable {
    void setController1(Controller1 controller);

}

6. InjectionUtils.java
Java:
package application;

public class InjectionUtils {

    public static Object controllerForClass(Class<?> clazz, Model model, Controller1 controller1) {
        try {
            // controllerInstance#someArbitraryMethodCall if necessary
            // will be triggered *before* the initialize method!
            Object controllerInstance = clazz.newInstance();
            // Set Model using Setter in Controller1
            if (IModelInjectable.class.isAssignableFrom(clazz)) {
                IModelInjectable c = (IModelInjectable) controllerInstance;
                c.setModel(model);
            }

            if (IController1Injectable.class.isAssignableFrom(clazz)) {
                IController1Injectable c = (IController1Injectable) controllerInstance;
                c.setController1(controller1);
            }
            return controllerInstance;
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
            return null;
        }
    }

    public static Object controllerForClass(Class<?> clazz, Model model) {
        try {
            // controllerInstance#someArbitraryMethodCall if necessary
            // will be triggered *before* the initialize method!
            Object controllerInstance = clazz.newInstance();
            // Set Model using Setter in Controller1
            if (IModelInjectable.class.isAssignableFrom(clazz)) {
                IModelInjectable c = (IModelInjectable) controllerInstance;
                c.setModel(model);
            }
            return controllerInstance;
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
            return null;
        }
    }

}

Nicht sicher bin ich mir ob es so richtig ist die beiden controllerForClass() Methoden so zu implementieren. In der Main injecte ich ja nur das Model in Controller1, in Controller injecte ich dann Controller1 und Model in Controller2.

Wenn das aus Eurer und somit JavaFX Sicht so richtig ist würde ich das als Blueprint nehmen, da dieser Fall ja vermutlich häufiger benötigt wird wenn man MVC verwendet.

Gruß
Ralf
 
@Robat wie immer sehr schön. Einzig andere Bezeichner würde ich wählen. Evtl. ControllerFactories für die Klasse und für die Methode newFactory.
Die Klasse gibt doch Controller und nicht ControllerFactories zurück?

Ich würd das noch ändern:
Java:
public static <T> T controllerForClass(Class<T> clazz, Model model) {
 
OK, ich habe mal irgendwo gelesen/gehört das man Interfaces mit "I" anfangen lässt - da ich das aber auch nicht schön finde werde ich au den Rat der Spezis hören und es umbenennen 🙂
Gruß
Ralf
 
@ralfb1105 man kann das ganze Spielchen auch noch weiter treiben und für
Java:
FXMLLoader loaderBox1 = new FXMLLoader();
// Use Factory to inject Model in Controller1
loaderBox1.setControllerFactory(clazz -> InjectionUtils.controllerForClass(clazz, model));
loaderBox1.setLocation(getClass().getResource("Box1.fxml"));
this.controller1 = loaderBox1.getController(); // controller aus dem Loader bekommen
Parent rootBox1 = loaderBox1.load();
eine eigene Klasse/Methode schreiben damit du dich nicht immer damit rumschlagen musst. Was du machst ist immer das selbe: FXML File als Ressource laden, ControllerFactory setzen und den Parent laden. Das könnte beispielhaft so aussehen:
Java:
public class ViewLoader {
    public static Parent load(String fxmlPath, (Callback<Class<?>,Object> controllerFactory) {
      Parent parent = null;   
      try {
             FXMLLoader loader = new FXMLLoader();
             loader.setControllerFactory(controllerFactory);
             loader.setLocation(ViewLoader.class.getResource(fxmlPath);
             parent = loader.load();
        } catch(...) { 
             ....
        }
        return pane;
    }
}
Verwenden kannst du das ganze dann wie fogt. Das macht den Code mEn wieder einiges leserlicher und du vermeidest doppelten Code.
Java:
Parent root = ViewLoader.load("Box1.fxml", clazz ->ControllerFactory.controllerForClass(clazz, model));
Scene scene = new Scene(root);
...
 
Den doppelten Code in der Factory kann man auch vermeiden.
Java:
public class ControllerFactory {
    private static <T> T controllerForClass(Class<T> clazz, Model model, Controller1 controller1) {
        try {
            Object controllerInstance = clazz.newInstance();
            if (model != null && IModelInjectable.class.isAssignableFrom(clazz)) {
                IModelInjectable c = (IModelInjectable) controllerInstance;
                c.setModel(model);
            }

            if (controller1 != null && IController1Injectable.class.isAssignableFrom(clazz)) {
                IController1Injectable c = (IController1Injectable) controllerInstance;
                c.setController1(controller1);
            }
            return controllerInstance;
        } catch (... {
            e.printStackTrace();
            return null;
        }
    }

    public static <T> T controllerForClass(Class<T> clazz, Model model) { 
        return controllerForClass(clazz, model, null); 
    }
    public static <T> T controllerForClass(Class<T> clazz, Model model, Controller1 controller1) { 
        return controllerForClass(clazz, model, controller1); 
    }
}
 
Hallo mrBrown,

dann habe ich jetzt den Ehrgeiz das wirklich schön zu machen 🙄
Ich habe in der Klasse InjectionUtils den generischen Type <T> für die Methode(n) gewählt:
Java:
public static <T> T controllerForClass(Class<T> clazz, Model model, Controller1 controller1) {
        try {
...
}

Was genau meinst Du mit
Einzig andere Bezeichner würde ich wählen. Evtl. ControllerFactories für die Klasse und für die Methode newFactory.

Die Klasse ist ja jetzt "InjectionUtils" - das ist doch so OK - oder?
Soll heißen der Bezeichner für die Methode "controllerForClass" ist zu ändern?

Gruß
Ralf
 
Hallo Robat,

in Deiner beschriebenen ViewLoader Klasse gibt es einen Syntax Fehler den ich leider nicht finde da mir die Syntax mit der Lambda Expression nicht geläufig ist ... bzw. ich es nicht verstehe ...
Java:
public class ViewLoader {
    public static Parent load(String fxmlPath, (Callback<Class<?>,Object> controllerFactory) {

}

Gruß
Ralf
 
Hallo Robat, mrBrown, mihe7,

Jetzt habe ich versucht alle Optimierungs Vorschäge einzubauen, und hier nun das Ergebnis. Ich poste jetzt noch einmal alle Klassen, falls es mal jemand braucht muss er sich nicht mühsam durch die mittlerweile 5 Seiten wühlen.
1. Main.java
Java:
package application;

import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Parent;
import javafx.scene.Scene;

public class Main extends Application {

    private Model model = new Model();

    @Override
    public void start(Stage primaryStage) throws Exception {
        try {
            Parent rootBox1 = ViewLoader.load("Box1.fxml", clazz -> ControllerFactory.controllerForClass(clazz, model));
            Scene scene = new Scene(rootBox1);
            scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
            primaryStage.setScene(scene);
            primaryStage.setTitle("Controller1");
            primaryStage.show();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        launch(args);
    }
}
2. Controller1.java
Java:
package application;

import java.io.IOException;
import javafx.fxml.FXML;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.stage.Modality;
import javafx.stage.Stage;

public class Controller1 implements ModelInjectable {

    private Model model;

    @Override
    public void setModel(Model model) {
        this.model = model;
    }

    @FXML
    VBox box1;

    @FXML
    private TextField textField1;

    @FXML
    Button startCtrl2;

    @FXML
    Button setFromModel;

    public void initialize() {
        textField1.setText("The quick brown fox jumps over the lazy dog");
    }

    public void startCtrl2() throws IOException {
        Parent rootBox2 = ViewLoader.load("Box2.fxml",
                clazz -> ControllerFactory.controllerForClass(clazz, model, this));
        Stage stageBox2 = new Stage();
        stageBox2.initModality(Modality.NONE);
        stageBox2.setOpacity(1);
        stageBox2.setTitle("Controller2");
        Scene sceneBox2 = new Scene(rootBox2);
        sceneBox2.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
        stageBox2.setScene(sceneBox2);
        stageBox2.show();
    }

    public String getTextField1Data() {
        return textField1.getText();
    }

    public void setFromModelTapped() {
        textField1.setText(model.returnString());
    }

}

3. Controller2.java
Java:
package application;

import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;

public class Controller2 implements ModelInjectable, Controller1Injectable {

    private Model model;

    @Override
    public void setModel(Model model) {
        this.model = model;
    }

    @FXML
    private Controller1 controller1;

    @Override
    public void setController1(Controller1 controller1) {
        this.controller1 = controller1;
    }

    @FXML
    VBox box2;

    @FXML
    TextField textField2;

    @FXML
    Button setLabel;

    @FXML
    Button setFromModel;

    public void initialize() {
        setInfoTextField2();
    }

    @FXML
    public void setInfoTextField2() {
        textField2.setText(controller1.getTextField1Data());
    }

    @FXML
    public void setFromModelTapped() {
        textField2.setText(model.returnString());
    }

}

4. ModelInjectable.java
Java:
package application;

public interface ModelInjectable {

    void setModel(Model model);

}

5. ControllerInjectable.java
Java:
package application;

public interface Controller1Injectable {
    void setController1(Controller1 controller);

}

6. ControllerFactory.java
Java:
package application;

public class ControllerFactory {

    public static <T> T controllerForClass(Class<T> clazz, Model model, Controller1 controller1) {
        try {
            // controllerInstance#someArbitraryMethodCall if necessary
            // will be triggered *before* the initialize method!
            T controllerInstance = clazz.newInstance();
            // Set Model using Setter in Controller1
            if (ModelInjectable.class.isAssignableFrom(clazz)) {
                ModelInjectable c = (ModelInjectable) controllerInstance;
                c.setModel(model);
            }

            if (Controller1Injectable.class.isAssignableFrom(clazz)) {
                Controller1Injectable c = (Controller1Injectable) controllerInstance;
                c.setController1(controller1);
            }
            return controllerInstance;
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
            return null;
        }
    }

    public static <T> T controllerForClass(Class<T> clazz, Model model) {
        return controllerForClass(clazz, model, null);
    }

}

7. ViewLoader.java
Java:
package application;

import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.util.Callback;

public class ViewLoader {

    public static Parent load(String fxmlPath, Callback<Class<?>, Object> controllerFactory) {

        Parent parent = null;
        try {
            FXMLLoader loader = new FXMLLoader();
            loader.setControllerFactory(controllerFactory);
            loader.setLocation(ViewLoader.class.getResource(fxmlPath));
            parent = loader.load();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return parent;
    }

}

Ich hoffe das ist jetzt so wie Ihr Euch das vorgestellt habt.

Stand jetzt habe ich zugegeben nicht alles verstanden, gerade das Thema mit der Lambda Expression und wie man die Controller Instanzen ermittelt/übergibt erschließt sich mir noch nicht in vollem Umfang.

Dennoch hat mich diese Session wiedet ein großes Stück nach vorn gebracht und ich bin froh das Ihr so gedulig geholfen habt und wir auch nach der 1. lauffähigen Version weiter an der Optimierung gearbeitet haben.

PS: Falls noch etwas auffällt - bin für jede Änderung bereit 😉

Gruß

Ralf
 
Alter Schwede!

Da verbringt man mal nen Abend mit Game of Thrones und prügelt sich am Vormittag mit seinem Laptop rum und schon sind hier 5 (!!!) Seiten! 😱😀

Ich sehe, mein Rat ist nicht mehr nötig, aber aus Prinzip habe ich jetzt noch was zusammengeschraubt, was sich ein wenig (in Details) von eurer Lösung unterscheidet. Hier mal der Startpunkt:
https://github.com/bgmf/poc/blob/ma...dzim/tests/fx/MainWithControllerFactory2.java

Die grundsätzliche Rangehensweise bleibt gleich. Aber ich habe die Tatsache ausgenutzt, dass in FXML definierte UIs den selben FXMLLoader wie das vom Parent-UI verwenden. Also fällt die Notwendigkeit der ControllerFactory-Utils schon mal weg.
Und da ihr die reine Interface-Variante schon hattet, hab ich das jetzt mit der Pösen Reflection-Black-Magic gemacht.

FXML "FactoryInjection.fxml"
HTML:
<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.VBox?>
<VBox xmlns="http://javafx.com/javafx"
      xmlns:fx="http://javafx.com/fxml"
      fx:controller="eu.dzim.tests.fx.controller.FactoryInjectionController" spacing="5">

    <fx:define>
        <fx:include fx:id="view2" source="FactoryInjection2.fxml"/>
    </fx:define>

    <Label fx:id="counterLabel" text="Increase"/>

    <fx:reference source="view2"/>

</VBox>

FXML "FactoryInjection2.fxml"
HTML:
<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.VBox?>
<VBox xmlns="http://javafx.com/javafx"
      xmlns:fx="http://javafx.com/fxml"
      fx:controller="eu.dzim.tests.fx.controller.FactoryInjectionController2" spacing="5">

    <Button fx:id="increaseButton" text="Increase" onAction="#increase"/>

</VBox>

Entsprechend "FactoryInjectionController":
Java:
package eu.dzim.tests.fx.controller;

import eu.dzim.tests.fx.MainWithControllerFactory2;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;

public class FactoryInjectionController implements MainWithControllerFactory2.ModelInjector {

    @FXML private VBox view2;
    @FXML private Object view2Controller;

    @FXML private Label counterLabel;

    private MainWithControllerFactory2.Model model = null;

    @Override
    public void setModel(MainWithControllerFactory2.Model model) {
        System.out.println(getClass().getName() + " is receiving model.");
        this.model = model;
    }

    @FXML
    private void initialize() {
        System.out.println(getClass().getName() + " is initializing.");
        System.out.println("has a sub-view: " + view2 + " -> " + view2Controller);
        counterLabel.textProperty().bind(model.counterProperty().asString());
    }

}

und "FactoryInjectionController2":
Java:
package eu.dzim.tests.fx.controller;

import eu.dzim.tests.fx.MainWithControllerFactory2;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;

public class FactoryInjectionController2 implements MainWithControllerFactory2.ModelInjector {

    @FXML private Button increaseButton;

    private MainWithControllerFactory2.Model model = null;

    @Override
    public void setModel(MainWithControllerFactory2.Model model) {
        System.out.println(getClass().getName() + " is receiving model.");
        this.model = model;
    }

    @FXML
    private void initialize() {
        System.out.println(getClass().getName() + " is initializing.");
    }

    @FXML
    private void increase(ActionEvent actionEvent) {
        model.increaseCounter();
        actionEvent.consume();
    }
}

Und hier der Glue-Code, die Main:
Java:
package eu.dzim.tests.fx;

import javafx.application.Application;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class MainWithControllerFactory2 extends Application {

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

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

        // Premise: We have a single and shared model, that will be used across the application
        // our second UI (sub-UI of the first one) will get the model and increase a counter
        // the first UI, loaded via the FXMLLoader, will display the counters value
        Model model = new Model();

        // our loader get's the shiny controller factory

        // we could use the interface mechanism, but to make it a bit different then the other solutions, we use reflection black-magic
        // although neat, note, that I don't need to generify the controller factory

        // since I will use the FXMLs sub-view mechanism (see FactoryInjection.fxml), the loader is inherited,
        // so all directly defined sub-UIs will not need to specify their own FXMLLoader with controller factory

        FXMLLoader loader = new FXMLLoader();
        loader.setControllerFactory(c -> controllerForClassWithName(c, "setModel", model));
        loader.setLocation(getClass().getResource("/fxml/FactoryInjection.fxml"));
        Parent root = loader.load();
        Scene scene = new Scene(root, 200, 100);

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

    // private Object controllerForClassWithInterface(Class<?> clazz, Model model) {
    //     try {
    //         Object controllerInstance = clazz.newInstance();
    //
    //         if (ModelInjector.class.isAssignableFrom(controllerInstance.getClass()))
    //             ((ModelInjector) controllerInstance).setModel(model);
    //
    //         return controllerInstance;
    //     } catch (InstantiationException | IllegalAccessException e) {
    //         e.printStackTrace();
    //         return null;
    //     }
    // }

    private Object controllerForClassWithName(Class<?> clazz, String inheritedMethodName, Object data) {
        try {
            Object controllerInstance = clazz.newInstance();

            try {
                Method method = clazz.getDeclaredMethod(inheritedMethodName, data.getClass());
                boolean accessible = method.isAccessible();
                method.setAccessible(true);
                method.invoke(controllerInstance, data);
                method.setAccessible(accessible);
            } catch (NoSuchMethodException | InvocationTargetException e) {
                e.printStackTrace();
            }

            return controllerInstance;
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
            return null;
        }
    }

    public final class Model {

        private final IntegerProperty counter;

        public Model() {
            counter = new SimpleIntegerProperty(this, "counter", 0);
        }

        public int getCounter() {
            return counter.get();
        }

        public IntegerProperty counterProperty() {
            return counter;
        }

        public void setCounter(int counter) {
            this.counter.set(counter);
        }

        public void increaseCounter() {
            counter.set(counter.get() + 1);
        }
    }

    public interface ModelInjector {
        void setModel(Model model);
    }
}

Die Controller verwenden noch das Interface, ist aber eher aus Bequemlichkeit da, man könnte es - und das @Override - auch löschen und trotzdem ginge es.
 
@ralfb1105 zu dem Lambda
Java:
        // am Ende:
        loader.setControllerFactory(c -> controllerForClassWithName(c, "setModel", model));
        // ist das selbe wie:
        loader.setControllerFactory(new Callback<Class<?>, Object>() {
            @Override
            public Object call(Class<?> param) {
                return controllerForClassWithName(param, "setModel", model);
            }
        });
        // vorausgesetzt: Das Interface, das man verwenden möchte, hat nur eine Methode (@FunctionalInterface)
        // (default-Methode zählen hier interessanterweise nicht)

Sehr vereinfacht: Ein Lambda (oder auch Methodenreferenzen) ersetzt eine Anonyme Klasse. Mehr muss man eigentlich IMHO fast nicht wissen, denn der grosse Pluspunkt ist am Ende, dass sie den Code übersichtlicher gestalten (können). Wobei man das bei grösseren Stream-Operationen jetzt gern mal in Frage stellen kann.

(Hinweis: Im Bytecode unterscheiden sich Lambda und Anonyme Klassen allerdings schon - dort wird das invokeDynamic flag, oder so, genutzt. Aber so tief steck ich nicht in allen JVM-Details drin 😛)
 
Hallo dzim,

erst einmal Danke für die Erklärung zu Lambda - das werde ich mir noch einmal ganz in Ruhe ansehen.
Schön das wir jetzt zwei Lösungen haben 🙂 wobei ich nach dem heutigen Tag im Monet nicht die Muße habe Deine mal zu testen.

Ich hoffe aber Du kannst mir vielleicht bei einem weiteren problem helfen was ich mit der Lösung von Robat habe - wobei es nicht an der Lösung liegt sondern an meinem zu wenigen Know-How 🙁

Problem:
In meiner Applikation, in der ich den Blueprint von heute gerne implementieren möchte, habe ich jetzt die Problematik, das ich in der Main über einen Setter die primaryStage an den Controller übergebe, damit ich dort dann diese beim drücken des Exit Button schließen kann.

Jetzt habe ich das ganze FXMLLoader aber in die ViewLoader Class ausgelagert und verwende die ControllerFactory Klasse. Nun weiß ich nicht wie ich in der Main, oder wahrscheinlich sinnvoller in der ControllerFactory den Setter für die Stage aufrufen soll 🙁

Hier mal der Code:

Im Controller.java
Java:
public void setStage(Stage stage) {
        mainStage = stage;
        mainStage.setOnCloseRequest(event -> {
            db.removeExceptionListener();
            LOGGER.info("Close JavaFX Stage: " + stage.getTitle());
            LOGGER.info("****** Exit OraSimpleConnect FX application ******");
        });
    }

Vor dem heutigen Tag hatte ich das in der Main wie folgt aufgerufen:
Java:
public void start(Stage primaryStage) {
        try {
            FXMLLoader loader = new FXMLLoader(getClass().getResource("main.fxml"));
            Parent root = loader.load();
            Controller controller = loader.<Controller>getController();
            controller.setStage(primaryStage);
            Scene scene = new Scene(root);
            scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
            primaryStage.setScene(scene);
            primaryStage.setTitle("OraSimpleConnectFX");
            primaryStage.setResizable(false);
            primaryStage.show();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

Den Controller geholt und dann mit
Java:
controller.setStage(primaryStage);
den Setter aufgerufen.

Jetzt habe ich in der Main nach nur noch:
Java:
Parent root = ViewLoader.load("main.fxml", clazz -> ControllerFactory.controllerForClass(clazz, model));

Wie setze ich denn nun die Stage im Controller? Ich habe es mal mit übergeben in die ControllerFactory probiert, bin aber mit NPE gescheitert, vermute aber das dies der Weg sein müsste ...

Gruß

Ralf
 
Hallo mrBrown,

sorry, aber da musst Du für mich etwas weiter ausholen 😉
Ich habe eine Methode load() vom Type Parent und vielen Parametern, von denen ich nicht alle im Detail verstehe.
Diese Methode gibt doch ein Parent parent zurück welches ich für die scene benötige.

Wie muss ich jetzt
Du kannst load einfach ein Pair<Parent, T> zurückgeben lassen, wobei T der Controller ist

das verstehen? Eine neue Methode ? Hättest Du vielleicht einen Code Schnipsel der es mir etwas klarer macht?

Gruß
Ralf
 

Zurück
Oben