JavaFX JavaFX in mehrere Controller

Diskutiere JavaFX in mehrere Controller im AWT, Swing, JavaFX & SWT Bereich.
M

macaubas

Hallo,
ich definiere FXML innerhalb eines FXLM-Files. Das funktioniert recht gut mit
<fx:include fx:id="xxx" source="FXMLxxx.fxml" />
Beim Zugriff auf Nodes von diesem auf auf weitere FXML hatte ich erhebliche Probleme.
Die Lösung vom User Knoten (JavaFX in mehrere Controller aufteilen) funktioniert, scheint mir aber recht mühsam mit 100 und mehr Nodes.
Deshalb habe ich eine andere Lösung gesucht, welche ich hier zur Diskussion stellen will.
meine Applikation:
- ich öffne mehrere gleiche Fenster (haben den gleichen Controller)
- ich öffne verschiedene Fenster (haben verschiedene Controller)

da die FXLM in der Reihenfolge FXMLTab1, FXMLTab2, FXMLMain abgearbeitet werden generiere ich nach dem laden von ein Event auf Tab1 und Tab2.
Damit habe ich auch alle Components in Tab1 und Tab2.

Vielleicht hat jemand von euch eine bessere Lösung ??

FXMLMain.xml:
XML:
<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Tab?>
<?import javafx.scene.control.TabPane?>
<?import javafx.scene.layout.AnchorPane?>

<AnchorPane fx:id="AnchorPane" id="AnchorPane" prefHeight="329.0" prefWidth="308.0" xmlns="http://javafx.com/javafx/8.0.60"
            xmlns:fx="http://javafx.com/fxml/1"   fx:controller="mytest.FXMLMainController" >
   <children>
      <TabPane layoutX="26.0" layoutY="10.0" prefHeight="200.0" prefWidth="200.0" tabClosingPolicy="UNAVAILABLE">
        <tabs>
          <Tab text="tab1">
            <content>
              <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="106.0" prefWidth="200.0">
                     <children>
                       <fx:include fx:id="tab1" source="FXMLTab1.fxml" /> 
                     </children>
                  </AnchorPane>
            </content>
          </Tab>
          <Tab text="tab2">
            <content>
              <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0">
                     <children>
                      <fx:include fx:id="tab2" source="FXMLTab2.fxml" /> 
                     </children>
                  </AnchorPane>
            </content>
          </Tab>
        </tabs>
      </TabPane>
      <Button fx:id="but_main" layoutX="126.0" layoutY="290.0" mnemonicParsing="false" onAction="#do_something" text="mainbutton" />
   </children>
</AnchorPane>
FXMLMainController.java:
Java:
package mytest;

import java.net.URL;
import java.util.HashMap;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.AnchorPane;

public class FXMLMainController implements Initializable {

    private HashMap<String, Node> Comps = new HashMap<>();
    @FXML private AnchorPane AnchorPane;

    @Override
    public void initialize(URL url, ResourceBundle rb) {
        Comps = (HashMap) Cla1.LeseComps(AnchorPane).clone();
        ((Button) Comps.get("B1")).fire();
        ((Button) Comps.get("B2")).fire();
    }

    @FXML
    private void do_something(ActionEvent event) {
    ((Label)Comps.get("label1")).setRotate(((Label)Comps.get("label1")).getRotate()+20);    
    }
}
FXMLTab1.fxml:
XML:
<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.AnchorPane?>

<AnchorPane id="AnchorPane" fx:id="tab1" prefHeight="126.0" prefWidth="127.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="mytest.FXMLTab1Controller">
   <children>
      <Label fx:id="label1" layoutX="44.0" layoutY="14.0" prefHeight="17.0" prefWidth="104.0" text="Label" />
      <Button fx:id="B1" layoutX="14.0" layoutY="87.0" mnemonicParsing="false" onAction="#ini_Comp" text="B1" />
      <TextField fx:id="text1" layoutX="14.0" layoutY="51.0" onAction="#rot_but_main" prefHeight="25.0" prefWidth="126.0" text="Text" />
   </children>
</AnchorPane>
FXMLTab1Controller.java
Java:
package mytest;

import java.net.URL;
import java.util.HashMap;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;

public class FXMLTab1Controller implements Initializable {

    @FXML private Button B1;
    @FXML private TextField text1;
    private HashMap<String, Node> Comps = new HashMap<>();

    @Override
    public void initialize(URL url, ResourceBundle rb) {
        B1.setVisible(false);
    }
    @FXML private void ini_Comp(ActionEvent event) {
        Comps = (HashMap) Cla1.Comps.clone();
    }
    @FXML private void rot_but_main(ActionEvent event) {
        ((Button) Comps.get("but_main")).setRotate(((Button) Comps.get("but_main")).getRotate() + 20);
    }
}
FXMLTab2.fxml:
XML:
<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.control.CheckBox?>
<?import javafx.scene.layout.AnchorPane?>

<AnchorPane id="AnchorPane" fx:id="tab2" prefHeight="135.0" prefWidth="118.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="mytest.FXMLTab2Controller">
   <children>
      <CheckBox fx:id="checkbox1" layoutX="23.0" layoutY="25.0" mnemonicParsing="false" onAction="#rot_text1" text="CheckBox" />
      <Button fx:id="B2" layoutX="23.0" layoutY="83.0" mnemonicParsing="false" onAction="#ini_Comp" text="B2" />
   </children>
</AnchorPane>
FXMLTab2Controller.java
Java:
package mytest;

import java.net.URL;
import java.util.HashMap;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.TextField;

public class FXMLTab2Controller implements Initializable {

    @FXML private Button B2;
    @FXML private CheckBox checkbox1;
    private HashMap<String, Node> Comps = new HashMap<>();

    @Override
    public void initialize(URL url, ResourceBundle rb) {
        B2.setVisible(false);
    }
    @FXML private void ini_Comp(ActionEvent event) {
        Comps = (HashMap) Cla1.Comps.clone();
    }
    @FXML private void rot_text1(ActionEvent event) {
        ((TextField) Comps.get("text1")).setRotate(((TextField) Comps.get("text1")).getRotate() + 20);
    }
}
Cla1.java
Java:
package mytest;
import java.util.HashMap;
import javafx.scene.Node;
import javafx.scene.control.SplitPane;
import javafx.scene.control.TabPane;
import javafx.scene.control.TextArea;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;

public class Cla1 {

    static public HashMap<String, Node> Comps = new HashMap<>();

    public static HashMap LeseComps(Node N) {
        Comps.clear();
        LeseCompsSub(N);
        return Comps;
    }

    private static void LeseCompsSub(Node N) {
        int anz = ((AnchorPane) N).getChildren().size();
        for (int i = 0; i < anz; i++) {
            Node N1 = ((AnchorPane) N).getChildren().get(i);
            switch (N1.getClass().getSimpleName()) {
                case "AnchorPane": {
                     LeseCompsSub(((AnchorPane) N1));
                    break;
                }
                case "TabPane": {
                    int CNT = ((TabPane) N1).getTabs().size();
                    for (int j = 0; j < CNT; j++) {
                        LeseCompsSub((((TabPane) N1).getTabs().get(j)).getContent());
                        //Comps = LeseCompsSub((((TabPane) ((AnchorPane) N).getChildren().get(i)).getTabs().get(j)).getContent(), Comps);    

                    }
                    break;
                }
                case "SplitPane": {
                    int CNT = ((SplitPane) N1).getItems().size();
                    for (int j = 0; j < CNT; j++) {
                        LeseCompsSub(((SplitPane) N1).getItems().get(j));
                    }
                    break;
                }
                case "VBox": {
                    int CNT = ((VBox) N1).getChildren().size();
                    for (int j = 0; j < CNT; j++) {
                        if (((VBox) N1).getChildren().get(j).getClass().getSimpleName().equals("AnchorPane")) {
                            LeseCompsSub(((VBox) N1).getChildren().get(j));
                        } else {
                            Comps.put(((VBox) N1).getChildren().get(j).getId(), ((VBox) N1).getChildren().get(j));
                        }
                    }
                    break;
                }
                case "HBox": {
                    int CNT = ((HBox) N1).getChildren().size();
                    for (int j = 0; j < CNT; j++) {
                        if (((HBox) N1).getChildren().get(j).getClass().getSimpleName().equals("AnchorPane")) {
                            LeseCompsSub(((HBox) N1).getChildren().get(j));
                        } else {
                            Comps.put(((HBox) N1).getChildren().get(j).getId(), ((HBox) N1).getChildren().get(j));
                        }
                    }
                    break;
                }
                default:
                    Comps.put(((AnchorPane) N).getChildren().get(i).getId(), ((AnchorPane) N).getChildren().get(i));
                    switch (((AnchorPane) N).getChildren().get(i).getClass().getSimpleName()) {
                        case "TextArea": {
                            ((TextArea) ((AnchorPane) N).getChildren().get(i)).setText("gaga");
                            break;
                        }
                    }
            }
        }
    }
}
 
mrBrown

mrBrown

Was für ein Problem versuchst du denn zu lösen?


Der Code sieht schon sehr ... schrecklich aus. Grad wenn das größer wird stell ich mir das ziemlich unwartbar vor
 
mrBrown

mrBrown

Hier mal ein Beispiel wie man das sauberer lösen kann: https://gitlab.com/mrBrown/fxml-demo-1

Im wesentlichen:
  • Zustand der View (zB die Rotation) wird in einem View-Model gehalten
  • Abhängige Controls binden sich jeweils daran (direkt in FXML)
  • Actions verändern das View-Model
  • Die Controller müssen jeweils nur das ViewModel (und das kann man noch aufteilen und durch Interfaces abstrahieren) und ihre eigenen Controls kennen
Du braucht dann "Cla1" nicht mehr und man muss nicht mehr wild mit Strings und Casts hantieren.
 
M

macaubas

Hallo MrBrown,
ich danke dir erstmals für deinen Einsatz hier.
Meine Statements zum Rotieren waren lediglich zur Visualisierung der richtigen Funktionalität gedacht, machen ja sonnst wenig Sinn.
Ich habe bei meiner Applikation rund 40 verschiedene Fenster (wie FXMLMains.fxml) und hier innerhalb von SplitPanes je 25 verschiedene included Panes (wie FXMLTab1.fxml) mit ca 60 Controls (Labels, TexFields, ..) und 30 included Panes (wie FXMLTab2.fxml) mit ca 100 Controls.
Mit einem Iterator auf Comps kann ich nun einfach die Felder lesen/schreiben und eine DB manipulieren.
Bei deiner Lösung wären das ja wohl ca 45'000 Statements zu schreiben, macht 2 Monate Arbeit und 2 Monate Tests.
Ich bin ja auch nicht zufrieden, dass ich die Comps aus FXML-Controller nicht überall lesen kann und wäre für einen besseren Lösungsvorschlag glücklich.
Gruss Macaubas
 
L

lam_tr

Hallo MrBrown,
ich danke dir erstmals für deinen Einsatz hier.
Meine Statements zum Rotieren waren lediglich zur Visualisierung der richtigen Funktionalität gedacht, machen ja sonnst wenig Sinn.
Ich habe bei meiner Applikation rund 40 verschiedene Fenster (wie FXMLMains.fxml) und hier innerhalb von SplitPanes je 25 verschiedene included Panes (wie FXMLTab1.fxml) mit ca 60 Controls (Labels, TexFields, ..) und 30 included Panes (wie FXMLTab2.fxml) mit ca 100 Controls.
Mit einem Iterator auf Comps kann ich nun einfach die Felder lesen/schreiben und eine DB manipulieren.
Bei deiner Lösung wären das ja wohl ca 45'000 Statements zu schreiben, macht 2 Monate Arbeit und 2 Monate Tests.
Ich bin ja auch nicht zufrieden, dass ich die Comps aus FXML-Controller nicht überall lesen kann und wäre für einen besseren Lösungsvorschlag glücklich.
Gruss Macaubas
Hi Macaubas,

Ich habe eine andere Frage, wie lässt sich die Anwendung starten. Ist sie überhaupt noch startbar, so viele Fxmls wie du da hast, kann ich mir schon vorstellen dass es aufwendig sein kann.

Kannst du eventuell dein Anwendungsfall ein bisschen genauer erklären? Ich kann mir gerade nicht wirklich viel darunter vorstellen.

Grüße
Lam
 
mrBrown

mrBrown

Meine Statements zum Rotieren waren lediglich zur Visualisierung der richtigen Funktionalität gedacht, machen ja sonnst wenig Sinn.
Schon klar, ich hab die auch nur übernommen, weil du sie schon hattest (und halt zusätzlich etwas mehr eingebaut).

Mit einem Iterator auf Comps kann ich nun einfach die Felder lesen/schreiben und eine DB manipulieren.
Bei deiner Lösung wären das ja wohl ca 45'000 Statements zu schreiben, macht 2 Monate Arbeit und 2 Monate Tests.
Hast du die beiden Varianten mal verglichen?

  • Deine Lösung braucht mehr Code (in diesem Beispiel braucht deins knapp 50% mehr Code)
  • Da du Tests ansprichst: deine Lösung ist nicht sinnvoll mit Unit-Tests abdeckbar – alles greift auf eine gemeinsame statische Variable zu, die Units sind also das Gegenteil von unabhängig. In meinem sind einmal alle Controller unabhängig und einzeln test-/ausführbar, und zusätzlich ist die im ViewModel stehende Logik mit simplen Unit-Tests abdeckbar
  • Deine Lösung greift auf alle Felder statisch über die ID zu, die als String (also ohne statische Überprüfbarkeit) quer über das gesamte Programm verteilt sind – jede Klasse kennt also (implizit über IDs) alle anderen Klassen, das kann doch gar nicht skalieren und ist völlig unmodularisierbar
  • Deine Cla1 bricht schon, wenn man ein minimal anderes Layout wählt – einfach nur die FXML ein bisschen ändern, damit zwei Dinge nebeneinander statt untereinander stehen, schon wird direkt ein NullPointer beim Start geworfen
Also mMn im gesamten: kürzer, testbar, unabhängige Klassen (und damit modularisierbar), wartbarer und stabiler.

Vielleicht übersehe ich den großen Vorteil deiner Lösung (dein Beispiel versteckt den dann allerdings ziemlich gut ;) ) – vielleicht hast du noch mal ein anderes Beispiel oder eine Erklärung, wo deines einen Vorteil bietet?



(Oh, bevor ich's vergesse: da ich static kritisiere und trotzdem selber static für das ViewModel nutze: wie dabei steht gehört das per DI injiziert, wollte nur in das Beispiel jetzt kein Framework mit reinziehen, und das sollte natürlich nicht eine Klasse sein, sondern in X kohärente Klassen geteilt werden, auch aufgrund der Kürze (und der fehlenden Problemdomäne) nicht gemacht)
 
mrBrown

mrBrown

Ich habe eine andere Frage, wie lässt sich die Anwendung starten. Ist sie überhaupt noch startbar, so viele Fxmls wie du da hast, kann ich mir schon vorstellen dass es aufwendig sein kann.
In wie fern meinst du aufwändig?

Ob man jetzt eine oder zehn per include nutzt, macht doch keinen wirklichen Unterschied, zumindest aus Programmiersicht?
 
L

lam_tr

In wie fern meinst du aufwändig?

Ob man jetzt eine oder zehn per include nutzt, macht doch keinen wirklichen Unterschied, zumindest aus Programmiersicht?
Ich meine es von Javafx aus, da die Fxmls per Reflections eingelesen werden, ist das so oder so schon in der Regel langsamer wie Programmtische Vorgehensweise. Ich habe auch schon Anwendungen mit 2-3 includes mit komplexen formen gehabt, die Anwendung hatte auch um einiges länger gebraucht wie ohne includes.
 
dzim

dzim

Ich glaube, @lam_tr war wegen der erwähnung von dutzenden von "Fenstern" verwirrt. Würde mich auch verwirren, wenn eine Applikation so viel Fenster aufmacht. Ich glaube aber der OP meinte damit nur Views innerhalb eines OS-Fensters (win32, Cocoa, GTK, etc.).

Ich bin mittlerweile auch von FXMLs abgerückt. Sie sind praktisch, UI von Controller zu trennen, bringen aber im schlimmsten Fall auch Performanceeinbußen mit.
 
M

macaubas

Hallo lam_tr,
ich habe in JavaFX14, die Installation in diesem Forum mein Vorgehen beschrieben.
MacaubasMain.java beinhaltet das Menü welches beliebig erweitert werden kann. Window starteAx(xxx) startet je ein jfxtra-Fenster wie wir das z.B. von EXCEL kennen. Damit sind ja nicht alle Fenster gleichzeitig offen.
Wenn du nicht weiterkommst, so melde dich nochmals.
 
M

macaubas

Hallo mrBrown,
ich teile deinen Meinung absolut nicht. CLA1 ist weitgehend vollständig und kann bei völlig verschiedenen FXMLMain aufgerufen werden.
Leider ist sie statisch und ich muss sie mit Comps = (HashMap) Cla1.LeseComps(AnchorPane).clone(); kopieren. Dies deshalb, damit ich sie nochmals für Tab1 und Tab2 lesen kann. Somit ist die HashMap in FXMLMain hier nicht mehr statisch und für jedes Fenster eineindeutig.
Jedenfalls hat jeder HashMap-Key immer den korrekten Node.
- Bei mir erhalte ich keine Exception bei CLA1 bei massiv verschiedenen Layouts (warum auch).
- auch bei sehr vielen Controls wird der Code nicht wesentlich grösser da CLA1 so bleibt. Damit ist dein Code schon massiv aufwändiger.

Ich beanspruche hier ja keine Absolution, Das halten der HashMap in Tab1 und Tab2 gefällt mir ja auch nicht
 
mrBrown

mrBrown

Leider ist sie statisch und ich muss sie mit Comps = (HashMap) Cla1.LeseComps(AnchorPane).clone(); kopieren
Sie muss übrigens nicht statisch sein und auch das ganze Klonen kann man weglassen. Man kann die über ganz normale Methodenaufrufe dorthin reichen, wo man sie braucht. Das Klone kann man auch einfach weglassen – das ist vor allem nur Symptombekämpfung, weil jeder von überall drauf zugreifen kann, muss man defensive Kopien antworten.

Dies deshalb, damit ich sie nochmals für Tab1 und Tab2 lesen kann.
Sie wird doch gar nicht nochmal gelesen, nur kopiert? Und das passiert auf eine völlig umständliche Art und Weise.

In jeder Komponente liegt ein Button, den du direkt auf unsichtbar schaltest, damit du dann den Button aus einer anderen Komponente feuern kannst, um zum Start Code in dessen Event-Handler auszuführen. Von hinten durch die Brust in's Auge wäre noch eine mehr als nette Umschreibung


- Bei mir erhalte ich keine Exception bei CLA1 bei massiv verschiedenen Layouts (warum auch).
Weil deine CLA1 Fehler enthält. Mit der von dir hier geposteten wirft es zB mit diesem FXMLTab1.fxml Fehler:
Java:
<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<AnchorPane id="AnchorPane" fx:id="tab1" prefHeight="126.0" prefWidth="127.0" xmlns="http://javafx.com/javafx/8"
            xmlns:fx="http://javafx.com/fxml/1" fx:controller="mytest.FXMLTab1Controller">
    <HBox>
        <VBox>
            <Label fx:id="label1" layoutX="44.0" layoutY="14.0" prefHeight="17.0" prefWidth="104.0" text="Label"/>
            <Button fx:id="B1" layoutX="14.0" layoutY="87.0" mnemonicParsing="false" onAction="#ini_Comp" text="B1"/>
        </VBox>
        <TextField fx:id="text1" layoutX="14.0" layoutY="51.0" onAction="#rot_but_main" prefHeight="25.0" prefWidth="126.0" text="Text"/>
    </HBox>
</AnchorPane>
Natürlich kann man die Fehler alle relativ einfach beheben – allerdings kannst du nichts davon sinnvoll mit automatisierten Unit-Tests abdecken.

- auch bei sehr vielen Controls wird der Code nicht wesentlich grösser da CLA1 so bleibt. Damit ist dein Code schon massiv aufwändiger.
Wenn man Controls ohne Verhalten hinzufügt, wird der Code nicht größer. Und Überraschung, das ist bei meiner Variante genauso ;)

Mehr Code wird es erst, wenn Controls Verhalten haben sollen. Entsprechende Action-Handler braucht es dann in beiden Varianten.

Was einen Unterschied macht, ist die Logik. Bei dir steckt die direkt im Action-Handler und manipuliert direkt die entsprechenden Komponenten. Bei mir ist die Logik ausgelagert – braucht etwas mehr Code, hat aber eben auch Vorteile:

Deine Variante ist absolut nicht testbar. Die gesamte GUI muss aufgebaut werden, damit überhaupt irgendetwas getestet werden kann. Dann kann man das ganze nur über die GUI direkt testen, die Logik (in deinem Beispiel das drehen um 20 Grad) ist nicht unabhängig von der GUI – automatisiertes Testen der Einzelnen Komponenten nicht möglich.

In meiner Variante kann man dagegen jede einzelne Komponente einzeln testen: sowohl die Logik einzeln (Rotation wird erhöht um 20 Grad), die Kommunikation Button<->Logik (Klick auf den Button erhöht Rotation um 20 Grad), Kommunikation Label<->Logik (Änderung der Rotation rotiert den Button auch wirklich) und das gesamte (Klick auf Button rotiert Label).


BTW: deine Map kann man im wesentlichen auch durch Node#lookup ersetzen, man muss dann nur den Root rumreichen.
 
mrBrown

mrBrown

Um das aber noch einmal kurz zusammenzufassen, was für mich die Probleme bei der Lösung sind und Diskussionswerte Punkte sind, würde mich freuen wenn du jeweils drauf eingehst :)

  1. Absolut keine Möglichkeit für automatisierte Unit-Tests: Verhalten direkt in Action-Handlern manipuliert direkt die Componenten
  2. Geringe Wartbarkeit und Stabilität: kleine Änderungen brechen direkt das ganze Programm
  3. Keine Möglichkeit, das ganze zu modularisieren: alle Komponenten kennen sich über die ID gegenseitig, man kann sie also nicht voneinander trennen und zB unterschiedliche Views in unterschiedliche Module auslagern
  4. Und bedingt dadurch auch schlechte Skalierbarkeit: für so wenige Controls kann ich mir noch vorstellen, dass man den Überblick behält. Bei allerdings hunderten Views halte ich es zumindest für mich völlig unmöglich, so den Überblick zu behalten

Und besonders würde mich auch interessieren, wo du bei mir den massiv aufwändigeren Code siehst? Grad im Zusammenhang damit, dass du offensichtlich bei meiner Variante deutlich mehr Bedarf für Tests siehst, während in deiner Variante nicht mal eine Möglichkeit für sinnvolle Tests existiert. (Oder ich übersehe das, würde mich sehr interessieren wie du beide Varianten Testen würdest).
 
M

macaubas

Hallo MrBrown,
ich hatte nun endlich Zeit, deine Variante auszutesten.
Da funktioniert ja etwas wesentliches nicht. Ich benutze jfxtras und habe in meiner Applikation mehrere Fenster gleichzeitig offen (auch mit dem gleichen Controller). Es werden jeweils alle Controls gedreht und nicht nur jene im aktuelle Fenster. Das war bei meiner Variante ebenfalls so, gerade deshalb muss ich die Comps clonen, sonst habe ich den gleichen Effekt.
Aber du hattest recht, meine CLA1 hatte Fehler, deshalb nun hier ein neuer Anlauf mit verschachtelten HBox und VBox.

Die Lösung mit dem fire Event auf Tab1 und Tab2 gefällt mir natürlich auch nicht, vielleicht hat ja jemand anders eine besser Variante.

Aber ich danke dir für deinen Einsatz

die neue CLA1.java
Java:
public class Cla1 {

    static public HashMap<String, Node> Comps = new HashMap<>();

    public static HashMap LeseComps(Node N) {
        Comps.clear();
        LeseCompsSub(N);
        return Comps;
    }

    public static HashMap LeseCompsSub(Node N) {
        int CNT = 0;
        switch (N.getClass().getSimpleName()) {
            case "AnchorPane": {
                CNT = ((AnchorPane) N).getChildren().size();
                for (int i = 0; i < CNT; i++) {
                    LeseCompsSub(((AnchorPane) N).getChildren().get(i));
                }
                break;
            }
            case "HBox": {
                CNT = ((HBox) N).getChildren().size();
                for (int i = 0; i < CNT; i++) {
                    LeseCompsSub(((HBox) N).getChildren().get(i));
                }
                break;
            }
            case "VBox": {
                CNT = ((VBox) N).getChildren().size();
                for (int i = 0; i < CNT; i++) {
                    LeseCompsSub(((VBox) N).getChildren().get(i));
                }
                break;
            }
            case "TabPane": {
                CNT = ((TabPane) N).getTabs().size();
                for (int i = 0; i < CNT; i++) {
                    LeseCompsSub((((TabPane) N).getTabs().get(i)).getContent());
                }
                break;
            }
            case "SplitPane": {
                CNT = ((SplitPane) N).getItems().size();
                for (int i = 0; i < CNT; i++) {
                    LeseCompsSub(((SplitPane) N).getItems().get(i));
                }
                break;
            }
            default: {
                Comps.put(N.getId(), N);
                System.out.println(N.getId());
            }
        }
        return Comps;
    }
 
mrBrown

mrBrown

Schade, dass du auf keinerlei Kritikpunkte an deiner Lösung eingehst.

Da funktioniert ja etwas wesentliches nicht. Ich benutze jfxtras und habe in meiner Applikation mehrere Fenster gleichzeitig offen (auch mit dem gleichen Controller). Es werden jeweils alle Controls gedreht und nicht nur jene im aktuelle Fenster.
Wenn du mehrere unterschiedliche Zustände benötigst, ist die einfache Lösung: mehrere Zustände erstellen, und jedem Fenster seinen eigenen geben, das funktioniert völlig problemlos ;)

Die Lösung mit dem fire Event auf Tab1 und Tab2 gefällt mir natürlich auch nicht, vielleicht hat ja jemand anders eine besser Variante.
Wenn du mit einem anderen Controller was machen musst, greif doch einfach direkt auf den jeweiligen Controller zu. Der Umweg über eine Action ist wirklich in jeder Hinsicht unschön.

die neue CLA1.java
Was passiert eigentlich, wenn ich jetzt ein BorderPane einbaue? :)
 
M

macaubas

Hallo mrBrown,
ja ich werde meine Lösung sicher noch kritisch hinterfragen und zur gegebene Zeit hier schreiben, aber vorerst muss ich deine Lösung zum laufen bringen:

- ich habe da von einem Fenster mit gleichem Controller mehrere gleichzeitig offen, es könnten ja durchaus 10 sein. Ich habe da deinen Vorschlag
nicht verstanden, muss ich diese dann selber verwalten und wenn ja, wie ?

- die Comps in FXMLMainControlle ist bei mir nicht mehr static. Jedes Fenster hat dadurch seine eigene Comps. Deshalb kann ich von FXMLTab1Controller nicht darauf zugreifen. Habe ich da was falsches verstanden ?

- meine CLA1 ist sicher nicht vollständig, ist auf meine Bedürfnisse zugeschnitten. Jederman kann hier auch ergänzen.
Gruss
Macaubas
 
mrBrown

mrBrown

ja ich werde meine Lösung sicher noch kritisch hinterfragen und zur gegebene Zeit hier schreiben, aber vorerst muss ich deine Lösung zum laufen bringen:
In "meiner" Lösung gibts keine Comps ;)

- ich habe da von einem Fenster mit gleichem Controller mehrere gleichzeitig offen, es könnten ja durchaus 10 sein. Ich habe da deinen Vorschlag
nicht verstanden, muss ich diese dann selber verwalten und wenn ja, wie ?
Jeder Controller kann auf seine "Untercontroller" zugreifen. enauso wie es einen Baum aus Nodes gibt, gibt es auch einen Baum aus Controllern. Und die Kind-Controller kannst du, genauso wie Nodes, mit @FXML injecten lassen. Selber verwalten musst du da nicht wirklich was, das verknüpfen übernimmt JavaFX für dich.

- die Comps in FXMLMainControlle ist bei mir nicht mehr static. Jedes Fenster hat dadurch seine eigene Comps. Deshalb kann ich von FXMLTab1Controller nicht darauf zugreifen. Habe ich da was falsches verstanden ?
Nein, du musst jetzt halt selbst dafür sorgen, dass jeder Controller die entsprechenden Comps bekommt. Mit static fängst du dir nur Probleme ein, grad wenn du mit mehreren Fenstern arbeitest, ohne static zu arbeiten ist da die sinnvollere und saubere Lösung.
 
M

macaubas

Hallo mrBrown,
zuerst mal meine Ueberlegungen zu deinen Kritiken:

zu 1: Ich teste ja das ganze User-Interface. Wenn alles in Ordnung ist kann ich beliebige weitere Masken erstellen, die sind dann auch fehlerfrei. Anschliessend kann ich mich voll auf Applikation konsentrieren, denn hier ist meine wirkliche Herausforderung.
zu 2: das sehe ich absolut nicht so wie du.
zu 3: gerade das ist ja erwünscht. Die Komponenten in einer Instanz kennen sich gegenseitig, aber die Komponenten in anderen Instanzen nicht. Das habe ich ja auch wenn ich alles in einer FXML habe.

Aber nun zu meinem Problem:

Ich will immer noch deinem Vorschlag nachgehen und bin bis jetzt nicht weitergekommen.
Wenn ich die Controller mit z.B. "@FXML private FXMLTab1Controller fXMLTab1Controller;" aufführe habe ich ja nur Zugriff auf den Controller aber nicht auf die Instanzen darin. Somit dreht sich ja alles wieder im Kreis.
Vielleicht könntest du oder jemand anders mir mit einem einfachen Beispiel zeigen wie du auf Instanzen von Tab2 auf Instanzen von Tab1 zugreifts.
Schon mal Danke an alle, die mir eine Antwort haben.
Gruss Macaubas
 
J

JustNobody

Wenn du Zugriff auf Instanzvariablen haben willst, dann erstell Getter für diese oder mach sie nicht mehr private.
 
M

macaubas

Hallo JustNobody,
schön wärs, aber ich habe FXMLMain mit korrespondierenden FXMLTab1 und FXMLTab2 und jede hat gleichzeitig mehre Instanzen. Ich habe so mein Ziel nicht erreicht.
 
Thema: 

JavaFX in mehrere Controller

Passende Stellenanzeigen aus deiner Region:
Anzeige

Neue Themen

Anzeige

Anzeige
Oben