# Programmeinstellungen für Anwendung??



## MiMa (20. Feb 2018)

Nun  möchte ich für mein JavaFX Programm Einstellungen speichern. Nicht nur nur Position, Größe und der letzten geöffneten Dateien. Ich möchte auch Verzeichnispfade (File) speichern, sowie boolsche Werte für aktivierte und deaktivierte Funktionen also nicht nur Strings und Integer Werte.
Angefangen über Registry, ini, XML und Preferences habe ich Informationen gefunden. 
Leider ist mir nicht ganz klar wie Einstellungen überhaupt funktionieren?
Mir kommt es vor als wenn  die Informationen in einer Externen datei gespeichert werden und bei Bedarf von Werten aus dieser Datei geladen werden?
Oder macht man eine eigene Klasse mit zu speichernden informationen, erstellt ein Objekt und macht es durch weiterreichen verfügbar?
Auf keinen Fall kommen ini und Regsitry in Frage, da ich gerne Platformübergreifend bleiben möchte.
Über eine Erleuchtung würde ich mich freuen.
Danke
Mi


----------



## thet1983 (20. Feb 2018)

schau dir mal Properties an


----------



## MiMa (20. Feb 2018)

Danke, habe ich gemacht.
Mir erschließt sich nicht ob die Wertepaare in einem ungeordneten Speicher sowas wie globale Variablen sind? Ich habe keine Idee, wie ich mit properties vorgehen soll?
Die Werte sollen nicht nur mit der GUI Synchonisiert werden, die Einstellungen zum Programm auflistet und auch dort geändert werden können. Diese Werte sollte im gesamten Programm zur Verfügung stehen.
Properties in JavaFX kenne ich als eingepackte Datentypen in Objekte, die mit der GUI synchronisiert werden.


----------



## thet1983 (21. Feb 2018)

MiMa hat gesagt.:


> ob die Wertepaare in einem ungeordneten Speicher sowas wie globale Variablen sind?


je nachdem...


MiMa hat gesagt.:


> Ich habe keine Idee, wie ich mit properties vorgehen soll?


vor dem start laden?

entweder die ließt die datei immer wenn du etwas benötigst oder vor dem start alles in ein object speichern und dieses durch die application reichen...beim beenden kannst du es ja dann speichern


----------



## MiMa (21. Feb 2018)

Danke für die Info.
Das durchreichen hört sich ziemlich aufwendig an, da man auch berücksichtigen muss wenn sich das Objekt in einer Methode verändert wieder zurück zu geben.
Beim ändern in die Datei zuschreiben und bei bedarf von einem Ort die Werte zu laden, das hört sich ziemlich unkompliziert an. Jedoch habe ich keine Erfahrungen, ob sich die Leistung des Programms durch ständige Zugriffe auf das Dateisystem beeinträchtigt wird?


----------



## thet1983 (21. Feb 2018)

schau mal hier:
PROPERTIES TUTORIAL
oder du machst es mit einem Object per Serializable
oder per JAVA PREFERENCES

probier herum und schau was dir am besten passt.


----------



## MiMa (9. Jul 2019)

Hi,
ich weiß es ist schon etwas länger her, muss aber immer noch am dem Problem mit den Programmeinstellungen arbeiten.
Lange habe ich gebraucht mit JavaFX eine GUI auf FXML Basis zu erstellen und auch funktionales mit dem MVC Modell erlernen.
War für mich nicht einfach, habe lange im Netz und in Büchern verbracht.
Die Aktuelle Situation ist, das ich im Modell  StringProperties verwendet habe. 
Das Speichern mit Serialize speichern möchte.
Die Datei wird zwar geschrieben, gibt aber eine Fehlermeldung.

Mein log

```
INFO  start - Das Programm wird initialisiert init()-Methode
INFO  start - Das Programm-Hauptfenster wird initialisiert start()-Methode
INFO  kontroller.Hauptfenster - Einstellungen wurde im Menue ausgewaehlt
INFO  kontroller.Einstellungen - Einstellungen werden initialisiert
INFO  kontroller.Einstellungen - sucheQuellverzeichnis wurde aufgerufen
INFO  kontroller.Einstellungen - Selektiertes Quellverzeichnis StringProperty [value: B:\01 Quelle]
INFO  kontroller.Einstellungen - Methode einstellungenSpeichern wurde aufgerufen
INFO  modelle.Einstellungen - Es trat ein Fehler beim schreiben der Programmeinstellungen auf
INFO  kontroller.Einstellungen - einstellungenLaden wurde aufgerufen
INFO  kontroller.Einstellungen - Alle Werte im Fenster Einstellungen werden gelöscht
INFO  kontroller.Einstellungen - Alle Werte für Einstellungen werden von Festplatte geladen
INFO  modelle.Einstellungen - lese EinstellungenHD wurde aufgerufen
INFO  kontroller.Einstellungen - Neu geladene Werte für Einstellungen werden im Fenster aktualisiert
INFO  kontroller.Hauptfenster - Programm beenden wurde im Menue ausgewaehlt
INFO  dms.DMS - Programm-Ende wird ausgeführt stop()-Methode
```

Methode Speichern auf HD

```
public void schreibeEinstellungenHD(Einstellungen einstellungen) {
        OutputStream dateiAusgabeStream = null;
        try {
            dateiAusgabeStream = new FileOutputStream(dateiEinstellungen);
            ObjectOutputStream out = new ObjectOutputStream(dateiAusgabeStream);
            out.writeObject(einstellungen);
            LOG.info("Die Programmeinstellungen wurden auf Festplatte gespeichert");

        } catch (IOException e) {
            LOG.info("Es trat ein Fehler beim schreiben der Programmeinstellungen auf");
        } finally {
            try {
                dateiAusgabeStream.close();
            } catch (IOException e) {
            }
        }
    } // schreibeEinstellungenHD
```

Methode Laden von HD

```
public Einstellungen leseEinstellungenHD() {
        LOG.info("lese EinstellungenHD wurde aufgerufen");
        Einstellungen programmEinstellungen = null;
        // prg.schreibePfadQuelle("Das ist ein neuer Quellpfad");
        try {
            try (FileInputStream dateiEingabeStream = new FileInputStream(dateiEinstellungen)) {
                ObjectInputStream objektEingabeStream = new ObjectInputStream(dateiEingabeStream);
                programmEinstellungen = (Einstellungen) objektEingabeStream.readObject();
                LOG.info("Die Programmeinstellungen wurde in das Objekt geladen");
                LOG.info("Quellverzeichnis wurde geladen : " + programmEinstellungen.holePfadQuelle());
                LOG.info("Benutzername wurde geladen     : " + programmEinstellungen.holeDbBenutzername());
            }
        } catch (IOException | ClassNotFoundException e) {
        }

        return programmEinstellungen;
    } // leseEinstellungenHD
```


Button Speichern

```
@FXML
    private void einstellungenSpeichern(ActionEvent event) {
        LOG.info("Methode einstellungenSpeichern wurde aufgerufen");
        // Alle gemachten Einstellungen werden dauerhaft gespeichert
        einstellungen.schreibeEinstellungenHD(einstellungen);
    } // einstellungenSpeichern
```


Button Laden

```
@FXML
    private void einstellungenLaden(ActionEvent event) {
        LOG.info("einstellungenLaden wurde aufgerufen");
        
        LOG.info("Alle Werte im Fenster Einstellungen werden gelöscht");
        lblBildverzeichnis.setText("");
        lblGeschuetztesVerzeichnis.setText("");
        lblLoeschverzeichnis.setText("");
        lblOCRVerzeichnis.setText("");
        lblOhneDokumentartVerzeichnis.setText("");
        lblQuellverzeichnis.setText("");
        lblZielverzeichnis.setText("");
        
        LOG.info("Alle Werte für Einstellungen werden von Festplatte geladen");
        this.einstellungen.leseEinstellungenHD();
        
        LOG.info("Neu geladene Werte für Einstellungen werden im Fenster aktualisiert");
        lblQuellverzeichnis.setText(einstellungen.holePfadQuelle().toString());
        tfDBBenutzername.setText(einstellungen.holeDbBenutzername().toString());
    } // einstellungenLaden
```

1. Fehlermeldung "Es trat ein Fehler beim schreiben der Programmeinstellungen auf"
Obwohl die Datei erstellt wird, wird die catch IOException e ausgeführt?

2. Im Fenster Einstellungen "StringProperty"
Im Fenster wird StringProperty inklusive Value und den Vert in Eckigen Klammern ausgegeben.
Kannman im Fenster auch nur den Wert anzeigen lassen? "B:\01 Quelle" 

3. Button "Laden"
Damit ich auch wirklich merke das neue Einstellungen im Fenster label dargestellt werden habe ich alle
Werte in den Labels zurückgesetzt lblQuellverzeichnis.setText("");
Dann lade ich die Einstellungen aus der Datei und es wird dann die StringProperty dargestellt

Schein zu funktionieren, bin mir aber nicht so ganz sicher, ob das wirklich so ist.

Ich würde mich freuen wenn mal jemand darüber schauen würde um den Punkt 1 und 2 zu klären.

Danke 
Mi


----------



## kneitzel (9. Jul 2019)

Schau Dir doch bitte die Methoden von Deiner log Instanz an!

Diese haben normalerweise auf Methoden, denen Du am Ende einfach die Exception (bzw. ein Throwable, wovon Exception erbt) übergeben kannst.

Bitte merke Dir eine Sache: Entwickler, die in einem catch die Exception komplett ignorieren, landen auf dem Scheiterhaufen.  

Also im Ernst: So Exceptions solltest Du wirklich nie ignorieren. Es mag evtl. ganz seltene Ausnahmen geben, bei denen das ok sein kann, aber selbst da ist Tracing meist noch sinnvoll....

Und sobald du das, was ich als erster Angesprochen habe, gemacht hast, bekommst Du auch deutlich mehr Informationen als nur ein "Es trat ein Fehler beim schreiben der Programmeinstellungen auf"!

Und noch ein kleiner Hinweis:
Und dieses close im finally Block: Schau Dir doch einmal das "try with resources" an. Dann entfällt dies komplett und Java kümmert sich darum.


----------



## kneitzel (9. Jul 2019)

Zu 2. hatte ich noch nichts geschrieben: Hier habe ich Probleme, Dich genau zu verstehen. Du setzt ja den Text über setText(...) und Du rufst bei holePfadQuelle() einfach .toString() auf. (Und bei dem anderen Wert natürlich auch!)

Ich vermute einmal, dass das Resultat mit den eckigen Klammern von der toString Implementation der Klasse, von der die Instanz, die da zurück gegeben wird, kommt. Und wenn das ein key / value Paar ist, dann kannst Du bestimmt auch den Wert abfragen. Also nicht einfach .toString() darauf aufrufen.


----------



## MiMa (9. Jul 2019)

Mit Log Instanz meinst du das Logger Objekt?
Meinst Du das man IOException an den Logger übergibt anstatt es in einer Catch Anweisung zu definieren?

Eine catch Exception zu ignorieren ist nicht meine Absicht und habe dies unter 1 beschrieben. Da ich in Büchern die Vorgehensweise erlernt habe die in etwa so aussieht frage ich mich wo das problem liegen könnte. Sehr viele Zeilen sind fsa ja nicht bei denen man etwas falsch machen könnte?
Und das die Datei dann plötzlich da ist sagt mir dass das writeObject etwas geschrieben hat.

Der Funktionskopf und finally habe ich geändert in

```
public void schreibeEinstellungenHD(Einstellungen einstellungen) throws IOException {
...

} finally {
                dateiAusgabeStream.close();
        }
    } // schreibeEinstellungenHD
```

Zu 2. StringProperties
Damit hatte ich zuvor noch nie gearbeitet und dachte, das es wie Strings behandelt wird die sich halt Serialisieren lassen.
Werde mir die StringProperty noch mal genauer ansehen.
Eigentlich ist der Inhalt nur ein String "Ein Verzeichnispfad" und wird durch holePfadQuelle() aus dem objekt geholt und mit toString als String für das Label deklariert.

Anscheinend habe ich das Serializieren und Laden nicht richtig verstanden, da ich beim Einfügen eines zweiten Stringwertes gesehen habe das diese nicht richtig den labels zugeordnet werden. Die Inhalte sind vertauscht. Ich könnte mir vorstellen das ich das schreiben und laden bezüglich des Datenobjektes nicht wirklich korrekt gemacht habe.


----------



## kneitzel (9. Jul 2019)

Ja, ich meinte Deine LOG Instanz. Ich weiß nicht, welches Logging Framework Du verwendest, aber Du könntest einmal schauen, ob der Code so funktionieren würde (im Forum-Editor geschrieben, daher evtl. Tippfehler vorhanden!)

```
// 1. Try with ressource
        try (dateiAusgabeStream = new FileOutputStream(dateiEinstellungen)) {
            ObjectOutputStream out = new ObjectOutputStream(dateiAusgabeStream);
            out.writeObject(einstellungen);
            LOG.info("Die Programmeinstellungen wurden auf Festplatte gespeichert");

        } catch (IOException e) {
            // 2. Exception wird mit übergeben. Das ist bei den meisten Logging Frameworks so gebaut ...
            LOG.info("Es trat ein Fehler beim schreiben der Programmeinstellungen auf", e);
        }
        // 3. finally Block entfällt da try with ressource das close übernimmt.
```

Man kann dann auch die Deklaration mit in das try ziehen und da der ObjectOutputStream auch AutoCloseable implementiert, würde ich dann am Ende zu sowas tendieren:

```
// 0. Die Deklaration von dateiAusgabeStream vor dem try entfällt!
        // 1. Try with ressource
        try (FileOutputStream dateiAusgabeStream = new FileOutputStream(dateiEinstellungen);
             ObjectOutputStream out = new ObjectOutputStream(dateiAusgabeStream)) {
            out.writeObject(einstellungen);
            LOG.info("Die Programmeinstellungen wurden auf Festplatte gespeichert");

        } catch (IOException e) {
            // 2. Exception wird mit übergeben. Das ist bei den meisten Logging Frameworks so gebaut ...
            LOG.info("Es trat ein Fehler beim schreiben der Programmeinstellungen auf", e);
        }
        // 3. finally Block entfällt da try with ressource das close übernimmt.
```

Die Kommentare mit dem 0. / 1. / 2. und 3. sind nur für Dein Verständnis. Im Code würde ich die nicht setzen.


----------



## Robat (9. Jul 2019)

Properties sind dazu da, an eine andere Property gebunden zu werden um automatisch aktualisiert zu werden.
So kannst du die TextProperty deines Textfeldes an die StringProperty deines Objektes binden.

```
lblQuellverzeichnis.textProperty() .bind(einstellungen.holePfadQuelle());
```


----------



## MiMa (9. Jul 2019)

@Robat
Danke nochmal für die Aufklärung. Ist schon länger her, aber ja genau deshalb sollte ich mich damals mit Properties beschäftigen, da ich ja mit einer GUI Arbeiten wollte. Bezüglich GUI Programmierung war echt nicht viel Literatur zu finden nur die übliche für Anfänger, bei denen scheinbar alle abschreiben. Jedenfalls kommt es mir so vor, da die Inhalte alle gleich sind.
Hat man bedarf darüber hinaus, sieht es schlecht aus.
Habe den SetText durch das oben vorgeschlagene Binding ersetzt und bekomme das gewünschte Ergebnis.
Danke freue mich dafür und werde das Kapitel nochmal etwas vertiefen.


@kneizel
Danke für den Vorschlag, ich werde das mal ausprobieren. Ich nutze Log4j2.

Mein Ziel war es ein Objekt mit dem Namen "einstellungen" zu erstellen. In dieses Objekt wollte ich alles was an Programmeinstellungen benötigt wird hinein speichern. Dieses Objekt sollte zu beginn des Programmes init() geladen werden. Mit dem aufrufen des Fensters "Einstellungen sollten alle Inhalte dieses Objektes verändert, aktualisiert oder gelöscht werden können. Der Laden Button habe ich aktuell nur zur Kontrolle eingebaut um die Speicherung zu testen. Beim Drücken von Speichern sollen alle Inhalte des Objektes auf die Festplatte gespeichert und das Fenster Einstellungen geschlossen werden.
Klingt nicht gerade kompliziert, aber für mich nicht gerade einfach.


----------



## MiMa (10. Jul 2019)

Deinen Vorschlag für die Log Instanz habe ich implementiert und siehe da, es kommen jede Menge informationen

```
INFO  start - Das Programm wird initialisiert init()-Methode
INFO  start - Das Programm-Hauptfenster wird initialisiert start()-Methode
INFO  kontroller.Hauptfenster - Einstellungen wurde im Menue ausgewaehlt
INFO  kontroller.Einstellungen - Einstellungen werden initialisiert
INFO  kontroller.Einstellungen - sucheQuellverzeichnis wurde aufgerufen
INFO  kontroller.Einstellungen - Selektiertes Quellverzeichnis StringProperty [value: B:\01 Quelle]
INFO  kontroller.Einstellungen - Methode einstellungenSpeichern wurde aufgerufen
INFO  modelle.Einstellungen - Es trat ein Fehler beim schreiben der Programmeinstellungen auf
java.io.NotSerializableException: javafx.beans.property.SimpleStringProperty
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1185) ~[?:?]
    at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1553) ~[?:?]
    at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1510) ~[?:?]
    at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1433) ~[?:?]
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1179) ~[?:?]
    at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:349) ~[?:?]
    at modelle.Einstellungen.schreibeEinstellungenHD(Einstellungen.java:161) ~[DMS.jar:1.0]
    at kontroller.Einstellungen.einstellungenSpeichern(Einstellungen.java:139) ~[DMS.jar:1.0]
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:?]
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:?]
    at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:?]
    at java.lang.reflect.Method.invoke(Method.java:564) ~[?:?]
    at com.sun.javafx.reflect.Trampoline.invoke(MethodUtil.java:76) ~[javafx.base:?]
    at jdk.internal.reflect.GeneratedMethodAccessor2.invoke(Unknown Source) ~[?:?]
    at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:?]
    at java.lang.reflect.Method.invoke(Method.java:564) ~[?:?]
    at com.sun.javafx.reflect.MethodUtil.invoke(MethodUtil.java:275) ~[javafx.base:?]
    at com.sun.javafx.fxml.MethodHelper.invoke(MethodHelper.java:83) ~[javafx.fxml:?]
    at javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1782) ~[javafx.fxml:?]
    at javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(FXMLLoader.java:1670) ~[javafx.fxml:?]
    at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86) ~[javafx.base:?]
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238) ~[javafx.base:?]
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191) ~[javafx.base:?]
    at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59) ~[javafx.base:?]
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58) ~[javafx.base:?]
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114) ~[javafx.base:?]
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56) ~[javafx.base:?]
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114) ~[javafx.base:?]
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56) ~[javafx.base:?]
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114) ~[javafx.base:?]
    at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74) ~[javafx.base:?]
    at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49) ~[javafx.base:?]
    at javafx.event.Event.fireEvent(Event.java:198) ~[javafx.base:?]
    at javafx.scene.Node.fireEvent(Node.java:8865) ~[javafx.graphics:?]
    at javafx.scene.control.Button.fire(Button.java:200) ~[javafx.controls:?]
    at com.sun.javafx.scene.control.behavior.ButtonBehavior.mouseReleased(ButtonBehavior.java:206) ~[javafx.controls:?]
    at com.sun.javafx.scene.control.inputmap.InputMap.handle(InputMap.java:274) ~[javafx.controls:?]
    at com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:218) ~[javafx.base:?]
    at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80) ~[javafx.base:?]
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238) ~[javafx.base:?]
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191) ~[javafx.base:?]
    at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59) ~[javafx.base:?]
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58) ~[javafx.base:?]
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114) ~[javafx.base:?]
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56) ~[javafx.base:?]
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114) ~[javafx.base:?]
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56) ~[javafx.base:?]
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114) ~[javafx.base:?]
    at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74) ~[javafx.base:?]
    at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54) ~[javafx.base:?]
    at javafx.event.Event.fireEvent(Event.java:198) ~[javafx.base:?]
    at javafx.scene.Scene$MouseHandler.process(Scene.java:3876) ~[javafx.graphics:?]
    at javafx.scene.Scene$MouseHandler.access$1300(Scene.java:3604) ~[javafx.graphics:?]
    at javafx.scene.Scene.processMouseEvent(Scene.java:1874) ~[javafx.graphics:?]
    at javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2613) ~[javafx.graphics:?]
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:397) ~[javafx.graphics:?]
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:295) ~[javafx.graphics:?]
    at java.security.AccessController.doPrivileged(Native Method) ~[?:?]
    at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$2(GlassViewEventHandler.java:434) ~[javafx.graphics:?]
    at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:389) ~[javafx.graphics:?]
    at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:433) ~[javafx.graphics:?]
    at com.sun.glass.ui.View.handleMouseEvent(View.java:556) ~[javafx.graphics:?]
    at com.sun.glass.ui.View.notifyMouse(View.java:942) ~[javafx.graphics:?]
    at com.sun.glass.ui.win.WinApplication._runLoop(Native Method) ~[javafx.graphics:?]
    at com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:175) ~[javafx.graphics:?]
    at java.lang.Thread.run(Thread.java:844) [?:?]
INFO  kontroller.Hauptfenster - Programm beenden wurde im Menue ausgewaehlt
INFO  start - Programm-Ende wird ausgeführt stop()-Methode
```

Da ich mir nicht vorstellen kann das es an der Schreibe Methode liegt, denke ich das es an meinem Datenodell liegt?

Hier sind zwei Methoden für die Knöpfe Quellverzeichnis und Zielverzeichnis.

```
@FXML
    private void sucheQuellverzeichnis(ActionEvent event) {
        LOG.info("sucheQuellverzeichnis wurde aufgerufen");
        String verzeichnis = Datei.auswahlVerzeichnis("Wähle das Quellverzeichnis");
        einstellungen.schreibePfadQuelle(verzeichnis);
        lblQuellverzeichnis.textProperty().bind(einstellungen.holePfadQuelle());
        LOG.info("Selektiertes Quellverzeichnis " + einstellungen.holePfadQuelle());
    } // sucheQuellverzeichnis

    @FXML
    private void sucheZielverzeichnis(ActionEvent event) {
        LOG.info("sucheZiellverzeichnis wurde aufgerufen");
        String verzeichnis = Datei.auswahlVerzeichnis("Wähle das Zielverzeichnis");
        einstellungen.schreibePfadZiel(verzeichnis);
//        lblZielverzeichnis.textProperty().bind(einstellungen.holePfadZiel());
        lblZielverzeichnis.setText(verzeichnis);
        LOG.info("Selektiertes Zielverzeichnis " + einstellungen.holePfadZiel());
    } // sucheZielverzeichnis
```
Wenn ich statt des Bindings setText verwende wird es richtig in das Fenster geschrieben, arbeite ich mit dem Binding, ist es falsch.
So wie im Bild einstellungen. Versteh ich nicht, wenn ich zwei verschiedene Labels mit Binding anpreche, wie kann denn dann der gleiche inhalt bei beiden Labels gleich sein obwohl das Quellverzeichnis schon korrekt dargestellt wurde?

Daher gehe ich davon aus, das mein Modell fehlerhaft sein könnte?
Die Klasse für das Modell ist im package modelle

```
public class Einstellungen implements Serializable {

    // Erstellt ein LoggerObjekt und holt dessen Instanz
    private static final Logger LOG = (Logger) LogManager.getLogger(Einstellungen.class);

    // Deklarationen Standardeinstellungen
    String dateiNameEinstellungen = "Programmeinstellungen.ser";
    String pfadEinstellungen = "B:\\00 Einstellungen\\";
    File dateiEinstellungen = new File(pfadEinstellungen + dateiNameEinstellungen);

    private final StringProperty pfadQuelle = new SimpleStringProperty();
    private final StringProperty pfadZiel = new SimpleStringProperty();
    private final StringProperty pfadOCR = new SimpleStringProperty();
    private final StringProperty pfadOhneDokumentArt = new SimpleStringProperty();
    private final StringProperty pfadLoeschen = new SimpleStringProperty();
    private final StringProperty pfadGeschuetzt = new SimpleStringProperty();
    private final StringProperty pfadBild = new SimpleStringProperty();

    private final StringProperty dbServeradresse = new SimpleStringProperty();
    private final StringProperty dbDatenbankname = new SimpleStringProperty();
    private final StringProperty dbBenutzername = new SimpleStringProperty();
    private final StringProperty dbPasswort = new SimpleStringProperty();
    private final StringProperty dnbToken = new SimpleStringProperty();

    // Konstruktor ohne Parameter
    public Einstellungen() {

    } // Konstruktor ohne Parameter

    // Methoden
    public StringProperty holePfadQuelle() {
        return pfadQuelle;
    }

    public void schreibePfadQuelle(String quellVerzeichnis) {
        pfadQuelle.set(quellVerzeichnis);
    }

    public StringProperty pfadQuelleProperty() {
        return pfadQuelle;

    }

    public StringProperty holePfadZiel() {
        return pfadZiel;
    }

    public void schreibePfadZiel(String zielVerzeichnis) {
        pfadQuelle.set(zielVerzeichnis);
    }

    public StringProperty holePfadOCR() {
        return pfadOCR;
    }

    public void schreibePfadOCR(String ocrVerzeichnis) {
        pfadQuelle.set(ocrVerzeichnis);
    }

    public StringProperty holePfadOhneDokumentArt() {
        return pfadOhneDokumentArt;
    }

    public void schreibePfadOhneDokumentArt(String ohneDokumentartVerzeichnis) {
        pfadQuelle.set(ohneDokumentartVerzeichnis);
    }

    public StringProperty holePfadLoeschen() {
        return pfadLoeschen;
    }

    public void schreibePfadLoeschen(String loeschenVerzeichnis) {
        pfadQuelle.set(loeschenVerzeichnis);
    }

     public void schreibePfadGeschuetzt(String geschuetztVerzeichnis) {
        pfadQuelle.set(geschuetztVerzeichnis);
    }

    public StringProperty holePfadBild() {
        return pfadBild;
    }

    public void schreibePfadBild(String bildVerzeichnis) {
        pfadQuelle.set(bildVerzeichnis);
    }

    public StringProperty holeDbServeradresse() {
        return dbServeradresse;
    }

    public void schreibeDbServeradresse(String serveradresse) {
        pfadQuelle.set(serveradresse);
    }

    public StringProperty holeDbDatenbankname() {
        return dbDatenbankname;
    }

    public void schreibeDbDatenbankname(String datenbankname) {
        pfadQuelle.set(datenbankname);
    }

    public StringProperty holeDbBenutzername() {
        return dbBenutzername;
    }

    public void schreibeDbBenutzername(String benutzername) {
        pfadQuelle.set(benutzername);
    }

    public StringProperty holeDbPasswort() {
        return dbPasswort;
    }

    public void schreibeDbPasswort(String passwort) {
        pfadQuelle.set(passwort);
    }

    public StringProperty holeDnbToken() {
        return dnbToken;
    }

    public void schreibeDnbToken(String token) {
        pfadQuelle.set(token);
    }

    public void schreibeEinstellungenHD(Einstellungen einstellungen) throws IOException {
        try (FileOutputStream dateiAusgabeStream = new FileOutputStream(dateiEinstellungen);
            ObjectOutputStream out = new ObjectOutputStream(dateiAusgabeStream)) {
            out.writeObject(einstellungen);
            LOG.info("Die Programmeinstellungen wurden auf Festplatte gespeichert");

        } catch (Exception e) {
            LOG.info("Es trat ein Fehler beim schreiben der Programmeinstellungen auf", e);
        }

    } // schreibeEinstellungenHD

    public Einstellungen leseEinstellungenHD() {
        LOG.info("lese EinstellungenHD wurde aufgerufen");
        Einstellungen programmEinstellungen = null;
        try {
            try (FileInputStream dateiEingabeStream = new FileInputStream(dateiEinstellungen)) {
                ObjectInputStream objektEingabeStream = new ObjectInputStream(dateiEingabeStream);
                programmEinstellungen = (Einstellungen) objektEingabeStream.readObject();
                LOG.info("Die Programmeinstellungen wurde in das Objekt geladen");
                LOG.info("Quellverzeichnis wurde geladen : " + programmEinstellungen.holePfadQuelle());
                LOG.info("Benutzername wurde geladen     : " + programmEinstellungen.holeDbBenutzername());
            }
        } catch (IOException | ClassNotFoundException e) {
        }
        return programmEinstellungen;
    } // leseEinstellungenHD
} // Einstellungen
```

Der Kontroller ist im package kontroller

```
public class Einstellungen implements Initializable {

    // Erstellt ein LoggerObjekt und holt dessen Instanz
    private static final Logger LOG = (Logger) LogManager.getLogger(Einstellungen.class);

    modelle.Einstellungen einstellungen = new modelle.Einstellungen();

    @FXML
    private TextField tfDBServeradresse;
    @FXML
    private TextField tfDBDatenbankname;
    @FXML
    private TextField tfDBBenutzername;
    @FXML
    private TextField tfDBPasswort;
    @FXML
    private TextField tfTokenDNB;
    @FXML
    private Label lblQuellverzeichnis;
    @FXML
    private Label lblZielverzeichnis;
    @FXML
    private Label lblOCRVerzeichnis;
    @FXML
    private Label lblOhneDokumentartVerzeichnis;
    @FXML
    private Label lblLoeschverzeichnis;
    @FXML
    private Label lblGeschuetztesVerzeichnis;
    @FXML
    private Label lblBildverzeichnis;

    /**
     * Initialisierung der Kontroller Klasse.
     */
    @Override
    public void initialize(URL url, ResourceBundle rb) {
        LOG.info("Einstellungen werden initialisiert");     
    } // initialize

    @FXML
    private void sucheQuellverzeichnis(ActionEvent event) {
        LOG.info("sucheQuellverzeichnis wurde aufgerufen");
        String verzeichnis = Datei.auswahlVerzeichnis("Wähle das Quellverzeichnis");
        einstellungen.schreibePfadQuelle(verzeichnis);
        lblQuellverzeichnis.textProperty().bind(einstellungen.holePfadQuelle());
        LOG.info("Selektiertes Quellverzeichnis " + einstellungen.holePfadQuelle());
    } // sucheQuellverzeichnis

    @FXML
    private void sucheZielverzeichnis(ActionEvent event) {
        LOG.info("sucheZiellverzeichnis wurde aufgerufen");
        String verzeichnis = Datei.auswahlVerzeichnis("Wähle das Zielverzeichnis");
        einstellungen.schreibePfadZiel(verzeichnis);
//        lblZielverzeichnis.textProperty().bind(einstellungen.holePfadZiel());
        lblZielverzeichnis.setText(verzeichnis);
        LOG.info("Selektiertes Zielverzeichnis " + einstellungen.holePfadZiel());
    } // sucheZielverzeichnis

    @FXML
    private void sucheOCRVerzeichnis(ActionEvent event) {
        LOG.info("sucheOCRVerzeichnis wurde aufgerufen");
        String verzeichnis = Datei.auswahlVerzeichnis("Wähle das OCR Verzeichnis");
        einstellungen.schreibePfadOCR(verzeichnis);
        lblOCRVerzeichnis.textProperty().bind(einstellungen.holePfadOCR());
        LOG.info("Selektiertes Zielverzeichnis " + einstellungen.holePfadOCR());
    } // sucheOCRVerzeichnis

    @FXML
    private void sucheBildverzeichnis(ActionEvent event) {
        LOG.info("sucheBildverzeichnis wurde aufgerufen");
        String verzeichnis = Datei.auswahlVerzeichnis("Wähle das Bildverzeichnis");
        einstellungen.schreibePfadBild(verzeichnis);
        lblBildverzeichnis.textProperty().bind(einstellungen.holePfadBild());
        LOG.info("Selektiertes Bildverzeichnis " + einstellungen.holePfadBild());
    } // sucheBildverzeichnis

    @FXML
    private void sucheOhneDokumentartVerzeichnis(ActionEvent event) {
        LOG.info("sucheOhneDokumentartVerzeichnis wurde aufgerufen");
        String verzeichnis = Datei.auswahlVerzeichnis("Wähle das Verzeichnis ohne Dokumentart");
        einstellungen.schreibePfadOhneDokumentArt(verzeichnis);
        lblOhneDokumentartVerzeichnis.textProperty().bind(einstellungen.holePfadOhneDokumentArt());
        LOG.info("Selektiertes Verzeichnis ohne Dokumentart " + einstellungen.holePfadOhneDokumentArt());
    } // sucheOhneDokumentartVerzeichnis

    @FXML
    private void sucheLoeschverzeichnis(ActionEvent event) {
        LOG.info("sucheLoeschverzeichnis wurde aufgerufen");
        String verzeichnis = Datei.auswahlVerzeichnis("Wähle das Löschverzeichnis");
        einstellungen.schreibePfadLoeschen(verzeichnis);
        lblLoeschverzeichnis.textProperty().bind(einstellungen.holePfadLoeschen());
        LOG.info("Selektiertes Löschverzeichnis " + einstellungen.holePfadLoeschen());
    } // sucheLoeschverzeichnis

    @FXML
    private void sucheGeschuetztesVerzeichnis(ActionEvent event) {
        LOG.info("sucheGeschuetztesVerzeichnis wurde aufgerufen");
        String verzeichnis = Datei.auswahlVerzeichnis("Wähle das Geschützte Verzeichnis");
        einstellungen.schreibePfadGeschuetzt(verzeichnis);
        lblGeschuetztesVerzeichnis.textProperty().bind(einstellungen.holePfadGeschuetzt());
        LOG.info("Selektiertes Geschützte Verzeichnis " + einstellungen.holePfadGeschuetzt());
    } // sucheGeschuetztesVerzeichnis

    @FXML
    private void einstellungenSpeichern(ActionEvent event) throws IOException {
        LOG.info("Methode einstellungenSpeichern wurde aufgerufen");
        // Alle gemachten Einstellungen werden dauerhaft gespeichert
        einstellungen.schreibeEinstellungenHD(einstellungen);
    } // einstellungenSpeichern
    

    @FXML
    private void einstellungenLaden(ActionEvent event) {
        LOG.info("einstellungenLaden wurde aufgerufen");
        
        LOG.info("Alle Werte im Fenster Einstellungen werden gelöscht");
        lblQuellverzeichnis.setText("");
        lblZielverzeichnis.setText("");
        lblOCRVerzeichnis.setText("");
        lblBildverzeichnis.setText("");
        lblOhneDokumentartVerzeichnis.setText("");
        lblLoeschverzeichnis.setText("");
        lblGeschuetztesVerzeichnis.setText("");
        
        LOG.info("Alle Werte für Einstellungen werden von Festplatte geladen");
        this.einstellungen.leseEinstellungenHD();
        
        LOG.info("Neu geladene Werte für Einstellungen werden im Fenster aktualisiert");
          lblQuellverzeichnis.textProperty().bind(einstellungen.holePfadQuelle());
          lblZielverzeichnis.textProperty().bind(einstellungen.holePfadZiel());
          lblOCRVerzeichnis.textProperty().bind(einstellungen.holePfadOCR());
          lblBildverzeichnis.textProperty().bind(einstellungen.holePfadBild());
          lblOhneDokumentartVerzeichnis.textProperty().bind(einstellungen.holePfadOhneDokumentArt());
          lblLoeschverzeichnis.textProperty().bind(einstellungen.holePfadLoeschen());
          lblGeschuetztesVerzeichnis.textProperty().bind(einstellungen.holePfadGeschuetzt());
                  
    } // einstellungenLaden

} // Einstellungen
```


----------



## kneitzel (10. Jul 2019)

Also das Problem ist in Deinem Log diese Stelle:
java.io.NotSerializableException: javafx.beans.property.SimpleStringProperty

Du kannst mit ObjectOutputStream nur Objekte schreiben, die Serializable sind. Und das ist SimpleStringProperty nicht.

Aber Du implementierst Serializable ja bei Einstellungen implementierst, wäre eine Möglichkeit, dass Du die Serialisierung selbst vornimmst:
a) alle Properties markierst du als transient
b) Du liest und schreibst die Werte selbst in den entsprechenden Funktionen:

```
private void writeObject(ObjectOutputStream s) throws IOException {
    s.defaultWriteObject();
    s.writeLong(idProperty().longValue());
    s.writeUTF(aStringProperty().getValueSafe()); // getValueSafe damit kein null rein kommen kann.
    // ...
}

private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
    idProperty.set(s.readLong());
    aStringProperty.set(s.readUTF());
    // Gleiche Reihenfolge wie bei writeObject()
    // ...
}
```


----------



## looparda (10. Jul 2019)

Ich würde das ganze aufspalten, sodass deine Einstellungen keine Properties haben. Die Properties wandern in ein ViewModel EinstellungenViewModel, das aus einem Model konstruiert werden kann. Im Controller verbindest du nur noch den View mit dem ViewModel

```
tfDBServeradresse.textProperty().bind(einstellungenVm.tfDBServeradresseProperty());
```
Die Einstellungen sind somit einfach zu serialisieren und die Zuständigkeiten besser geklärt. Ich hoffe das war verständlich genug skizziert.


----------



## MiMa (10. Jul 2019)

Hi, wie meinst du das mit dem aufteilen?
Ich habe ein Model, einen Kontroller und eine View! Ich habe ewig gebraucht das zu begreifen und jetzt versuche ich das in der Praxis. Im Kontroller "EinstellungenK" wird wir ein Objekt erzeugt welches durch den Parameterlosen Konstruktor ein Einstellungen Objekt erzeugt, mit den Methoden und Properties aus der Klasse "EinstellungenM". Somit habe doch doch schon mal eine Verbindung zum Modell durch den Kontroller hergestellt. War doch richtig?
Und im Kontroller und dem Binding wird das Modell dann zum Fenster gebunden?!?! 
Wenn ich jetzt etwas in die Properties stecke, sollte es dann doch im Fenster sichtbar werden? War doch so?
Eine wirklich gute Literatur hierzu habe ich nie wirklich gefunden die das an einem Beispiel in JavaFX zeigt. 

Es scheint so das ich etwas mit den Methoden "schreibePfadQuelle", "schreibePfadZiel" zwar hineinschreibe aber es nicht wirklich drin ist oder so. Zumindest habe ich das Gefühl, dass das objekt nicht so reagiert wie ich es erwartet hätte.


----------



## mrBrown (10. Jul 2019)

Dein Model hat das Problem, dass es JavaFX-Properties benutzt, und damit kaum von der View unabhängig ist. Besser ist’s, das Model möglichst einfach zu halten.

Um das Model trotzdem an die View binden zu können, führt man ein ViewModel ein, dieses enthält die Properties (an die sich die View binden kann) und kennt das Model, holt sich also den aktuellen Zustand daher und schreib Änderungen dort hin.


----------



## looparda (10. Jul 2019)

Ein Minimalbeispiel, um es darzustellen.
Die Einstellungen (Modell) als POJO ohne Abhängigkeiten zu JavaFX. Reines Java Standardlib.

```
// POJO
public class Einstellungen {

    private String tfDBServeradresse;

    public Einstellungen setTfDBServeradresse(final String tfDBServeradresse) {
        this.tfDBServeradresse = tfDBServeradresse;
        return this;
    }

    public String getTfDBServeradresse() {
        return tfDBServeradresse;
    }

    public static Einstellungen defaultSettings() {
        return new Einstellungen()
                .setTfDBServeradresse("localhost");
    }
}
```
Das ViewModel als Mapper zwischen Modell und View. Hier kommt jede Logik rein, die zur Darstellung benötigt wird. z.B. die Logik, ob ein Feld disabled sein soll oder nicht (wird wiederum als Property nach außen sichtbar, um die View daran zu binden). Das ViewModel darf keine Referenzen auf die View haben.

```
public class EinstellungenViewModel {
    private final StringProperty tfDBServeradresse = new SimpleStringProperty(this, "tfDBServeradresse");

    public EinstellungenViewModel(Einstellungen einstellungen) {
        tfDBServeradresse.set(einstellungen.getTfDBServeradresse());
    }

    public String getTfDBServeradresse() {
        return tfDBServeradresse.get();
    }

    public StringProperty tfDBServeradresseProperty() {
        return tfDBServeradresse;
    }

    public void setTfDBServeradresse(final String tfDBServeradresse) {
        this.tfDBServeradresse.set(tfDBServeradresse);
    }

}
```
Der View-Controller macht das Binding der Observables der View und des ViewModel. Streng genommen kommt hier kein bisschen Logik rein, sondern nur das Herstellen der Bindings.

```
public class EinstellungenView implements Initializable {

    @FXML
    private TextField tfDBServeradresse;

    private final EinstellungenViewModel viewModel;

    public EinstellungenView() {
        // Load from file in real world example
        Einstellungen einstellungen = Einstellungen.defaultSettings();
        // Construct view model
        this.viewModel = new EinstellungenViewModel(einstellungen);
    }

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        tfDBServeradresse.textProperty().bind(viewModel.tfDBServeradresseProperty());
    }
}
```
Ein Startercode, um die GUI zu erzeugen.

```
public class Main extends Application {

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

    @Override
    public void start(Stage primaryStage) throws IOException {
        Parent root = FXMLLoader.load(getClass().getResource("EinstellungenView.fxml"));
        Scene scene = new Scene(root);
        primaryStage.setScene(scene);
        primaryStage.sizeToScene();
        primaryStage.show();
    }
}
```
Das sehr simple FXML

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

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

<AnchorPane xmlns="http://javafx.com/javafx"
            xmlns:fx="http://javafx.com/fxml"
            fx:controller="EinstellungenView"
            prefHeight="400.0" prefWidth="600.0">

    <TextField fx:id="tfDBServeradresse"/>

</AnchorPane>
```
Ich hoffe dir wird ersichtlich, wie deine Klasse Einstellungen sauberer wird und die Serialisieren wieder einfach.


----------



## MiMa (11. Jul 2019)

Vielen Dank für die Ausführliche Darstellung.
Wenn ich das richtig verstanden habe, sollte ich mein Modell genau so gestalten, wie ich es bei meinen Konsolenprogrammen ohne GUI in normalen Java Code gemacht habe. Dann eine weitere Modell Klasse (ViewModell) die im Prinzip die gleichen Inhalte der zu speichernden Werte hat wie das Modell, aber als Properties weil diese an das View gebunden werden können. Im Kontroller sind dann nur Methoden enthalten die zum View gehören wie Buttons, Labels usw.
Methoden zur Verarbeitung von Werten kommen weiterhin in das Modell!
Das Serialisieren der Werte wird dann nur mit dem Modell durchgeführt, also dort implements Serializable?
Habe dann mal unter ViewModel im Netz nachgesehen und da tauchte dann das Konzept des *MVVM* auf.
Ich werde mein Programm nach den o.a. Informationen abändern und hoffe, das es klappt 

Eine Frage hätte ich noch zur Organisation der Pakete.
Sollte man diese nach Fenster gruppieren, was zum Fenster Einstellungen gehört?
Also Paket Einstellungen und darunter dann das Modell, ViewModell, Kontroller, FXML, stil.css usw.

Oder wie im Bild nach Gruppen wie Fenster, Kontroller, Modelle, Stiele.css, FXMLs?

Vielen Dank für die tolle Hilfe, ich melde mich zurück wenn es es funktioniert, oder wenn es auch nicht funktioniert 
Mi


----------



## looparda (11. Jul 2019)

MiMa hat gesagt.:


> Methoden zur Verarbeitung von Werten kommen weiterhin in das Modell!


Der View ruft Methoden aus dem ViewModel auf, wenn Aktionen auftreten wie _Button gedrückt_. Das ViewModel entscheidet was passieren soll, wenn die Methode aufgerufen wurde. Beispielsweise führst du im ViewModel eine Validierung durch, setzt eine Fehlerproperty, die dafür sorgt, dass im View der Fehler sichtbar wird oder rufst letztlich eine Methode aus dem Modell auf.


MiMa hat gesagt.:


> Das Serialisieren der Werte wird dann nur mit dem Modell durchgeführt, also dort implements Serializable?


Ja


MiMa hat gesagt.:


> Habe dann mal unter ViewModel im Netz nachgesehen und da tauchte dann das Konzept des *MVVM* auf


Ja, es handelt sich im groben um das Konzept. Da ich nicht alle Nuancen kenne und damit sicher nicht einhalte bezeichne ich mein Beispiel aber nicht als MVVM Beispiel.
Wie du die Klassen zusammenpackst ist Geschmackssache. Kommt auch drauf an, ob du ein Master-Detail UI hast, ein Form auslagerst, welches du in View, Update und Edit einbindest oder nur über Button navigierst und jedes mal ein neues Fenster öffnest. Für sowas kleines reicht vermutlich die Struktur
in src
proj.Application.java
proj.model.Einstellungen.java
proj.ui.einstellungen.EinstellungenView.java
proj.ui.einstellungen.EinstellungenViewModel.java
und in resources
proj.ui.einstellungen.EinstellungenView.fxml
proj.ui.einstellungen.style.css


----------



## MiMa (16. Jul 2019)

Das minimale Beispiel mit der Serveradresse habe ich jetzt mal in mein Projekt mit Dateien übertragen.
Es funktioniert zwar soweit, aber die Abwicklung hat sich mir noch nicht so ganz erschlossen.
Ich Dokumentiere zwar sehr viel, das ist aber erstmal ein Hilfmittel für mich um es besser zu verstehen.

Auszug der Klasse Einstellungen (Java-Standardlib)

```
private String pfadQuelle;
private String pfadZiel;

// Methode für das einsetzen von Standard Werten
    public static Einstellungen standardWerte() {
        String heimVerzeichnis = System.getProperty("user.home");
        // return new Einstellungen().schreibePfadQuelle(homeVerzeichnis+"\\Dateien\\01 Quelle");
        Einstellungen standardWerte = new Einstellungen();
        standardWerte.schreibePfadQuelle(heimVerzeichnis+"\\Dateien\\01 Quelle");
        standardWerte.schreibePfadZiel(heimVerzeichnis+"\\Dateien\\02 Ziel");    
        return standardWerte;
    } // Methode für das einsetzen von Standard Werten

public String holePfadQuelle() {
        return pfadQuelle;
    }
   
    public Einstellungen schreibePfadQuelle(final String quellVerzeichnis) {
        pfadQuelle = quellVerzeichnis;
        return this;
    }
   
    public String holePfadZiel() {
        return pfadZiel;
    }

    public void schreibePfadZiel(String zielVerzeichnis) {
        pfadQuelle = zielVerzeichnis;
    }
```
Jedoch habe ich Schwierigkeiten zu verstehen warum bei der schreibePfadQuelle der Datentp Einstellungen gewählt wurde?
Es wird doch nur den Pfad der einen String enthält zurück gegeben!
Normalerweise definiere ich so wie bei schreibePfadZiel eine Methode die nichts zurückgibt mit "void" bei einer Set-Methode?

Auszug der Klasse EinstellungenFM (ViewModell)

```
private final StringProperty pfadQuelle = new SimpleStringProperty();
private final StringProperty pfadZiel = new SimpleStringProperty();

// Erstellt das Einstellungen Fenster Modell
    public EinstellungenFM(Einstellungen einstellungen){
        pfadQuelle.set(einstellungen.holePfadQuelle());
        pfadZiel.set(einstellungen.holePfadZiel());
    }

public String holePfadQuelle() {
        return pfadQuelle.get();
    }
   
    public StringProperty pfadQuelleProperty() {
        return pfadQuelle;
    }
   
    public void schreibePfadQuelle(final String pfadQuelle) {
        this.pfadQuelle.set(pfadQuelle);
    }
   
    public StringProperty holePfadZiel() {
        return pfadZiel;
    }
    public StringProperty pfadZielProperty() {
        return pfadZiel;
    }

    public void schreibePfadZiel(String zielVerzeichnis) {
        pfadQuelle.set(zielVerzeichnis);
    }
```

Bei EinstellungenFM wird ein Kosntruktor aufgerufen der einen Parameter einstellungen enthält.
Ich denke mal hier wird ein Objekt Erzeugtder aus dem Bauplan von Einstellungen der Standardlib erzeugt wird und direkt Werte zugewiesen bekommt. Zu diesem Zeitpunkt ist mir nicht bekannt, ob sich bereit Werte darin enthalten? Ich denke nicht, da die Methode standardWerte() noch nicht aufgerufen wurde?
Ich verstehe aber schon das im ViewModell alles enthalten sein soll was für den Fensteraufbau und deren verhalten notwendig ist.

Auszug der Klasse EinstellungenFK (ViewController)

```
@FXML
private Label lblQuellverzeichnis;
@FXML
private Label lblZielverzeichnis;

private EinstellungenFM fensterModell;

// Konstruktor um das Fenstermodell zu erzeugen
    public EinstellungenFK() {
        // Laedt die Standardwerte aus dem Modell
        Einstellungen einstellungen = Einstellungen.standardWerte();
        // Erstellt das Modell des Fenstermodells
        this.fensterModell = new EinstellungenFM(einstellungen);
    } // Konstruktor um das Fenstermodell zu erzeugen

public void initialize(URL url, ResourceBundle rb) {
        LOG.info("Einstellungen werden initialisiert");
        lblQuellverzeichnis.textProperty().bind(fensterModell.pfadQuelleProperty());
    } // initialize

@FXML
    private void sucheQuellverzeichnis(ActionEvent event) {
        LOG.info("sucheQuellverzeichnis wurde aufgerufen");
        String verzeichnis = Datei.auswahlVerzeichnis("Wähle das Quellverzeichnis");
        fensterModell.schreibePfadQuelle(verzeichnis);
        lblQuellverzeichnis.textProperty().bind(fensterModell.pfadQuelleProperty());
        LOG.info("Selektiertes Quellverzeichnis " + fensterModell.holePfadQuelle());
    } // sucheQuellverzeichnis

    @FXML
    private void sucheZielverzeichnis(ActionEvent event) {
        LOG.info("sucheZiellverzeichnis wurde aufgerufen");
        String verzeichnis = Datei.auswahlVerzeichnis("Wähle das Zielverzeichnis");
        fensterModell.schreibePfadZiel(verzeichnis);
        lblZielverzeichnis.textProperty().bind(fensterModell.pfadZielProperty());
        LOG.info("Selektiertes Zielverzeichnis " + fensterModell.pfadZielProperty());
    } // sucheZielverzeichnis
```

Der Fenster Kontroller soll also nur die Daten vom Modell zum Fenster verbinden.
Also wird EinstellungenFM mit der variable fensterModell definiert, welche dann im Konstruktor irgendwie gefüllt werden?
Der Konstruktor so wie er da ist verstehe ich nicht so ganz. Ich weiß das dort durch die Methode die Pfade gesetzt werden.
Es wird ein Objekt einstellungen erzeugt und dort die die Werte übergeben.
Aber einstellungen wird doch nach dem Bauplan erzeugt. Wird in diesem Objekt tatsächlich Quellverzeichnis und Zielverzeichnis in den entsprechenden Platzhalter erzeugt und übergeben so wie es in der Vorgabe definiert wurde?

Wenn ich mein Programm mit beiden Daten (Quellverzeichnis und Zielverzeichnis) ausführe dann erhalte ich im Textfeld des Fensters nur den letzten also Zielverzeichnis.
Im label Quellverzeichnis wird der Pfad des Zielverzeichnisses angezeigt und im label Zielverzeichnis nichts.


----------



## looparda (16. Jul 2019)

MiMa hat gesagt.:


> Jedoch habe ich Schwierigkeiten zu verstehen warum bei der schreibePfadQuelle der Datentp Einstellungen gewählt wurde?
> Es wird doch nur den Pfad der einen String enthält zurück gegeben!
> Normalerweise definiere ich so wie bei schreibePfadZiel eine Methode die nichts zurückgibt mit "void" bei einer Set-Methode?


Ich habe hier bloß "fluent-Setter" benutzt (z.B. hier erklärt).


MiMa hat gesagt.:


> Zu diesem Zeitpunkt ist mir nicht bekannt, ob sich bereit Werte darin enthalten? Ich denke nicht, da die Methode standardWerte() noch nicht aufgerufen wurde?


Du gibst doch in den Konstruktor des ViewModels ein Einstellungs-Objekt rein, welches im Kontroller erzeugt wurde:


MiMa hat gesagt.:


> public EinstellungenFK() { // Laedt die Standardwerte aus dem Modell Einstellungen einstellungen = Einstellungen.standardWerte(); // Erstellt das Modell des Fenstermodells this.fensterModell = new EinstellungenFM(einstellungen); } // Konstruktor um das Fenstermodell zu erzeugen



standardWerte wurde zu dem Zeitpunkt aufgerufen, und das Einstellungs-Objekt auf die Werte gesetzt, die in standardWerte gesetzt werden. Das natürlich nur für dieses minimale Beispiel. Später liest du die Einstellungen aus einer Datei oder Properties (java.util).


MiMa hat gesagt.:


> Aber einstellungen wird doch nach dem Bauplan erzeugt. Wird in diesem Objekt tatsächlich Quellverzeichnis und Zielverzeichnis in den entsprechenden Platzhalter erzeugt und übergeben so wie es in der Vorgabe definiert wurde?


Auch hier: Es wird das gesetzt, was in standardWerte definiert ist.



MiMa hat gesagt.:


> Wenn ich mein Programm mit beiden Daten (Quellverzeichnis und Zielverzeichnis) ausführe dann erhalte ich im Textfeld des Fensters nur den letzten also Zielverzeichnis.


Du hast im Konstruktor nur eins von zwei Textfeldern gebunden und bindest in den Action-Handlern erneut Properties.
Die Bindings erledigst du ausschließlich im Konstruktor.
In den Action-Handlern setzt du die neuen Verzeichnispfade im ViewModel über die Setter ( schreibePfadZiel usw. ) und der View aktualisiert sich aufgrund der Bindings selbstständig.


----------



## MiMa (16. Jul 2019)

Ich hab es mir jetzt noch mal angeschaut und bin ein bisschen durcheinander.
Vielleicht hat jemand Zeit in form einer Nachhilfestunde mir das an meinem Code über Teamviever zu erklären.
Ich bin auch gerne bereit die Stunde zu bezahlen, bitte einfach eine PN senden.


----------



## MiMa (18. Jul 2019)

Mittlerweile habe ich es geschafft alle Werte in der Methode standardWerte() zu definieren und in das ViewModel zu transferieren und werden auch dort im Fenster angezeigt. Im Fenster kann ich durch meine Button Methoden die Verzeichnisse ändern und durch Logeinträge konnte ich kontrollieren, das die neuen Pfade auch tatsächlich im VielModell enthalten sind.

Ich weiß jetzt wie es in etwa funktionieren sollte, aber es herrscht bei mir immer noch in den ein oder anderen Punkten Unklarheiten.
Wobei im MVC auf ein Muster mit Model, View und Controller beschrieben wird, kann es für JavaFX anwendungen so nicht verwendet werden. Zumindest habe ich das jetzt so interpretiert, da die Properties nicht Model platziert werden können, wenn es auch um Persistente Speicherung der Modell-Daten geht?!?
Beim MVVM basiert der Entwurf auf ein Model, VieModel und der View. Hier im Forum habe ich jetzt gelernt zu diesem Entwurf noch einen ViewContoller hinzu zu nehmen der nur die Bindung übernimmt. Das macht es etwas übersichtlicher. So wie ich es auf der Wikipedia Seite gelesen habe, aber nicht nötig, da die Bindung, die Steuerung und Geschäftslogik im ViewController sein darf.
Mittlerweile habe ich aus dem Netz, aus Büchern und Tutorials unterschiedliche Informationen. Ich tue mich ein bisschen schwer damit um zu gehen, weil ich noch keine Erfahrung in GUI Programmierung gemacht habe und erst mit dieser Beispielapplikation damit angefangen habe.

Ich weiß jetzt, daß die View (Fenster) durch die Property Bindung im Controller mit dem ViewModel verbunden ist und jede Eingabe im Textfield automatisch aktualisiert wird. Jedoch ist mir nicht klar wie dann die Aktualisierung mit dem Modell der Standardlib in der ich implements Serializable verwendet habe?

Schließlich möchte ich die gesamten Daten im Einstellungen Fenster gerne auf Festplatte permanent speichern.
Um den Ablauf besser zu verstehen, habe ich mir einen kleine Hilfe erstellt. 

Ich habe Schwierigkeiten zu verstehen, wie ich die Daten Serialisieren kann, da ich nicht verstehe, wie die Daten aus dem ViewModel in mit dem Modell abgeglichen werden.


----------



## looparda (18. Jul 2019)

MiMa hat gesagt.:


> Wobei im MVC auf ein Muster mit Model, View und Controller beschrieben wird, kann es für JavaFX anwendungen so nicht verwendet werden. Zumindest habe ich das jetzt so interpretiert, da die Properties nicht Model platziert werden können, wenn es auch um Persistente Speicherung der Modell-Daten geht?!?


Doch, kann man schon machen, aber gibt halt Wildwuchs, in meinen Augen. Ich rate davon ab und bin für eine Trennung. MVVM bekommt diese Trennung in meinen Augen am leichtesten hin.



MiMa hat gesagt.:


> So wie ich es auf der Wikipedia Seite gelesen habe, aber nicht nötig, da die Bindung, die Steuerung und Geschäftslogik im ViewController sein darf.


Also alles in den ViewController? Das ist ja Quatsch. Das ViewModel enthält die Logik für die Darstellung und delegiert an das Model für die Geschäftslogik. Der einzige Job des (View)Controllers ist es die in MVVM zentrale Rolle des Binders einzunehmen und die Action Handler zu definieren, die wiederum nur an das ViewModel delegieren. Der Binder stellt die Bindings zwischen View und ViewModel her. Dank der Properties in JavaFX hat der Binder nicht viel zutun als eine Property des View mit der zugehörigen Property des ViewModels zu verbinden. Hätten wir keine Properties, würden wir im Binder vermutlich das Oberserver-Pattern nutzen und manuell Änderungen durch Listener mappen. Ich will mir gar nicht vorstellen, wie hässlich das im Vergleich wäre.


MiMa hat gesagt.:


> View (Fenster)


Ein View ist erstmal kein Fenster. Du kannst z.B. mehrere Instanzen eines Views im gleichen Fenster anzeigen. FensterModell ist deshalb auch keine gute Übersetzung, fällt mir auf.


MiMa hat gesagt.:


> Jedoch ist mir nicht klar wie dann die Aktualisierung mit dem Modell der Standardlib in der ich implements Serializable verwendet habe?


Du definierst in deiner View eine Action (z.B. Klick auf Button 'Speichern'), dazu definierst du einen Action-Handler im Controller. Der Controller delegiert die Action an das ViewModel. Das ViewModel validiert nun die Anfrage in Kooperation mit dem Modell. Beispielsweise wenn auf Speichern geklickt wurde sollen doch bitte alle Felder ausgefüllt sein. Falls ok: Speichern, falls nicht ok: nicht Speichern. Je nach Validierungsergebnis werden

die Werte des ViewModels in das Modell übertragen
die Geschäftslogik des Models aufgerufen (speichern) bzw. wird das Modell an einen für die Persistierung zuständigen Service weitergegeben
Properties aktualisiert (z.B. eine Fehler-Property oder "Alles OK und erfolgreich gespeichert")
Deine Grafik, kann ich leider nicht ganz deuten. Die Pfeile sind nicht definiert und ein Pfeil ist orange(?). Manche Pfeile gehen in eine Richtung und manche in zwei.


MiMa hat gesagt.:


> Ich habe Schwierigkeiten zu verstehen, wie ich die Daten Serialisieren kann, da ich nicht verstehe, wie die Daten aus dem ViewModel in mit dem Modell abgeglichen werden.


Das habe ich nun beantwortet. Ab da musst du nur noch das Schreiben und Lesen in ein File anstoßen.


----------



## MiMa (18. Jul 2019)

Mit der Grafik habe ich mir einen überblick verschafft, was passiert nachdem das JavaFX Programm ausgeführt wird.
Wie gesagt, ist JavaFX und die GUI Programmierung für mich neu.
Linien mit Doppelpfeilen sollen veranschaulichen, das die Daten hin und zurück gehen.
Der Roten Linien war das wo ich nicht wie es geregelt wird damit das Model die Daten vom VielModell aktualisiert.
Du hast mir gesagt, des es im Speicher Button passiert. Also Dort muss ich alle Variablen vom Modell Viel auf das Model übertragen und dann das Modell Speichern.

Ich werde den Button Code posten wenn ich es fertig habe.


----------



## MiMa (18. Jul 2019)

Der Code für den Button speichern, indem ich einen Variableninhalt von einem Modell in das andere Kopiere und einen Logeintrag mit dem Wert erzeuge und dann das Modell auf Festplatte Speichern.


```
public static void schreibeEinstellungenHD(Einstellungen einstellungen) {
        OutputStream ausgabeStreamDatei = null;
        try {
            ausgabeStreamDatei = new FileOutputStream(einstellungen.holePfadEinstellungen()+"\\Einstellungen.ser");
            ObjectOutputStream ausgabeStreamObjekt = new ObjectOutputStream(ausgabeStreamDatei);
            ausgabeStreamObjekt.writeObject(einstellungen);
            ausgabeStreamDatei.close();
            LOG.info("Einstellungen wurden auf Festplatte gespeichert");
        } catch (Exception e) {
            LOG.info("Es trat ein Fehler beim schreiben der Programmeinstellungen auf", e);
        }
    } // schreibeEinstellungenHD
```



```
@FXML
    private void einstellungenSpeichern(ActionEvent event) throws IOException {
        LOG.info("Methode einstellungenSpeichern wurde aufgerufen");
        // Alle gemachten Einstellungen werden dauerhaft gespeichert
        einstellungen.schreibePfadQuelle(fensterModell.holePfadQuelle());
        LOG.info("Modell Einstellungen Pfad Quelle : " + einstellungen.holePfadQuelle());    
        Einstellungen.schreibeEinstellungenHD(einstellungen);
    } // einstellungenSpeichern
```

Der Code gibt in der Netbeans iDE keine Fehler an, erzeugt aber beim ausführen folgende Fehler in der Konsole.
Die Meldung ist ziemlich umfangreich, wenn man bedenkt, das ich nur eine Variablen von einem Model ins andere Modell kopiere und einen LOG ausgeben wollte.
Der Logeintrag wurde nicht erzeugt.
FXML Loader hat doch was mit dem Fenster zu tun?
Das Fenster wurde ordnungsgemäß erstellt und dargestellt, mit dem Speichern Button wird weder einen neues Fenster erzeugt oder dargestellt.


```
Exception in thread "JavaFX Application Thread" java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
    at javafx.fxml/javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1787)
    at javafx.fxml/javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(FXMLLoader.java:1670)
    at javafx.base/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
    at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
    at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
    at javafx.base/com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
    at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
    at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base/com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
    at javafx.base/com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49)
    at javafx.base/javafx.event.Event.fireEvent(Event.java:198)
    at javafx.graphics/javafx.scene.Node.fireEvent(Node.java:8865)
    at javafx.controls/javafx.scene.control.Button.fire(Button.java:200)
    at javafx.controls/com.sun.javafx.scene.control.behavior.ButtonBehavior.mouseReleased(ButtonBehavior.java:206)
    at javafx.controls/com.sun.javafx.scene.control.inputmap.InputMap.handle(InputMap.java:274)
    at javafx.base/com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:218)
    at javafx.base/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
    at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
    at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
    at javafx.base/com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
    at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
    at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base/com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
    at javafx.base/com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
    at javafx.base/javafx.event.Event.fireEvent(Event.java:198)
    at javafx.graphics/javafx.scene.Scene$MouseHandler.process(Scene.java:3876)
    at javafx.graphics/javafx.scene.Scene$MouseHandler.access$1300(Scene.java:3604)
    at javafx.graphics/javafx.scene.Scene.processMouseEvent(Scene.java:1874)
    at javafx.graphics/javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2613)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:397)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:295)
    at java.base/java.security.AccessController.doPrivileged(Native Method)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$2(GlassViewEventHandler.java:434)
    at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:389)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:433)
    at javafx.graphics/com.sun.glass.ui.View.handleMouseEvent(View.java:556)
    at javafx.graphics/com.sun.glass.ui.View.notifyMouse(View.java:942)
    at javafx.graphics/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at javafx.graphics/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:175)
    at java.base/java.lang.Thread.run(Thread.java:844)
Caused by: java.lang.reflect.InvocationTargetException
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:564)
    at com.sun.javafx.reflect.Trampoline.invoke(MethodUtil.java:76)
    at jdk.internal.reflect.GeneratedMethodAccessor2.invoke(Unknown Source)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:564)
    at javafx.base/com.sun.javafx.reflect.MethodUtil.invoke(MethodUtil.java:275)
    at javafx.fxml/com.sun.javafx.fxml.MethodHelper.invoke(MethodHelper.java:83)
    at javafx.fxml/javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1782)
    ... 47 more
Caused by: java.lang.NullPointerException
    at einstellungen.EinstellungenFK.einstellungenSpeichern(EinstellungenFK.java:165)
    ... 58 more
```


----------



## looparda (18. Jul 2019)

Du bist im zweiten Code offensichtlich im Controller, hier solltest du die Einstellungen nicht anfassen. Alles geht über das ViewModel. Lies hierzu erneut:


looparda hat gesagt.:


> Du definierst in deiner View eine Action (z.B. Klick auf Button 'Speichern'), dazu definierst du einen Action-Handler im Controller. Der Controller delegiert die Action an das ViewModel. Das ViewModel validiert nun die Anfrage in Kooperation mit dem Modell. Beispielsweise wenn auf Speichern geklickt wurde sollen doch bitte alle Felder ausgefüllt sein. Falls ok: Speichern, falls nicht ok: nicht Speichern. Je nach Validierungsergebnis werden
> 
> die Werte des ViewModels in das Modell übertragen
> die Geschäftslogik des Models aufgerufen (speichern) bzw. wird das Modell an einen für die Persistierung zuständigen Service weitergegeben
> Properties aktualisiert (z.B. eine Fehler-Property oder "Alles OK und erfolgreich gespeichert")



Im Action Handler einstellungenSpeichern setzt du immer noch Bindings, die eigentlich nur im Konstruktor einmalig gesetzt werden sollen. Ich dachte das hatte ich im vorhergehenden Beiträgen schon bemängelt.

Eine NullPointerException deutet darauf hin, dass eins deiner Attribute null ist. Das kann leicht passieren, wenn man ein Feld anders als im FXML benennt, denn das Mapping passiert über den Namen. Ansonsten steht in der Exception explizit die Quelle des Übels ((EinstellungenFK.java:165)).


----------



## MiMa (19. Jul 2019)

Aktuell habe ich noch ein Problem mit dem Binden der Textfelder im FensterKontroller.
Ich hatte bisher immer nur die Labels gebunden und das funktioniert auch soweit ganz gut.
Nun habe ich auch die TextFelder eingebunden (TextField) und ab dem Zeitpunkt kann ich keine Eingaben mehr im Fenster machen wenn das Programm läuft?

Auszug aus "EinstellungenFensterKontroller"

```
// Einstellungen Fenster Kontroller
@FXML
    private Label lblQuellverzeichnis;
    @FXML
    private Label lblZielverzeichnis;
    @FXML
    ...
    ...

    @FXML
    private TextField tfDBServeradresse;
    @FXML
    private TextField tfDBDatenbankname;
    @FXML
    private TextField tfDBBenutzername;
    @FXML
    private TextField tfDBPasswort;
    ...
    ...

    // Datenmodelle
    private EinstellungenFM fensterModell;
    private Einstellungen einstellungen;

    // Konstruktor um das Fenstermodell zu erzeugen
    public EinstellungenFK() {
        // Erzeugt ein Modell-Objekt Einstellungen mit Standardwerten
        this.einstellungen = Einstellungen.standardWerte();

        // Erzeugt ein Fenster-Modell aus dem Einstellungen Model-Objekt
        this.fensterModell = new EinstellungenFM(einstellungen);
    } // Konstruktor um das Fenstermodell zu erzeugen

    /**
     * Initialisierung der Kontroller Klasse.
     */
    @Override
    public void initialize(URL url, ResourceBundle rb) {
        LOG.info("Einstellungen werden initialisiert");
        // Binden der Werte an das Fenster
        lblQuellverzeichnis.textProperty().bind(fensterModell.pfadQuelleProperty());
        lblZielverzeichnis.textProperty().bind(fensterModell.pfadZielProperty());
        ...
        ...
        tfDBServeradresse.textProperty().bind(fensterModell.dbServeradresseProperty());
        tfDBDatenbankname.textProperty().bind(fensterModell.dbDatenbanknameProperty());
        tfDBBenutzername.textProperty().bind(fensterModell.dbBenutzernameProperty());
        tfDBPasswort.textProperty().bind(fensterModell.dbPasswortProperty());
   } // initialize

...
@FXML
    private void einstellungenSpeichern(ActionEvent event) throws IOException {
        LOG.info("Methode einstellungenSpeichern wurde aufgerufen");
        EinstellungenFM.einstellungenSpeichernHD(fensterModell, einstellungen);
    } // einstellungenSpeichern
...
} // Einstellungen Fenster Kontroller
```

Auszug aus "Einstellungen Fenster Modell"

```
// Einstellungen Fenster Modell
...
    private final StringProperty pfadEinstellungen = new SimpleStringProperty();
    private final StringProperty pfadQuelle = new SimpleStringProperty();
    ...
    // Erstellt das Fenster Modell Einstellungen
    public EinstellungenFM(Einstellungen einstellungen) {
        // Holt die Werte aus den Variablen in das Fenster Modell
        pfadEinstellungen.set(einstellungen.holePfadEinstellungen());
        pfadQuelle.set(einstellungen.holePfadQuelle());
        ...
        dbServeradresse.set(einstellungen.holeDbServeradresse());
        dbDatenbankname.set(einstellungen.holeDbDatenbankname());
        dbBenutzername.set(einstellungen.holeDbBenutzername());
        dbPasswort.set(einstellungen.holeDbPasswort());
    } // Konstruktor Fenster Modell
   
        public static void einstellungenSpeichernHD(EinstellungenFM fensterModell, Einstellungen einstellungen) {
        einstellungen.schreibePfadQuelle(fensterModell.holePfadQuelle());
        ...
        einstellungen.schreibeDbServeradresse(fensterModell.holeDbServeradresse());
        einstellungen.schreibeDbDatenbankname(fensterModell.holeDbDatenbankname());
        einstellungen.schreibeDbBenutzername(fensterModell.holeDbBenutzername());
        ...      
        LOG.info("Modell Einstellungen Pfad Quelle : " + einstellungen.holePfadQuelle());
        LOG.info("Modell Einstellungen Pfad Ziel : " + einstellungen.holePfadZiel());
        ...      
        LOG.info("Modell Einstellungen DB Serveradresse : " + einstellungen.holeDbServeradresse());
        LOG.info("Modell Einstellungen DB Datenbankname : " + einstellungen.holeDbDatenbankname());
        LOG.info("Modell Einstellungen DB Benutzername : " + einstellungen.holeDbBenutzername());
        ...
        Einstellungen.schreibeEinstellungenHD(einstellungen);
    } // einstellungenSpeichernHD
   
     public String holePfadEinstellungen() {
        return pfadEinstellungen.get();
    }
   
    public StringProperty pfadEinstellungenProperty() {
        return pfadEinstellungen;
    }
   
    public void schreibePfadEinstellungen(String pfadEinstellungen) {
        this.pfadEinstellungen.set(pfadEinstellungen);
    }
    ...
```
Auszug aus Einstellungen

```
// Einstellungen
public class Einstellungen implements Serializable {

    private String pfadEinstellungen;
    private String pfadQuelle;
    ...
    private String dbServeradresse;
    private String dbDatenbankname;
    private String dbBenutzername;
    private String dbPasswort;
    ...
    // Methode für das einsetzen von Standard Werten
    public static Einstellungen standardWerte() {
        String heimVerzeichnis = System.getProperty("user.home");
        Einstellungen standardWerte = new Einstellungen();
        standardWerte.schreibePfadQuelle(heimVerzeichnis+"\\Dateien\\01 Quelle");
        standardWerte.schreibePfadZiel(heimVerzeichnis+"\\Dateien\\02 Ziel");  
        ...
        return standardWerte;
    } // Methode für das einsetzen von Standard Werten
   
public String holePfadQuelle() {
        return pfadQuelle;
    }
   
    public Einstellungen schreibePfadQuelle(String quellVerzeichnis) {
        this.pfadQuelle = quellVerzeichnis;
        return this;
    }
...
public static void schreibeEinstellungenHD(Einstellungen einstellungen) {
        OutputStream ausgabeStreamDatei = null;
        try {
            ausgabeStreamDatei = new FileOutputStream(einstellungen.holePfadEinstellungen()+"\\Einstellungen.ser");
            ObjectOutputStream ausgabeStreamObjekt = new ObjectOutputStream(ausgabeStreamDatei);
            ausgabeStreamObjekt.writeObject(einstellungen);
            ausgabeStreamDatei.close();
            LOG.info("Einstellungen wurden auf Festplatte gespeichert");
        } catch (Exception e) {
            LOG.info("Es trat ein Fehler beim schreiben der Programmeinstellungen auf", e);
        }
    } // schreibeEinstellungenHD
...
} // Einstellungen
```

Ich habe versucht die Aufgabe zu lösen wie looparda es beschrieben hat.
1. Eine Klasse Einstellungen erstellt mit allen benötigten Parameter im Standard Java
2. Ein Klasse EinstellungenFensterModell die im alle Parameter enthält wie in 1. nur das die Werte Getter und Setter in Properties definiert sind
3. Eine Klasse EinstellungenFensterKontroller der zum Binden des Fenster Modelles und dem Fenster dient.

Im FensterKontroller Konstruktor wird ein EinstellungenObjekt erstellt, welches die Standard Werte aus dem Konstruktor holt.
Weiter wird im Konstruktor ein FensterModell Objekt erstellt, welches auf dem EinstellungenObjekt Basiert.

In der Initialisierung des FensterKontrollers wird das Fenster mit dem FensterModell gebunden.
Wenn im Fenster auf Speichern geklickt wird, dann wird im FensterKontroller die Methode "EinstellungenSpeichern" ausgeführt, worin eine Methode ausgeführt wird die im FensterModell Klasse definiert wurde. Als Parameter werden die im FensterKontroller erzeugten Einstellungen Objekt und fensterModell Objekt übergeben.
In der Methode der FensterModell Klasse werden dann die Daten vom FensterModell in das Einstellungen Objekt übertragen, wobei dann noch die Methode "schreibeEinstellungenHD" ausgeführt wird mit dem Paramter des einstellungen objektes.
Die Methode ist definiert in der Klasse Einstellungen und schreibt dort das Objekt Einstellungen auf die Festplatte.

Aktuell funktioniert das noch nicht wirklich so, da erst mal das Problem des TextFields gelöst werden muss um keine leeren Datenobjekte zu haben.

Ich hoffe, das ich das ganze so richtig verstanden habe?


----------



## looparda (19. Jul 2019)

MiMa hat gesagt.:


> kann ich keine Eingaben mehr im Fenster machen wenn das Programm läuft?


Das Binding ist unidirektional. Benutze .bindBidirectional, um Änderungen in beide Richtungen zu ermöglichen - sorry, das hatte ich falsch in meinem Snippet vorgegeben.



MiMa hat gesagt.:


> EinstellungenFM.einstellungenSpeichernHD(fensterModell, einstellungen);


Du kannst im ViewModell ganz normal eine Methode anbieten, denn im ViewModel hast du die benötigten Objekte einstellungen und den Zustand des ViewModell selbst - also keine Notwenigkeit static zu nutzen. `viewmodel.einstellungenSpeichernHD();`

Die Implementierung von einstellungenSpeichernHD wiederum gehört dort nicht rein sondern in das Model oder einen externen Service. Lies dazu erneut:


looparda hat gesagt.:


> Der View ruft Methoden aus dem ViewModel auf, wenn Aktionen auftreten wie _Button gedrückt_. Das ViewModel entscheidet was passieren soll, wenn die Methode aufgerufen wurde. Beispielsweise führst du im ViewModel eine Validierung durch, setzt eine Fehlerproperty, die dafür sorgt, dass im View der Fehler sichtbar wird. [Dann mappst du das ViewModel ins Model] und rufst letztlich eine Methode aus dem Modell [oder einen Service zum Speichern] auf.



Ich habe die Implementierungen jetzt nicht genau gelesen sondern bin eher über den Datenfluss und die Struktur geflogen. Ansonsten sieht das jetzt schon viel besser aus!


----------



## MiMa (19. Jul 2019)

Vielen Dank, das hat jetzt funktioniert.
Bei den Labels ging es Unidirektional weil ich direkt in das Modell geschrieben habe, was sich im Label aktualisiert hat.
Bei den Textfeldern hat Bidirektional gut funktioniert.
Auch die Datei "Einstellungen.ser"  wurde ohne Fehlermeldungen in einem geänderten Pfad geschrieben.

Ich muss mich jetzt noch mal für die tolle Unterstützung bedanken, sowas als Beispiel habe ich nirgendwo finden können und bin froh das es jetzt funktioniert. Auch gelernt habe ich super viel davon und sehe das es nicht so ganz easy ist.

Morgen mache ich mich ran die Datei zu lesen.
Jetzt habe ich einen funktionierenden *Einstellungen* Dialog in dem Standardverzeichnisse enthalten sind, die ich sogar ändern kann und Eingaben durch Textfelder. Die Daten werden jetzt zwar auf Festplatte gespeichert, aber wie mache ich dann all die Daten im gesamten Programm verfügbar?
Gebe ich das Einstellungen Objekt jetzt Klasse für Klasse weiter?
Lade ich es von Festplatte wenn ich Daten davon Benötige?


----------



## looparda (20. Jul 2019)

MiMa hat gesagt.:


> Die Daten werden jetzt zwar auf Festplatte gespeichert, aber wie mache ich dann all die Daten im gesamten Programm verfügbar?


Eine Möglichkeit ist es die Einstellungen bei Programmstart einzulesen und sicherzustellen, dass es im gesamten Verlauf nur ein einziges Objekt davon gibt (Umsetzung mit Singleton-Pattern), welches global überall zugreifbar ist. Das erzeugt jedoch eine hohe Kopplung zwischen der Einstellungen-Klasse und den Klassen, wo sie genutzt wird. Es verhindert gut testbaren Code zu schreiben. Ich bin mir unsicher, ob ich dir wirklich zum Singleton raten sollte.
So lange du an keine Grenzen stößt würde ich es verwenden. Spätestens wenn du dich ernsthaft mit Testen auseinandersetzt fällt es dir auf die Füße und du wirst nach Alternativen suchen.

Eine Alternative dazu wäre erstmal ein Interface für die Einstellungen einzuführen, um die Kopplung zu lösen (Dependency Inversion) und die Einstellungen (von einem Dependency Injection Framework erzeugen lassen oder selbst erzeugen) und in die abhängigen Klassen zu injecten (Dependency Injection).
Weiterhin würde man dann versuchen die Einstellungen in verschiedene Sichten aufzusplitten (z.B. Pfade, Datenbankeinstellungen, Sicherheitseinstellungen) (Interface segregation principle), um die Kopplung weiter zu verringern. 
Hier wird es erst richtig spannend und da lerne selbst noch ständig dazu. Mich würden auch andere Alternative oder Meinungen zum Singleton interessieren.


----------



## MiMa (7. Aug 2019)

Aktuell bin ich jetzt dabei die Programmeinstellungen zu laden.
Ich habe im Einstellungen Fenster einen Lade Button platziert der beim Drücken die Einstellungen von der Festplatte laden soll.
Da beim Aufruf des Einstellungen Fenster alles Standardwerte geladen werden kann ich beim drücken vom Lade Button sehen ob die Funktion korrekt arbeitet, denn dann müssten sich die Pfade ändern.
Der Lade Button wird später entfernt da ich dann beim Programmstart die Einstellungen automatisch von Festplatte laden möchte, wenn die Einstellungen Datei existiert.

Die aktuelle Fehlermeldung beim Laden

```
INFO  einstellungen.Einstellungen - Es trat ein Fehler beim laden der Einstellungen auf
java.lang.ClassCastException: einstellungen.Einstellungen cannot be cast to einstellungen.EinstellungenFM
    at einstellungen.Einstellungen.ladeEinstellungenHD(Einstellungen.java:207) ~[DMS.jar:1.0]
    at einstellungen.EinstellungenFM.einstellungenLadenHD(EinstellungenFM.java:93) ~[DMS.jar:1.0]
    at einstellungen.EinstellungenFK.einstellungenLaden(EinstellungenFK.java:176) ~[DMS.jar:1.0]
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:?]
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:?]
    at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:?]
    at java.lang.reflect.Method.invoke(Method.java:564) ~[?:?]
    at com.sun.javafx.reflect.Trampoline.invoke(MethodUtil.java:76) ~[javafx.base:?]
    at jdk.internal.reflect.GeneratedMethodAccessor2.invoke(Unknown Source) ~[?:?]
    at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:?]
    at java.lang.reflect.Method.invoke(Method.java:564) ~[?:?]
    at com.sun.javafx.reflect.MethodUtil.invoke(MethodUtil.java:275) ~[javafx.base:?]
    at com.sun.javafx.fxml.MethodHelper.invoke(MethodHelper.java:83) ~[javafx.fxml:?]
    at javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1782) ~[javafx.fxml:?]
    at javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(FXMLLoader.java:1670) ~[javafx.fxml:?]
    at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86) ~[javafx.base:?]
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238) ~[javafx.base:?]
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191) ~[javafx.base:?]
    at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59) ~[javafx.base:?]
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58) ~[javafx.base:?]
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114) ~[javafx.base:?]
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56) ~[javafx.base:?]
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114) ~[javafx.base:?]
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56) ~[javafx.base:?]
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114) ~[javafx.base:?]
    at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74) ~[javafx.base:?]
    at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49) ~[javafx.base:?]
    at javafx.event.Event.fireEvent(Event.java:198) ~[javafx.base:?]
    at javafx.scene.Node.fireEvent(Node.java:8865) ~[javafx.graphics:?]
    at javafx.scene.control.Button.fire(Button.java:200) ~[javafx.controls:?]
    at com.sun.javafx.scene.control.behavior.ButtonBehavior.mouseReleased(ButtonBehavior.java:206) ~[javafx.controls:?]
    at com.sun.javafx.scene.control.inputmap.InputMap.handle(InputMap.java:274) ~[javafx.controls:?]
    at com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:218) ~[javafx.base:?]
    at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80) ~[javafx.base:?]
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238) ~[javafx.base:?]
    at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191) ~[javafx.base:?]
    at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59) ~[javafx.base:?]
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58) ~[javafx.base:?]
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114) ~[javafx.base:?]
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56) ~[javafx.base:?]
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114) ~[javafx.base:?]
    at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56) ~[javafx.base:?]
    at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114) ~[javafx.base:?]
    at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74) ~[javafx.base:?]
    at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54) ~[javafx.base:?]
    at javafx.event.Event.fireEvent(Event.java:198) ~[javafx.base:?]
    at javafx.scene.Scene$MouseHandler.process(Scene.java:3876) ~[javafx.graphics:?]
    at javafx.scene.Scene$MouseHandler.access$1300(Scene.java:3604) ~[javafx.graphics:?]
    at javafx.scene.Scene.processMouseEvent(Scene.java:1874) ~[javafx.graphics:?]
    at javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2613) ~[javafx.graphics:?]
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:397) ~[javafx.graphics:?]
    at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:295) ~[javafx.graphics:?]
    at java.security.AccessController.doPrivileged(Native Method) ~[?:?]
    at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$2(GlassViewEventHandler.java:434) ~[javafx.graphics:?]
    at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:389) ~[javafx.graphics:?]
    at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:433) ~[javafx.graphics:?]
    at com.sun.glass.ui.View.handleMouseEvent(View.java:556) ~[javafx.graphics:?]
    at com.sun.glass.ui.View.notifyMouse(View.java:942) ~[javafx.graphics:?]
    at com.sun.glass.ui.win.WinApplication._runLoop(Native Method) ~[javafx.graphics:?]
    at com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:175) ~[javafx.graphics:?]
    at java.lang.Thread.run(Thread.java:844) [?:?]
INFO  start.Hauptfenster - Programm beenden wurde im Menue ausgewaehlt
INFO  start.Start - Programm-Ende wird ausgeführt stop()-Methode
```

Methode im Einstellungen Fenster Kontroller

```
@FXML
    private void einstellungenLaden(ActionEvent event) {
        LOG.info("einstellungenLaden wurde aufgerufen");
        fensterModell.einstellungenLadenHD(einstellungen);
    } // einstellungenLaden
```

Methode im Einstellungen Fenster Modell

```
public EinstellungenFM einstellungenLadenHD(Einstellungen einstellungen){
        EinstellungenFM fensterModell = null;
        Einstellungen.ladeEinstellungenHD(einstellungen);
        return fensterModell;
    } // einstellungenLadenHD
```

Methode in der Klasse Einstellungen

```
public static EinstellungenFM ladeEinstellungenHD(Einstellungen einstellungen) {
         InputStream eingabeStreamDatei = null;
         EinstellungenFM fensterModell = null;
         try {
             LOG.info("Einstellungen Pfad : " + einstellungen.holePfadEinstellungen());
             eingabeStreamDatei = new FileInputStream(einstellungen.holePfadEinstellungen()+"\\Einstellungen.ser");
             ObjectInputStream eingabeStreamObjekt = new ObjectInputStream(eingabeStreamDatei);
             fensterModell = (EinstellungenFM) eingabeStreamObjekt.readObject();
             LOG.info("Die Einstellungen wurden in das Modell geladen");
             LOG.info("Quellverzeichnis im Modell : " + fensterModell.holePfadQuelle());
             LOG.info("Verzeichnis Einstellungen im Modell : " + fensterModell.holePfadEinstellungen());
             return fensterModell;
         } catch (Exception e) {
             LOG.info("Es trat ein Fehler beim laden der Einstellungen auf" , e);
         }
     } // ladeEinstellungenHD
```

Es wurde schon mal angesprochen, dass ich meine Variablen bezüglich der Modelle nicht wirklich gut benannt habe, aber das wird sich ändern wenn ich das Prinzip und und die Funktionsweise besser verstanden habe.


----------



## kneitzel (7. Aug 2019)

Die Fehlermeldung sagt es doch schon:
java.lang.ClassCastException: einstellungen.Einstellungen cannot be cast to einstellungen.EinstellungenFM

Du versuchst eine Instanz vom Typ Einstellungen in einer Variablen vom Typ EinstellungenFM zu speichern und der Cast scheint nicht möglich.

Deine Klassen Einstellungen und EinstellungenFM kenne ich aber nicht ebenso weiss ich nicht, was du in deiner Datei wie gespeichert hast. Aber die Zeile mit der Fehlermeldung ("at einstellungen.Einstellungen.ladeEinstellungenHD(Einstellungen.java:207)")
scheint die folgende zu sein:
`fensterModell = (EinstellungenFM) eingabeStreamObjekt.readObject();`


----------



## mrBrown (7. Aug 2019)

MiMa hat gesagt.:


> Es wurde schon mal angesprochen, dass ich meine Variablen bezüglich der Modelle nicht wirklich gut benannt habe, aber das wird sich ändern wenn ich das Prinzip und und die Funktionsweise besser verstanden habe.


Andersrum ist’s einfacher  Alles so gut wie möglich zu benennen macht das Verstehen deutlich einfacher...


----------



## MiMa (7. Aug 2019)

Die Schreib Methode die zuvor Diskutiert wurde arbeitet ohne Fehler und schreibt ein Objekt auf die Festplatte.
Demnach müsste es doch ausreichen, wenn ich in der Lese Methode das Objekt erstelle und es dann mit

```
fensterModell = (EinstellungenFM) eingabeStreamObjekt.readObject();
```
wieder ein zu lesen.


----------



## mrBrown (7. Aug 2019)

Offensichtlich schreibst du ein Einstellungen-Objekt und versuchst es als EinstellungenFM zu lesen, das klappt natürlich nicht 

Halte EinstellungenFM gänzlich aus deinem Model raus, dann gibt es solche Fehler erst gar nicht


----------



## MiMa (7. Aug 2019)

Ach ja, das Stimmt.
Du hast recht, das schreiben der Objekt-Datei war ein Einstellungen Objekt.
Ich erinnere mich das die Methode writeObjekt nicht mit Properties umgehen kann.
Ich komme damit immer noch etwas durcheinander, mir fehlt halt die Übung.
Ich schreibe die Methoden neu und hoffe das es diesmal klappt 
Danke


----------



## MiMa (7. Aug 2019)

Die Laden Methode funktioniert soweit.

```
INFO  start.Start - Das Programm wird initialisiert init()-Methode
INFO  start.Start - Das Programm-Hauptfenster wird initialisiert start()-Methode
INFO  start.Hauptfenster - Einstellungen wurde im Menue ausgewaehlt
INFO  einstellungen.EinstellungenFK - Einstellungen werden initialisiert
INFO  einstellungen.EinstellungenFK - einstellungenLaden wurde aufgerufen
INFO  einstellungen.Einstellungen - Pfad zum  laden von Einstellungen : C:\Users\Michael\JApp\Einstellungen.ser
INFO  einstellungen.Einstellungen - Die Einstellungen wurden in das Modell geladen
INFO  einstellungen.Einstellungen - Quellverzeichnis im Modell : B:\01 Quelle
INFO  start.Hauptfenster - Programm beenden wurde im Menue ausgewaehlt
INFO  start.Start - Programm-Ende wird ausgeführt stop()-Methode
```

Nachdem ich dann im Einstellungen Fenster Modell die werte des Einstellungen Modell in das Fenster Modell übertragen wollte, trat dann wieder ein Fehler in der Konsole auf. Im Log waren keine Fehler ausgegeben worden.

Methode Laden aus der Klasse Einstellungen

```
public static Einstellungen leseEinstellungenHD(Einstellungen einstellungen) {
         InputStream eingabeStreamDatei = null;
         Einstellungen hdEinstellungen = new Einstellungen();
         String dateiEinstellungen = einstellungen.holePfadEinstellungen()+"\\Einstellungen.ser";
         LOG.info("Pfad zum  laden von Einstellungen : " + dateiEinstellungen);
         try {
             eingabeStreamDatei = new FileInputStream(dateiEinstellungen);
             ObjectInputStream eingabeStreamObjekt = new ObjectInputStream(eingabeStreamDatei);
             hdEinstellungen = (Einstellungen) eingabeStreamObjekt.readObject();
           
             LOG.info("Quellverzeichnis von HD geladen : " + hdEinstellungen.holePfadQuelle());
         } catch (Exception e) {
             LOG.info("Es trat ein Fehler beim laden der Einstellungen auf" , e);
         }
         return hdEinstellungen;
     } // ladeEinstellungenHD
```

Methode aus Einstellungen Fenster Modell

```
public EinstellungenFM einstellungenLadenHD(Einstellungen einstellungen){
        EinstellungenFM fensterModell = null;
        Einstellungen einstellungenHD = new Einstellungen();
        einstellungenHD = Einstellungen.leseEinstellungenHD(einstellungen);
       
        // Werte in das Fenster-Modell übetragen
        fensterModell.schreibePfadQuelle(einstellungenHD.holePfadQuelle());
        fensterModell.schreibePfadZiel(einstellungenHD.holePfadZiel());
        fensterModell.schreibePfadOCR(einstellungenHD.holePfadOCR());
        fensterModell.schreibePfadOhneDokumentArt(einstellungenHD.holePfadOhneDokumentArt());
        fensterModell.schreibePfadLoeschen(einstellungenHD.holePfadLoeschen());
        fensterModell.schreibePfadGeschuetzt(einstellungenHD.holePfadGeschuetzt());
        fensterModell.schreibePfadBild(einstellungenHD.holePfadBild());
        fensterModell.schreibePfadEinstellungen(einstellungenHD.holePfadEinstellungen());
       
        fensterModell.schreibeDbServeradresse(einstellungenHD.holeDbServeradresse());
        fensterModell.schreibeDbDatenbankname(einstellungenHD.holeDbDatenbankname());
        fensterModell.schreibeDbBenutzername(einstellungenHD.holeDbBenutzername());
        fensterModell.schreibeDnbToken(einstellungenHD.holeDnbToken());
       
        return fensterModell;
    } // einstellungenLadenHD
```

Fehler in der Konsole

```
Exception in thread "JavaFX Application Thread" java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
    at javafx.fxml/javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1787)
    at javafx.fxml/javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(FXMLLoader.java:1670)
    at javafx.base/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
    at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
    at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
    at javafx.base/com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
    at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
    at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base/com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
    at javafx.base/com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49)
    at javafx.base/javafx.event.Event.fireEvent(Event.java:198)
    at javafx.graphics/javafx.scene.Node.fireEvent(Node.java:8865)
    at javafx.controls/javafx.scene.control.Button.fire(Button.java:200)
    at javafx.controls/com.sun.javafx.scene.control.behavior.ButtonBehavior.mouseReleased(ButtonBehavior.java:206)
    at javafx.controls/com.sun.javafx.scene.control.inputmap.InputMap.handle(InputMap.java:274)
    at javafx.base/com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:218)
    at javafx.base/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
    at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
    at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
    at javafx.base/com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
    at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
    at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
    at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
    at javafx.base/com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
    at javafx.base/com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
    at javafx.base/javafx.event.Event.fireEvent(Event.java:198)
    at javafx.graphics/javafx.scene.Scene$MouseHandler.process(Scene.java:3876)
    at javafx.graphics/javafx.scene.Scene$MouseHandler.access$1300(Scene.java:3604)
    at javafx.graphics/javafx.scene.Scene.processMouseEvent(Scene.java:1874)
    at javafx.graphics/javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2613)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:397)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:295)
    at java.base/java.security.AccessController.doPrivileged(Native Method)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$2(GlassViewEventHandler.java:434)
    at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:389)
    at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:433)
    at javafx.graphics/com.sun.glass.ui.View.handleMouseEvent(View.java:556)
    at javafx.graphics/com.sun.glass.ui.View.notifyMouse(View.java:942)
    at javafx.graphics/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at javafx.graphics/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:175)
    at java.base/java.lang.Thread.run(Thread.java:844)
Caused by: java.lang.reflect.InvocationTargetException
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:564)
    at com.sun.javafx.reflect.Trampoline.invoke(MethodUtil.java:76)
    at jdk.internal.reflect.GeneratedMethodAccessor2.invoke(Unknown Source)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:564)
    at javafx.base/com.sun.javafx.reflect.MethodUtil.invoke(MethodUtil.java:275)
    at javafx.fxml/com.sun.javafx.fxml.MethodHelper.invoke(MethodHelper.java:83)
    at javafx.fxml/javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1782)
    ... 47 more
Caused by: java.lang.NullPointerException
    at einstellungen.EinstellungenFM.einstellungenLadenHD(EinstellungenFM.java:96)
    at einstellungen.EinstellungenFK.einstellungenLaden(EinstellungenFK.java:176)
    ... 58 more
```


----------



## MiMa (7. Aug 2019)

Ich wollte den obigen Post noch korrigieren aber die Zeit war abgelaufen.
Ich habe den Fehler habe ich selbst gefunden. So funktioniert es.

```
public EinstellungenFM einstellungenLadenHD(Einstellungen einstellungen){
        EinstellungenFM fensterModell = new EinstellungenFM(einstellungen);
//        EinstellungenFM fensterModell = null;
        Einstellungen einstellungenHD = new Einstellungen();
        einstellungenHD = Einstellungen.leseEinstellungenHD(einstellungen);

        // Werte in das Fenster-Modell übetragen
        fensterModell.schreibePfadQuelle(einstellungenHD.holePfadQuelle());
        fensterModell.schreibePfadZiel(einstellungenHD.holePfadZiel());
        fensterModell.schreibePfadOCR(einstellungenHD.holePfadOCR());
        fensterModell.schreibePfadOhneDokumentArt(einstellungenHD.holePfadOhneDokumentArt());
        fensterModell.schreibePfadLoeschen(einstellungenHD.holePfadLoeschen());
        fensterModell.schreibePfadGeschuetzt(einstellungenHD.holePfadGeschuetzt());
        fensterModell.schreibePfadBild(einstellungenHD.holePfadBild());
        fensterModell.schreibePfadEinstellungen(einstellungenHD.holePfadEinstellungen());

        fensterModell.schreibeDbServeradresse(einstellungenHD.holeDbServeradresse());
        fensterModell.schreibeDbDatenbankname(einstellungenHD.holeDbDatenbankname());
        fensterModell.schreibeDbBenutzername(einstellungenHD.holeDbBenutzername());
        fensterModell.schreibeDnbToken(einstellungenHD.holeDnbToken());

        return fensterModell;
    } // einstellungenLadenHD
```

Anschliessend die neuen Daten dann im Fenster aktualisiert anzeigen lassen.
Methode im Fenster Kontroller

```
@FXML
    private void einstellungenLaden(ActionEvent event) {
        LOG.info("einstellungenLaden wurde aufgerufen");
//        fensterModell.einstellungenLadenHD(einstellungen);
        EinstellungenFM fensterModellHD = new EinstellungenFM(einstellungen);
        fensterModellHD = fensterModell.einstellungenLadenHD(einstellungen);
        fensterModell.schreibePfadQuelle(fensterModellHD.holePfadQuelle());
} // einstellungenLaden
```

Ich hatte versucht einfach das fenstermodell zu überschreiben `fensterModellHD = fensterModell.einstellungenLadenHD(einstellungen);`
Das hatte soweit nicht funktioniert und daher wollte ich mal nachfragen ob das überhaupt möglich ist?

Als ich gerade so dabei bin die neuen Daten des FensterModelles zurück zu geben ist mir aufgefallen, das ich doch auch das Modell der Einstellungen zurückgeben sollte, damit die am entsprechenden Programmpunkt ebenfalls aktuell sind??


----------



## kneitzel (7. Aug 2019)

```
Caused by: java.lang.NullPointerException
    at einstellungen.EinstellungenFM.einstellungenLadenHD(EinstellungenFM.java:96)
```

==> Was ist die Zeile 96 der Klasse EinstellungenFM (In einstellungenLadenHD Methode)?


----------



## MiMa (7. Aug 2019)

In der Zeile 96 ist nichts.
Der null Pointer Exception kam weil ich fensterModell als null deklariert hatte und es dann nicht als Instanz erzeugt hatte.
Ich habe es oben Kommentiert wie es vorher war. Darüber habe ich die Instanz erzeugt, danach gab es keine Fehlermeldungen mehr.

Danke


----------



## kneitzel (7. Aug 2019)

MiMa hat gesagt.:


> In der Zeile 96 ist nichts.
> Der null Pointer Exception kam weil ich fensterModell als null deklariert hatte und es dann nicht als Instanz erzeugt hatte.
> Ich habe es oben Kommentiert wie es vorher war. Darüber habe ich die Instanz erzeugt, danach gab es keine Fehlermeldungen mehr.
> 
> Danke


Nach deiner Änderung war da nichts mehr. Als die Exception geworfen wurde, muss da etwas gewesen sein, dass eben die NPE ausgelöst hat. Nach Deiner Beschreibung dann wohl ein Zugriff auf eben dieses fensterModell, welches null war (So dass Deine Änderung war).

Also als Hinweis für die eigene Fehlersuche:
Bei dem StackTrace immer schauen, was die eigentliche Ursache ist. Wenn die Exception abgefangen und dann eine andere Exception geworfen wird, dann kommen manchmal Exceptions, die so erst einmal einem nicht weiter helfen. Daher immer schön bis zum Ende gehen und dann findet man schnell die Ursache.


----------



## mrBrown (7. Aug 2019)

kneitzel hat gesagt.:


> Nach deiner Änderung war da nichts mehr. Als die Exception geworfen wurde, muss da etwas gewesen sein, dass eben die NPE ausgelöst hat. Nach Deiner Beschreibung dann wohl ein Zugriff auf eben dieses fensterModell, welches null war (So dass Deine Änderung war).


Der Code ist auf der vorherigen Seite zu finden  

der relevante Teil ist:


```
-->     EinstellungenFM fensterModell = null;
        Einstellungen einstellungenHD = new Einstellungen();
        einstellungenHD = Einstellungen.leseEinstellungenHD(einstellungen);
      
        // Werte in das Fenster-Modell übetragen
-->     fensterModell.schreibePfadQuelle(einstellungenHD.holePfadQuelle());
```


----------



## MiMa (7. Aug 2019)

Da aktuell das Grundgerüst nun steht und soweit auch alles soweit funktioniert überlege ich gerade an folgende Optionen um mit den Programmeinstellungen ab zu schließen.

1. Einen Standardpfad für Programmeinstellungen der sich nicht mehr ändert?
Einen Standardpfad für die Einstellungen im Benutzerverzeichnis C:\Benutzerordner\Benutzername\Applikation\Einstellungen.ser
Wenn das Programm keine Einstellungen hat werden alle Standardpfade auf das Benutzerverzeichnis gelegt.
Wenn man das Verzeichnis der Einstellungen verlegen sollte wie zB. B:\Einstellungen\Einstellungen.ser weiß das Programm nicht wo es die Einstellungen laden soll. Daher dache ich mir das es das einfachste wäre die Einstellungendatei immer im Benutzerverzeichnisse zu lagern und die anderen Verzeichnisse sind dann ja in der Datei definiert.

2. Laden der Einstellungen vor dem eigentlichen Start?
Ich habe mir Überlegt die Einstellungen in der init() zu laden, damit diese vor dem Programmstart alle verfügbar sind.
Und immer weiter reichen um die Inhalte verfügbar zu machen.

3. Keine Einstellungsdatei dann Fenster aufrufen?
Wenn keine Einstellungen Datei vorhanden sind, werden alle Pfade als Standard in das Benutzerverzeichnis gelegt. Sollte man es einfach so belassen oder das Fenster mit den Einstellungen öffnen um die Pfade zu definieren?
Das Einstellungen Fenster kann auch im Menü angewählt werden um die Pfade zu ändern.


----------



## MiMa (15. Jan 2020)

Hi,
aktuell habe ich ein Problem das ich schon den ganzen Tag versuche zu lösen.
Das Laden der Einstellungen funktioniert für alle Werte.
Das Speichern funktioniert auch für alle Werte bis auf einen (Pfad Einstellungen).
Wenn ich diesen einen Wert eintrage und speichern drücke, dann wird die Datei überhaupt nicht geschrieben?
Fehlermeldungen erhalte ich keine, es werden auch keine Exceptiones geworfen. Es wird halt die Datei einfach nicht geschrieben.
Da ich den ganzen Tag damit verbracht habe den Kontroller und das Modell zu kontrollieren könnte die Fehlerquelle nur die Speichern Methode in Frage kommen, aber ich habe nichts gefunden.
Wäre super wenn sich das mal jemand anderes ansehen könnte
Der Problemwert ist: schreibePfadEinstellungen
Wenn ich aber die Standardwerte speichere, dann funktioniert das speichern für alle Werte problemlos?
Danke
Mi


```
@FXML
    private void einstellungenSchreiben(ActionEvent event) throws IOException {
        LOG.info("Methode einstellungenSpeichern wurde aufgerufen");
        this.einstellungen.schreibePfadQuelle(this.einstellungenModell.getPfadQuelle());
        LOG.info("Schreibe Einstellung Pfad Quelle           : " + this.einstellungen.holePfadQuelle());
        this.einstellungen.schreibePfadZiel(einstellungenModell.getPfadZiel());
        LOG.info("Schreibe Einstellung Pfad Ziel             : " + this.einstellungen.holePfadZiel());
        this.einstellungen.schreibePfadOCR(einstellungenModell.getPfadOCR());
         LOG.info("Schreibe Einstellung Pfad OCR              : " + this.einstellungen.holePfadOCR());
        this.einstellungen.schreibePfadBild(einstellungenModell.getPfadBild());
        LOG.info("Schreibe Einstellung Pfad Bild             : " + this.einstellungen.holePfadBild());
        this.einstellungen.schreibePfadOhneDokumentArt(einstellungenModell.getPfadOhneDokumentArt());
        LOG.info("Schreibe Einstellung Pfad Ohne Dokumentart : " + this.einstellungen.holePfadOhneDokumentArt());
        this.einstellungen.schreibePfadLoeschen(einstellungenModell.getPfadLoeschen());
        LOG.info("Schreibe Einstellung Pfad Loeschen         : " + this.einstellungen.holePfadLoeschen());
        this.einstellungen.schreibePfadGeschuetzt(einstellungenModell.getPfadGeschuetzt());
        LOG.info("Schreibe Einstellung Pfad Geschuetzt       : " + this.einstellungen.holePfadGeschuetzt());
        this.einstellungen.schreibePfadEinstellungen(einstellungenModell.getPfadEinstellungen());
        LOG.info("Schreibe Einstellung Einstellungen         : " + this.einstellungen.holePfadEinstellungen());
     
        this.einstellungen.schreibeDbServeradresse(einstellungenModell.getDbServeradresse());
         LOG.info("Schreibe Einstellung Serveradresse         : " + this.einstellungen.holeDbServeradresse());
        this.einstellungen.schreibeDbDatenbankname(einstellungenModell.getDbDatenbankname());
        LOG.info("Schreibe Einstellung Datenbankname         : " + this.einstellungen.holeDbDatenbankname());
        this.einstellungen.schreibeDbBenutzername(einstellungenModell.getDbBenutzername());
        LOG.info("Schreibe Einstellung Benutzername          : " + this.einstellungen.holeDbBenutzername());
        this.einstellungen.schreibeDbPasswort(einstellungenModell.getDbPasswort());
        LOG.info("Schreibe Einstellung Passwort              : " + this.einstellungen.holeDbPasswort());
        this.einstellungen.schreibeDnbToken(einstellungenModell.getDnbToken());
        LOG.info("Schreibe Einstellung DNB Token             : " + this.einstellungen.holeDnbToken());
             
        Einstellungen.schreibeEinstellungenHD(einstellungen);
    } // einstellungenSchreiben
```


```
public static void schreibeEinstellungenHD(Einstellungen einstellungen) {
        OutputStream ausgabeStreamDatei;
        // Prüfen ob es das Einstellungen-Verzeichnis existiert
        File einstellungenVerzeichnis = new File(einstellungen.holePfadEinstellungen());
         if (einstellungenVerzeichnis.mkdirs()) {
             LOG.info("Das Einstellungen-Verzeichnis ist nicht vorhanden und wurde erstellt");
         }
       
        try {
            LOG.info("Einstellungen Pfad : " + einstellungen.holePfadEinstellungen());
            ausgabeStreamDatei = new FileOutputStream(einstellungen.holePfadEinstellungen()+"\\Einstellungen.ser");
            ObjectOutputStream ausgabeStreamObjekt = new ObjectOutputStream(ausgabeStreamDatei);
            ausgabeStreamObjekt.writeObject(einstellungen);
            ausgabeStreamDatei.close();
            LOG.info("Einstellungen wurden auf Festplatte gespeichert");
        } catch (IOException e) {
            LOG.info("Es trat ein Fehler beim schreiben der Programmeinstellungen auf", e);
        }
    } // schreibeEinstellungenHD
```


----------



## kneitzel (15. Jan 2020)

Bist du Dir sicher, dass Du info Logmeldungen mitbekommst? Ich vermute, dass eine Exception geworfen wird und dass Du die nur nicht mitbekommst. Sprich: Siehst Du die Logmeldung von wegen "Einstellungen Pfad" und so? (Und Fehler sollten auch nicht auf Level INFO sondern auf Level ERROR im Log geschrieben werden!)


----------



## MiMa (16. Jan 2020)

Danke für den Hinweis der Logging Anwendung bei den Fehlermeldungen. Ich bin so darauf konzentriert, das MVC hin zu bekommen. Ich trau mich schon gar nicht zu sagen, das ich es schon viele Monate versuche das Konzept in eine funktionierende Version um zu setzen.
Habe das Projekt mal in Eclipse übertragen, für den Fall das Netbeans irgendetwas macht? Aber das gleichen Problem.
Der Wert Einstellungen wurde geändert. Beim speichern wird der richtige Wert übernommen, aber die Datei wird nicht gespeichert, obwohl das Log es als erfolgreich ausgibt?

Das Logfile sieht so aus

```
INFO  start.Start - Das Programm wird initialisiert init()-Methode
INFO  start.Start - Das Programm-Hauptfenster wird initialisiert start()-Methode
INFO  start.Hauptfenster - Einstellungen wurde im Menue ausgewaehlt
INFO  einstellungen.EinstellungenFK - Einstellungen werden initialisiert
INFO  einstellungen.EinstellungenFK - einstellungenLaden wurde aufgerufen
INFO  einstellungen.Einstellungen - Pfad zum  laden von Einstellungen : C:\Users\Michael\DMS\00 Einstellungen\Einstellungen.ser
INFO  einstellungen.Einstellungen - Die Einstellungen wurden von Festplatte geladen :
INFO  einstellungen.EinstellungenFK - Lade Einstellung Pfad Quelle : B:\01 Quelle
INFO  einstellungen.EinstellungenFK - Lade Einstellung Pfad Ziel : B:\02 Ziel
INFO  einstellungen.EinstellungenFK - Lade Einstellung Pfad OCR : B:\03 OCR
INFO  einstellungen.EinstellungenFK - Lade Einstellung Pfad Bild : B:\04 Bild
INFO  einstellungen.EinstellungenFK - Lade Einstellung Pfad ohne Dokumnentart : B:\05 ohne Dokumentart
INFO  einstellungen.EinstellungenFK - Lade Einstellung Pfad Loeschen : B:\06 Loeschen
INFO  einstellungen.EinstellungenFK - Lade Einstellung Pfad Geschuetzt : B:\07 geschuetzt
INFO  einstellungen.EinstellungenFK - Lade Einstellung Pfad Einstellungen : C:\Users\Michael\DMS\00 Einstellungen
INFO  einstellungen.EinstellungenFK - Lade Einstellung Serveradresse : 192.168.178.10
INFO  einstellungen.EinstellungenFK - Lade Einstellung Datenbankname  : DMS
INFO  einstellungen.EinstellungenFK - Lade Einstellung Benutzername : michael
INFO  einstellungen.EinstellungenFK - Lade Einstellung Passwort : PW123
INFO  einstellungen.EinstellungenFK - Lade Einstellung DNB Token : DNB 123
INFO  einstellungen.EinstellungenFK - sucheEinstellungenVerzeichnis wurde aufgerufen
INFO  einstellungen.EinstellungenFK - Selektiertes Einstellungen Verzeichnis : B:\00 Einstellungen
INFO  einstellungen.EinstellungenFK - Methode einstellungenSpeichern wurde aufgerufen
INFO  einstellungen.EinstellungenFK - Schreibe Einstellung Pfad Quelle           : B:\01 Quelle
INFO  einstellungen.EinstellungenFK - Schreibe Einstellung Pfad Ziel             : B:\02 Ziel
INFO  einstellungen.EinstellungenFK - Schreibe Einstellung Pfad OCR              : B:\03 OCR
INFO  einstellungen.EinstellungenFK - Schreibe Einstellung Pfad Bild             : B:\04 Bild
INFO  einstellungen.EinstellungenFK - Schreibe Einstellung Pfad Ohne Dokumentart : B:\05 ohne Dokumentart
INFO  einstellungen.EinstellungenFK - Schreibe Einstellung Pfad Loeschen         : B:\06 Loeschen
INFO  einstellungen.EinstellungenFK - Schreibe Einstellung Pfad Geschuetzt       : B:\07 geschuetzt
INFO  einstellungen.EinstellungenFK - Schreibe Einstellung Einstellungen         : B:\00 Einstellungen
INFO  einstellungen.EinstellungenFK - Schreibe Einstellung Serveradresse         : 192.168.178.10
INFO  einstellungen.EinstellungenFK - Schreibe Einstellung Datenbankname         : DMS
INFO  einstellungen.EinstellungenFK - Schreibe Einstellung Benutzername          : michael
INFO  einstellungen.EinstellungenFK - Schreibe Einstellung Passwort              : PW123
INFO  einstellungen.EinstellungenFK - Schreibe Einstellung DNB Token             : DNB 123
INFO  einstellungen.Einstellungen - Einstellungen Pfad : B:\00 Einstellungen
INFO  einstellungen.Einstellungen - Einstellungen wurden auf Festplatte gespeichert
INFO  start.Hauptfenster - Programm beenden wurde im Menue ausgewaehlt
INFO  start.Start - Programm-Ende wird ausgeführt stop()-Methode
```


----------



## kneitzel (16. Jan 2020)

Ok, meine erste Vermutung ist damit hinfällig. Du bekommst ja alle info Meldungen angezeigt und so wie es aussieht schreibt er die Datei auch.

Kann es daran liegen, dass Du an der falschen Stelle schaust? 

Beim Laden sehe ich:

```
INFO  einstellungen.Einstellungen - Pfad zum  laden von Einstellungen : C:\Users\Michael\DMS\00 Einstellungen\Einstellungen.ser
INFO  einstellungen.EinstellungenFK - Lade Einstellung Pfad Einstellungen : C:\Users\Michael\DMS\00 Einstellungen
```

Aber wenn es zum Speichern geht, dann ist der Pfad angegeben als:

```
INFO  einstellungen.EinstellungenFK - Schreibe Einstellung Einstellungen         : B:\00 Einstellungen
```

Das ist der Wert von:
`LOG.info("Schreibe Einstellung Einstellungen         : " + this.einstellungen.holePfadEinstellungen());`

Und das ist dann ggf. der Wert, der auch hier verwendet wird:
`ausgabeStreamDatei = new FileOutputStream(einstellungen.holePfadEinstellungen()+"\\Einstellungen.ser");`

Somit wäre der Pfad, der geschrieben wird: B:\00 Einstellungen\Einstellungen.ser
Da schaust Du auch? Oder schaust Du aus dem Pfad, der weiter oben im Log zu finden ist:
C:\Users\Michael\DMS\00 Einstellungen\Einstellungen.ser


----------



## MiMa (16. Jan 2020)

Ich bin ja ein Dummie. Ja jetzt fällt es mir wieder ein. Wenn sich der Pfad für die Einstellungen ändert, dann wird die Datei auch direkt in diesem neuen gespeichert. Da könnte ich noch lange suchen. Denn im alten Pfad ist die Einstellungsdatei ohne den geänderten Pfad und auf Laufwerk B ist die Einstellungen.ser Datei mit dem korrekt eingetragenen Pfad.
Ich bin ja so froh, dass mein Code doch so funktioniert wie ich es mal ursprünglich geplant hatte.  
Keine Fehler bin ich froh. 

Vielen, Vielen Dank.

Allerdings bin ich dann irgendwann dahinter gekommen, das es besser wäre das Verzeichnis Einstellungen im benutzerdefinierten Home Verzeichnis zu belassen. Beim ersten Programmstart habe ich Standardwerte im Programm festgelegt welche automatisch gesetzt werden wenn keine Einstellungen.ser vorhanden ist. 
Wenn diese Datei irgendwo anders abgelegt wird und das Programm neu startet, weis es nicht wo es ist.
Woher soll es wissen das es einen anderen Pfad gibt?! Also besser einen festen Ort wählen war dann im Kopf. 
Alle Einstellungen können geändert werden, aber nicht der Ort dieser Datei.
Und mit dieser Überlegung habe ich die gesamte Zeit den Fehler gesucht 
Das werde ich als erste korrigieren.

Zumindest habe ich es jetzt hin bekommen eine Applikation zu erstellen, die auf JavaFX basiert und die Oberflächen mit FXML eingebunden werden. Auch Werte im Fenster können über Tastatur als auch über einen FileDialog definiert werden, die dann permanent auf Festplatte gespeichert und geladen werden können.

Jetzt kommt der Teil vor dem ich mich schon immer gefürchtet habe und so wage weis wie es funktionierten sollte, könnte?
Ich weis, wenn ich es dann einmal weiß wie es geht und funktioniert, dann geht es auch mit allen anderen Fenstern.

Das Problem ist, das ich nicht weiß wie ich ein Objekt mit einigen Inhalten an eine andere Instanz senden kann.
1. Das Programm startet (main: launch)
2. Initialisierung init()
3. In der Methode start() wird das Hauptfenster aufgebaut (FXMLLoader Hauptfenster.fxml)
4. In der Instanz HauptfensterKontroller  initialize() wird das Einstellungen Objekt mit Standardwerten erzeugt.
5. Über das Menü wird das Einstellungen Fenster aufgerufen (FXMLLoader Einstellungen.fxml) 
6. >> Jetzt >> wie bekomme ich das Einstelllungen-Objekt mit seinen inhalten in die Instanz des Einstellungen-Fensters?
7. Nach dem Speichern der werte auf Festplatte sollen diese dann wieder in der Instanz des Hautpfensters zurück.

Ich wäre über einen Tipp sehr dankbar.

Mi


----------



## MiMa (17. Jan 2020)

Da das Objekt ja im RAM Speicher ist, muss ich die Referenz an den Kontroller senden.
Wenn ich meinen Kontroller-Konstruktor wie folgt ändere, dann Öffnet sich das Einstellungen Fenster nicht mehr?

```
// Datenmodelle
    private EinstellungenMOD fensterModell;
    private Einstellungen einstellungen;

    // Konstruktor um das Fenstermodell zu erzeugen
    public EinstellungenKON(Einstellungen einstellungen) {
        // Referenz des Einstellungen-Objektes übergeben
         this.einstellungen = einstellungen;

        // Erzeugt ein Fenster-Modell aus dem Einstellungen Model-Objekt
        this.fensterModell = new EinstellungenMOD(einstellungen);
    } // Konstruktor um das Fenstermodell zu erzeugen
```

Ausserdem erhalte ich dann folgenden Fehler vom FXML Loader?

```
[java] Exception in thread "JavaFX Application Thread" java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
     [java]     at javafx.fxml/javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1787)
     [java]     at javafx.fxml/javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(FXMLLoader.java:1670)
     [java]     at javafx.base/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)
     [java]     at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
     [java]     at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
     [java]     at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
     [java]     at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
     [java]     at javafx.base/com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
     [java]     at javafx.base/com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49)
     [java]     at javafx.base/javafx.event.Event.fireEvent(Event.java:198)
     [java]     at javafx.controls/javafx.scene.control.MenuItem.fire(MenuItem.java:465)
     [java]     at javafx.controls/com.sun.javafx.scene.control.ContextMenuContent$MenuItemContainer.doSelect(ContextMenuContent.java:1380)
     [java]     at javafx.controls/com.sun.javafx.scene.control.ContextMenuContent$MenuItemContainer.lambda$createChildren$12(ContextMenuContent.java:1333)
     [java]     at javafx.base/com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:218)
     [java]     at javafx.base/com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
     [java]     at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
     [java]     at javafx.base/com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
     [java]     at javafx.base/com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
     [java]     at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
     [java]     at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
     [java]     at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
     [java]     at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
     [java]     at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
     [java]     at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
     [java]     at javafx.base/com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
     [java]     at javafx.base/com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
     [java]     at javafx.base/com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
     [java]     at javafx.base/com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
     [java]     at javafx.base/javafx.event.Event.fireEvent(Event.java:198)
     [java]     at javafx.graphics/javafx.scene.Scene$MouseHandler.process(Scene.java:3876)
     [java]     at javafx.graphics/javafx.scene.Scene$MouseHandler.access$1300(Scene.java:3604)
     [java]     at javafx.graphics/javafx.scene.Scene.processMouseEvent(Scene.java:1874)
     [java]     at javafx.graphics/javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2613)
     [java]     at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:397)
     [java]     at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:295)
     [java]     at java.base/java.security.AccessController.doPrivileged(Native Method)
     [java]     at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleMouseEvent$2(GlassViewEventHandler.java:434)
     [java]     at javafx.graphics/com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:389)
     [java]     at javafx.graphics/com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:433)
     [java]     at javafx.graphics/com.sun.glass.ui.View.handleMouseEvent(View.java:556)
     [java]     at javafx.graphics/com.sun.glass.ui.View.notifyMouse(View.java:942)
     [java]     at javafx.graphics/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
     [java]     at javafx.graphics/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:175)
     [java]     at java.base/java.lang.Thread.run(Thread.java:844)
     [java] Caused by: java.lang.reflect.InvocationTargetException
     [java]     at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
     [java]     at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
     [java]     at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
     [java]     at java.base/java.lang.reflect.Method.invoke(Method.java:564)
     [java]     at com.sun.javafx.reflect.Trampoline.invoke(MethodUtil.java:76)
     [java]     at jdk.internal.reflect.GeneratedMethodAccessor2.invoke(Unknown Source)
     [java]     at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
     [java]     at java.base/java.lang.reflect.Method.invoke(Method.java:564)
     [java]     at javafx.base/com.sun.javafx.reflect.MethodUtil.invoke(MethodUtil.java:275)
     [java]     at javafx.fxml/com.sun.javafx.fxml.MethodHelper.invoke(MethodHelper.java:83)
     [java]     at javafx.fxml/javafx.fxml.FXMLLoader$MethodHandler.invoke(FXMLLoader.java:1782)
```

Anstatt die Referenz immer zu übergeben würde mir noch einfallen die Daten ständig von Festplatte zu lesen.
Ich denke das dies aber wohl kein guter Ansatz wäre?!?


----------



## MiMa (6. Feb 2020)

Hi,
Die grafische Oberfläche scheint soweit zu funktionieren.
Einstellungen werden von HD geladen können geändert werden und lassen sich auch auf HD wieder speichern.

Ich habe noch ein Problem, welches ich noch nicht verstehe.
Wenn das Programm gestartet wird, öffnet sich ein Hauptfenster.
Im Kontroller dieses Hauptfenster soll das Objekt für die Programmeinstellungen erstellt und von der HD geladen werden.

Wenn ich im Menü aber jetzt das Einstellungen Fenster öffne, muss die Referenz des Objektes für die Programmeinstellungen übergeben werden.

Mir ist nicht klar wie das Funktioniert. Der Aufruf des Fensters wird mit dem fxml loader gemacht und in der fxml Datei ist die Verbindung zum kontroller, welchen ich im Scene Builder eingestellt habe.

Kann man beim Laden einer fxml datei ausser dem String der FXML datei noch Parameter übergeben?

Wenn in dem Einstellungen Werte geändert wurden, wie kommen die dann in den Kontroller des Hauptfensters zurück?

Für Hilfe bin ich sehr dankbar 
M


----------



## MiMa (7. Feb 2020)

Lange hat es gedauert, aber ich denke, das ich nur wieder einen Schritt weiter bin und habe es nun endlich geschafft, das Einstellungen-Objekt an das Fenster Einstellungen zu senden.
Ausschnitt aus der Klasse "HauptfensterKON"

```
/**
     * Initialisierung der Kontroller Klasse Hauptfenster.
     * @param url
     * @param rb
     */
    @Override
    public void initialize(URL url, ResourceBundle rb) {
        // Das Einstellungen Objekt wird erzeugt
        this. einstellungen = new Einstellungen();
     
        String heimVerzeichnis = System.getProperty("user.home");
        File dateiEinstellungen = new File(heimVerzeichnis + "\\DMS" + "\\Einstellungen.ser");
        LOG.info("Der Pfad für die Einstellungsdatei ist : " + dateiEinstellungen);
      
         if (dateiEinstellungen.exists()) {
             LOG.info("Die Einstellungsdatei ist vorhanden, die Einstellungen werden geladen");
             this.einstellungen = Einstellungen.leseEinstellungenHD();
        } else {
             LOG.info("Die Einstellungsdatei fehlt, die Standardwerte werden geladen");
             this.einstellungen = Einstellungen.standardWerte();
         }
    } // initialize

    @FXML
    private void menuEinstellungen(ActionEvent event) throws IOException {
        LOG.info("Einstellungen wurde im Menue ausgewaehlt");
        Stage eintellungenFenster = new Stage();
     
        FXMLLoader einstellungenLaden = new FXMLLoader();
        einstellungenLaden.setLocation(HauptfensterKON.class.getResource("/einstellungen/Einstellungen.fxml"));
     
        // Den Controller erzeugen und Einstellungen-Objekt als Parameter Übergeben
        EinstellungenKON einstellungenKontroller = new EinstellungenKON(this.einstellungen);
        // Verbinden des Controllers mit dem Fenster
        einstellungenLaden.setController(einstellungenKontroller);
     
        Parent einstellungenLayout = (Parent) einstellungenLaden.load();
     
        Scene einstellungenInhalt = new Scene(einstellungenLayout);
        eintellungenFenster.setScene(einstellungenInhalt);
        eintellungenFenster.setTitle("Einstellungen");
        eintellungenFenster.show();
    } // menuEinstellungen
```

Folgende Zeilen habe in der Klasse "EinstellungenKON" ergänzt und geändert

```
// Datenmodelle
    private EinstellungenMOD fensterModell;
    private Einstellungen einstellungen;

    // Konstruktor mit Standardeinstellungen
    public EinstellungenKON() {
        // Erzeugt ein Modell-Objekt Einstellungen mit Standardwerten
        this.einstellungen = Einstellungen.standardWerte();

        // Erzeugt ein Fenster-Modell aus dem Einstellungen Model-Objekt
        this.fensterModell = new EinstellungenMOD(this.einstellungen);
    } // Konstruktor mit Standardeinstellungen

    // Konstruktor mit dem Einstellungen-Objekt als Parameter
    public EinstellungenKON(Einstellungen einstellungen) {
       this.einstellungen = einstellungen;
       this.fensterModell = new EinstellungenMOD(this.einstellungen);
    } // Konstruktor mit Paramter "Einstellungen-Objekt"
```

Die Kontroller Zuordnung von der FXML Datei im Scene Builder habe ich entfernt damit wirklich nur der Controller die Verbindungen besitzt.

Im Grunde hatte ich die Lösungen schon seit Monaten durch meinen Post " Übergeben von Parameter bei FXML Aufruf?" aber ich hatte so einen Knoten im Kopf, das ich es irgendwie nie wirklich verstanden habe es in diesem Projekt um zu setzen! Daran merkt man das Programmieren nicht nur Bücher lesen und Tutorials anschauen sind, sondern wirklich erst in der Praxis lernt.
Auch habe ich schon so viele Testprojekte angelegt um das Problem zu lösen, das ich auch darüber den Überblick verloren habe. 

Ich habe festgestellt, das beim schließen des Fensters die Eingestellten oder veränderten Werte beim speichern auf die Festplatte beim erneuten Öffnen des Einstellung-Fensters wieder korrekt angezeigt werden.
Wenn die nicht auf Festplatte speichere dann sind die geänderten Werte verloren.

1. Bedeutet das, dass beim schließen des Fensters ich mich wieder im Kontroller des Hauptfensters befinde und dort wieder die init() Methode aufgerufen wird?

2. Wo befinde ich mich nach dem schließen des Einstellungen-Fensters und wie kann ich das geänderte Einstellungen-Objekt wieder zurückgeben. Wenn ich im Hauptfenster das Menü auswählen kann, sollte ich mich doch im Hauptfenster Kontroller befinden?

Danke
Mi


----------



## MiMa (29. Apr 2020)

Hi, das Thema ist für mich leider immer noch nicht vom Tisch.
Lange habe ich mir Gedanken über den Verlauf von Daten gemacht die von Fenster zu Fenster gegeben werden sollen um damit zu arbeiten.
Aktuell sieht die Lage so aus, das im Kontroller des Hauptprogramms überprüft wird ob es eine Datei mit Einstellungen auf der Festplatte gibt.
Wenn ja dann werden diese geladen und in ein Java-Objekt "einstellungen" geschrieben.
Wenn es nicht vorhanden ist, dann werden Standard Werte in das Java-Objekt "einstellungen" geschrieben.

In den Einstellungen befinden sich Pfade für Verzeichnisse verschiedenster Dateitypen und andere Daten.

Im Menü oder über die Toolbar kann ich ein Fenster für Einstellungen öffnen. 
In diesem können die Pfade aus dem Java-Objekt "einstellungen" verändert und auf Festplatte gespeichert werden.

Wenn ich jetzt das Fenster zu den Einstellungen schließe, dann ist das "einstellungen" Objekt verloren, daher wurde es vorher auf Festplatte gespeichert. 
Meine Frage ist jetzt, wie ich dem "einstellungen" Objekt aus dem Hauptfenster die neuen einstellungen vom Einstellungen Fenster übermittle?
Irgendwie war ich fest gefahren und wollte beim Schliessen des Einstellungen Fenster die neuen "einstellungen" Inhalte übergeben.

Ich habe es bisher nicht heraus gefunden wie es funktioniert.
Die Daten sollen vom Kontroller des Hautpfensters und des Einstellungen Fenster hin und her übermittelt werden.  Damit soll auch das Problem gelöst werden das beim Schliessen des Einstellungen Fesnters der Kontroller die neuen -Daten bekommt. Wenn das Einstellungen Fenster erneut geöffnet wird, das es dann stets die neuen Daten bekommt.

Das laden der neuen Daten von Festplatte wird nur bei einem neustart des Programms gelesen.
Alternative wäre einen Button im Hauptfenster zu machen der das Objekt aktualisiert bzw neu einließt.
Aber das finde ich nicht Zeitgemäß und suche daher einen Weg die Daten vom Einstellungen Kontroller zum Hauptfenster Kontroller hin und her zu senden.

Ich würde mich freuen, wenn mir jemand helfen könnte?

Danke
Mi


----------

