Schliessen eines FXML Fensters?

MiMa

Top Contributor
Aktuell habe ich ein Hauptfenster und in diesem ist ein Button der ein weiteres Fenster für Programmeinstellungen öffnet.
Im Kontroller des Hauptfensters wird das Einstellungsfenster über eine FXMLLoader aufgerufen.
Im Kontroller des Einstellungsfensters habe ich aktuell keine Möglichkeit das fenster über .close() zu schliessen.

Daraufhin habe ich im Kontroller des eine getMethode erstellt welche ich die Stage des Einstellungsfensters zurückgeben kann.
Im Kontroller des Einstellungsfensters habe ich dann versucht das über close() zu schließen, welches leider nicht funktionierte?

Kann mir da jemand Helfen?
Danke
Mi

Auszug Kontroller Hauptfenster
Java:
// Fenster
private Stage einstellungenFenster = new Stage();
...
public void menuOptionenProgrammeinstellungen(ActionEvent aktion) throws IOException {
        LOG.info("Programmeinstellungen wurde im Menue Optionen ausgewaehlt");
        this.einstellungenFenster = new Stage();

        FXMLLoader einstellungenLaden = new FXMLLoader();
        einstellungenLaden.setLocation(HauptfensterKON.class.getResource("/Fenster/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);
        this.einstellungenFenster.setScene(einstellungenInhalt);
        this.einstellungenFenster.setTitle("Einstellungen");
        this.einstellungenFenster.initModality(Modality.APPLICATION_MODAL);
        this.einstellungenFenster.show();
    } // menuOptionenProgrammeinstellungen
...
public Stage getEinstellungenFenster() {
        return einstellungenFenster;
    }

Auszug Kontroller Fenster Einstellungen
Java:
private void einstellungenSpeichern(ActionEvent event) throws IOException {
        LOG.info("Methode einstellungenSpeichern wurde aufgerufen");
        ...
        Einstellungen.schreibeEinstellungenHD(einstellungen);
        
        HauptfensterKON hsKon = new HauptfensterKON();
        Stage fenster = hsKon.einstellungenFenster;
        fenster.close();
    } // einstellungenSpeichern
 
Das kann ja so auch nicht klappen, denn Du erstellst ja ein neues HauptfensterKON. Damit ist einstellungenFenster ja das new Stage(), mit dem Du es initialisierst.

Aber dieses Konstrukt dürfte auch nicht notwendig sein, denn Du kannst Dir von jedem Element ja die Stage holen:
getScene().getWindow(); kannst Du z.B. auf dem Button zum Schließen des Fensters aufrufen, um die Stage zu bekommen. Und auf der kannst Du close() aufrufen.

Und das kannst Du sogar mit dem Event Parameter machen, denn der hat ja eine source ...
 
Danke, ich probiere schon ewig an dem MVC mit FXML Muster.
Habe das mal wie folgt in den EinstellungenKON eingebaut.
Java:
        ...
        Einstellungen.schreibeEinstellungenHD(einstellungen);
        Stage fenster = new Stage();
        fenster.getScene().getWindow();
        fenster.close();
    } // einstellungenSpeichern

Nach dem Drücken auf speichern kommt eine Fehlermeldung
Habe ich da denn richtig gemacht?
Danke.
 
Also zum einen ist es generell hilfreich, wenn Du die Fehlermeldung immer genau angibst.
In Deinem Fall kann ich es mir aber schon denken, denn Du wirst eine NullPointerException bekommen. Du sollst nicht neue Objekte erzeugen - das ist fast immer falsch, wenn Du etwas bestehendes ändern willst! Wenn Du ein offenes Fenster bei Dir zuhause schließen willst: Dann besorgst Du Dir doch auch nicht erst ein neues Fenster um das dann zu schließen!

Du wirst in dem Controller doch irgendwelche Controls haben. Ein existierendes Control könntest Du verwenden.

Oder wenn die Methode ein Action Handler ist, dann ginge etwas wie:
Java:
public void closeButtonListener(ActionEvent e) {
    final Node source = (Node) e.getSource();
    final Stage stage = (Stage) source.getScene().getWindow();
    stage.close();
}
 
Ja du hast schon recht und das leuchtet mir auch ein.
Aber ich hatte gedacht, das man ein Objekt erstellt, diesem dann die Speicheradresse des aktuellen Fensters zuweist um es dann schließen zu können. Wir arbeiten doch mit Zeigern oder verstehe ich da was falsch?
In meiner Implementierung hatte ich halt gedacht ein Objekt zu erstellen,
dann dem Objekt die Speicheradresse des Fenster zu zu weisen,
und dann um es zu schließen?

Die Methode in der ich einfach das Fenster schließen möchte ist bereits die Methode in der ich Daten speichere, und ist auch mit dem Button bereits verbunden.

Du schreibst da was von source. Aber was ist source ?
Es muss ja von irgend etwas her kommen, also das aktuelle Fenster.
Dan denke ich es muss ja eine Option geben indem man das aktuelle Fenster zurückgeben kann um es zu schließen!
Nur weis ich nicht wie.
 
Also bezüglich fxml Controller schauen wir uns einfach einmal ein Beispiel an. Ich habe mal eine Blog Serie mit JavaFX gestartet (mit der ich nicht zufrieden bin, die ist mangelhaft! Ich überarbeite das derzeit komplett und will das auch mit YouTube Komponente neu aufziehen ...) und da kann ich das Beispiel einfach einmal nehmen.

Code findet sich unter: https://github.com/kneitzel/blog-javafx-series und dort in "02 - helloworld-fxml".

[CODE lang="java" title="src/main/java/helloworld/HelloWorldController.java"]package helloworld;

import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;

public class HelloWorldController {

@FXML private Button greetMeButton;

public void closeApplicationAction(final ActionEvent e) {
System.exit(0);
}

public void handleGreetMeButton(final ActionEvent e) {
Alert alert = new Alert(Alert.AlertType.INFORMATION, "Hallo Anwender!", ButtonType.OK);
alert.showAndWait();
}

@FXML
public void initialize() {
greetMeButton.setOnAction(this::handleGreetMeButton);
}
}
[/CODE]

Und dazu gehört ein fxml:
[CODE lang="java" title="src/main/resources/helloworld/HelloWorld.fxml"]<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.Pane?>
<?import javafx.scene.text.Font?>

<Pane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="145.0" prefWidth="237.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="helloworld.HelloWorldController">
<children>
<Label alignment="CENTER" contentDisplay="CENTER" layoutX="-1.0" layoutY="14.0" prefHeight="53.0" prefWidth="237.0" text="Hallo Welt" textOverrun="CLIP" underline="true">
<font>
<Font size="24.0" />
</font>
</Label>
<Button fx:id="greetMeButton" layoutX="30.0" layoutY="78.0" mnemonicParsing="false" text="Grüße Mich" />
<Button layoutX="137.0" layoutY="78.0" mnemonicParsing="false" onAction="#closeApplicationAction" text="Beenden" />
</children>
</Pane>
[/CODE]

Wenn nun dieses fxml geladen wird, dann wird auch eine Instanz der Klasse HelloWorldController erzeugt. Dazu dient die Angabe fx:controller="helloworld.HelloWorldController" im fxml.
Nach der Erstellung dieser Instanz wird dies noch analysiert - und die Annotations mit @FXML werden aufgelöst. Das ist der Moment, wenn der Variablen greetMeButton ein Wert zugewiesen wird. (Das erfolgt nach der Erstellung der Instanz, d.h. im Konstruktor wäre die Variable noch null!)

Wenn das fertig ist, wird initialize aufgerufen. Da setze ich den OnAction Handler auf die Methode.

==> Also ganz wichtig: Ich habe hier einen Button greetMeButton. Dieser ist in der Scene drin und der Aufbau ist so, dass die Children jeweils den Parent kennen. Also kann ich jetzt mittels getScene die Scene bekommen. Die Scene kennt die Stage sozusagen als Ihren Parent, daher kann ich auf der Scene ein getWindow aufrufen. So Du etwas ähnliches aufgebaut hättest, dann hättest Du ein beliebiges Control entsprechend nutzen können.

==> Wenn Du auf eine Aktion reagierst, dann hat der Handler immer ein Event als Parameter. Und ein Bestandteil dieses Events ist immer, wodurch das Event ausgelöst wurde. So ist es möglich ein Handler für viele Dinge zu nutzen, also unterschiedliche Dinge machen je nachdem ob Button1, Button2 oder ein Menupunkt aufgerufen wurde. (Beispiel Taschenrechner: Die 10 Knöpfe mit den Ziffern 0-9 haben alle einen Handler. Der nimmt den Text vom Button, holt sich den Wert und macht dann, was damit zu tun ist ...)
Das bedeutet bei Dir: Selbst wenn Du kein Control hast das Du mittels @FXML "injecten" läßt (Man spricht hier gerne von Injection. Die Werte werden ja sozusagen in die Instanz hinein gespritzt ...), dann brauchst Du das auch nicht. Du nutzt einfach die Source vom Event.

Wurden die Vorgänge dadurch etwas erklärt?

PS: Der Blog-Text zu dem Source findet sich unter https://blog.kneitzel.de/2020/06/26/javafx-scene-builder/ - aber der lohnt sich nicht wirklich derzeit. Ich bin halt dabei mir da was neues zu überlegen incl. neuem Format und so ...
 
Vielen Dank für Deine ausführliche Beschreibung.
Also kann durch den Parameter des Events das gesuchte identifiziert!

Ich hab es dann mal so probiert, aber get von Scene und oder Window ist nicht möglich.
Deine Beschreibung gut verständlich und leuchtet auch ein, aber ich bekomme das einfach nicht hin?
Auch bin ich schon den ganzen Tag im Netz nach Antworten am suchen!
Oft habe ich Posts gefunden die das gleiche Problem haben aber keine passende Lösung 🙁

Java:
@FXML
    private void einstellungenSpeichern(ActionEvent event) throws IOException {
        LOG.info("Methode einstellungenSpeichern wurde aufgerufen");

        this.einstellungen.schreibePfadQuelle(this.fensterModell.holePfadQuelle());
        LOG.info("Schreibe Einstellung Pfad Quelle           : " + this.einstellungen.holePfadQuelle());
        ...
        ..
        ...
        Einstellungen.schreibeEinstellungenHD(einstellungen);
     
        // Hier fenster schliessen
        Stage fenster = event.getScene().getWindow();
        fenster.close();
    } // einstellungenSpeichern

Ich habe viel über JavaFX Lerktüre recherchiert, die meisten sind für Anfänger geschrieben und gefühlt immer das gleiche.
Auch die Taschenrechner App kann ich schon nicht mehr sehen. Für Fortgeschrittene über GUI Programmierung mit FXML und mehreren Fenstern ist kaum was zu finden. Und das was zu finden ist ist kaum verständlich, falsch und unvollständig.
 
Zuletzt bearbeitet:
Von Event musst du erst getSource machen. Und du musst es casten, da es wohl ein Object zurück gibt. Also die zwei Zeilen mal mit dem ersetzen:
Java:
final Node source = (Node) event.getSource();
final Stage stage = (Stage) source.getScene().getWindow();
stage.close();

Edit: den Carsten mal zu casten geändert ... die Autokorrektur des Tablets hat da eingegriffen.
 
Vielen Dank für Deine Hilfe.
Jetzt hat es endlich geklappt.

Das war ja mal wieder ein Ding für sich 😅
Wie oben schon geschrieben habe ich mir einiges an Literatur und Tutorials angesehen, aber zu GUI Programmierung ist so gut wie nichts anständiges zu finden.
Wenn du einen Kurs machst, dann mache mal was anderes als alle anderen.
Das GUI mit MVC und FXML um zu setzen hat mich fast ein Jahr gekostet um es zu Lernen.
Aus verschiedenen Büchern Entwurfmuster angesehen, OOP im Buch angesehen im Netz gesucht und angesehen, in den Bibliotheken der Kölner und Düsseldorfer Uni, das kostet Zeit. Viele behandeln JavaFX als ein Fenster mit FXML und bei mehreren Fenstern wird das GUI im Code implementiert, das wars. Hier im Forum bekomme ich mehr Hilfe als in so einem Udemy Kurs. Das ist nur Massenware, echt schade. Die eine oder andere Ausnahme gibt es schon aber selten.

Aktuell habe ich es zwar hin bekommen, aber immer noch hier und da mit einem Fragezeichen.
Ich interessiere mich für Themen wie:
Die Netbeans IDE, wie man diese Einrichtet, Benutzen von GIT und Maven in Netbeans, Tipps und Trick zum Coden durch Templates und Ausstattungen, alle Themen um mehrere Fenster zu verwalten inklusive MVC mit FXML, Wie implementiert man eine Updatefunktion in das Programm, Menüs und Toolbars und Fenstern mit grafischen Elementen wie Icons und Bildern.

Also alles um ein Grundgerüst eines Programmes mit allen erforderlichen Grundelementen zu bekommen.
Ich sehe immer wieder Eclipse ind IntelliJ als IDE und kann nicht verstehen warum Netbeans so wenig genutzt wird. Ich finde das diese IDE mit abstand die beste ist weil Netbeans vieles bereits implementiert und vorschlägt wo die Eclipse Programmierer echt tippen müssen.
 
Zuletzt bearbeitet:
Ja, kannst du vorhandenen Blog Einträge mal ansehen. Ich bin damit auch unzufrieden. MVC habe ich bei JavaFX für mangelhaft befunden. Da benötigt man ein Binding und das geht nur sehr begrenzt (JavaScript im fxml File ist da die Lösung gewesen um die Trennung zu haben ...)

Daher war meine Schlussfolgerung: mvvmFX nutzen mit MVVM Pattern. Damit kriegt man aus meiner Sicht eine gute Aufteilung hin. Schau Dir das mvvmFX einmal an. Da ist einiges an Dokumentation dabei und viele Links, z.B. zu ausführlicher MVVM Pattern Beschreibung.
 

Zurück
Oben