Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

JavaFX Discussion :

Action à la fermeture d'une fenêtre


Sujet :

JavaFX

  1. #1
    Membre à l'essai
    Action à la fermeture d'une fenêtre
    Bonjour à tous !

    par avance, un grand merci pour votre aide sur le sujet suivant, qui est un sujet de débutant. Je vais essayer de l'expliquer le plus simplement possible si j'y arrive.

    J'ai donc fait une petite application minimaliste "to do liste" sur javafx qui marche parfaitement.
    Au tout début du développement j'ai fait toute l'application dans une seule et unique classe, histoire de m'assurer que tout fonctionnait. et tout fonctionne !

    après avoir constaté que tout marchait très bien, j'ai décidé de recommencer mais avec une structure d'organisation plus propre à savoir : une classe ListViewDemo.java (ma classe main )qui va charger un MainApp.fxml ( qui contient la structure graphique de l'application ), puis ce MainApp.fxml va être controlé par un MainController.java.

    tout marche très bien, mon seul et unique souci : à la fermeture de ma fenêtre, au niveau du primary stage qui est déclaré au niveau de mon ListViewDemo.java, je veux enregistrer les données de ma todoliste pour qu'à la réouverture de cette todoliste les taches enregistrées apparaisent à nouveau.

    le problème, c'est que je dois faire référence dans le main à mon ObservableList, qui est déclaré dans mon MainController.java, et je n'y ai pas accès. à l'inverse, il m'est impossible d'observer la fermeture de mon primary stage depuis le MainController.java.

    voici mon code :

    ListViewDemo.java
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    package ListViewDemo;
     
    import javafx.application.Application;
    import javafx.fxml.FXMLLoader;
    import javafx.scene.Parent;
    import javafx.scene.Scene;
    import javafx.stage.Stage;
     
    public class ListViewDemo extends Application {
     
    	@Override
    	public void start(Stage stage) throws Exception {
    		Parent root = FXMLLoader.load(getClass().getResource("/ressources/fxml/MainApp.fxml"));
     
    		stage.setTitle("ToDoliste");
     
    		Scene scene = new Scene(root, 350, 200);
    		stage.setScene(scene);
    		stage.show();
    		stage.setResizable(false);
     
    //		stage.addEventFilter(javafx.stage.WindowEvent.WINDOW_CLOSE_REQUEST, e -> {
    //			try {
    //				Utils.DataLoadSave.writeToTextFile(books);
    //			} catch (IOException e1) {
    //			
    //				e1.printStackTrace();
    //			}
    //		});
     
    	}
     
    	public static void main(String[] args) {
    		launch(args);
    	}
    }


    on notera que la partie avec des quotes, est celle qui déclenche la sauvegarde des données de mon observable liste "books" en appelant la méthode Utils.DataLoadSave.writeToTextFile(books); et tout marchait tres bien au moment ou je déclarais mon Observable liste dans cette même classe ListViewDemo.java

    voici maintenant ma classe MainApp.fxml qui est appelée depuis ListViewDemo.java :
    MainApp.fxml
    Code xml :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
     
    <?xml version="1.0" encoding="UTF-8"?>
     
    <?import javafx.geometry.Insets?>
    <?import javafx.scene.Cursor?>
    <?import javafx.scene.control.Button?>
    <?import javafx.scene.control.ListView?>
    <?import javafx.scene.control.TextField?>
    <?import javafx.scene.layout.HBox?>
    <?import javafx.scene.layout.VBox?>
     
    <VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ListViewDemo.MainController">
    	<children>
    		<TextField id="textField" fx:id="textField" />
    		<HBox prefHeight="55.0" prefWidth="600.0">
    			<children>
    				<Button mnemonicParsing="false" onAction="#HandleAddTaskButtonAction" text="ajouter une tache">
    					<opaqueInsets>
    						<Insets bottom="30.0" left="30.0" right="30.0" top="30.0" />
    					</opaqueInsets>
    					<cursor>
    						<Cursor fx:constant="HAND" />
    					</cursor>
                   <HBox.margin>
                      <Insets bottom="3.0" left="3.0" right="3.0" top="3.0" />
                   </HBox.margin>
    				</Button>
    				<Button mnemonicParsing="false" onAction="#HandleRemoveTaskButtonAction" text="supprimer une tache">
    					<opaqueInsets>
    						<Insets bottom="30.0" left="30.0" right="30.0" top="30.0" />
    					</opaqueInsets>
    					<cursor>
    						<Cursor fx:constant="HAND" />
    					</cursor>
                   <HBox.margin>
                      <Insets bottom="3.0" left="3.0" right="3.0" top="3.0" />
                   </HBox.margin>
    				</Button>
                <Button mnemonicParsing="false" onAction="#HandleSaveButtonAction" text="SAVE">
                   <HBox.margin>
                      <Insets bottom="3.0" left="3.0" right="3.0" top="3.0" />
                   </HBox.margin>
                </Button>
    			</children>
    			<opaqueInsets>
    				<Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
    			</opaqueInsets>
             <VBox.margin>
                <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
             </VBox.margin>
    		</HBox>
    		<ListView id="listView" fx:id="listView" prefHeight="400.0" prefWidth="600.0" />
    	</children>
    </VBox>

    je met cette classe juste pour info car le problème je pense ne se situe pas là.

    voici maintenant ma classe MainController.java, qui va controller les éléments définis dans le MainApp.fxml.
    c'est dans cette classe que je déclare mon ObservableList avec laquelle je tente de communiquer depuis ma classe ListViewDemo qui est ma classe main

    MainController.java
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
     
    package ListViewDemo;
     
     
    import java.io.IOException;
    import java.net.URL;
    import java.util.ResourceBundle;
    import javafx.scene.control.TextField;
    import javafx.collections.ObservableList;
    import javafx.event.ActionEvent;
    import javafx.fxml.FXML;
    import javafx.fxml.Initializable;
    import javafx.scene.control.ListView;
     
    public class MainController implements Initializable{
    	//Creat an Observable List
    	public ObservableList<Book> books = Utils.DataLoadSave.intializeObservable();
     
        // Create a ListView
    	@FXML
    	private ListView<Book> listView ;
     
     
     
    	@FXML
    	private TextField textField ;
     
     
     
    	@FXML
    	protected void HandleAddTaskButtonAction( ActionEvent e) {
    		String monTexte = textField.getText();
     
     
    		if (!monTexte.isEmpty()) {
    			Book bookNext = new Book(monTexte);
    			books.add(books.size(), bookNext);
    			textField.clear();
    			System.out.println(books);
    		}
    	}
     
    	@FXML
    	protected void HandleRemoveTaskButtonAction( ActionEvent e) {
    		System.out.println("ok remove");
    		if (!listView.getSelectionModel().isEmpty()) {
    			int index = listView.getSelectionModel().getSelectedIndex();
    			books.remove(index);
    		}
    	}
     
    	@FXML
    	protected void HandleSaveButtonAction ( ActionEvent e ) throws IOException {
    		Utils.DataLoadSave.writeToTextFile(books);
    	}
     
     
     
    	@Override
    	public void initialize(URL arg0, ResourceBundle arg1) {
    		listView.setItems(books);
    	}
     
    }


    et aussi juste pour info voici la classe qui me permet de sauver et de charger les infos déclarées dans mon ObservableList :

    DataLoadSave.java
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    package Utils;
     
    import java.io.BufferedReader;
    import java.io.FileWriter;
    import java.io.IOException;
    import java.nio.file.Files;
    import java.nio.file.Paths;
     
    import ListViewDemo.Book;
    import javafx.collections.FXCollections;
    import javafx.collections.ObservableList;
     
    public class DataLoadSave {
     
     
     
    	//write the list to text file
    	 public static void writeToTextFile( ObservableList<Book> books)
    	            throws IOException {
    		 	System.out.println("ok on sauve dans dataload save" + books);
    	        FileWriter writer = new FileWriter("students.txt");
    	        for (Book book : books) {
    				writer.write(book.getName() +"\n");
    			}
    	        writer.close();
    	    }
     
    	public static ObservableList<Book> intializeObservable() {
    		ObservableList<Book> books = FXCollections.observableArrayList();
    		 BufferedReader reader = null;
    			try {
    				reader = Files.newBufferedReader(Paths.get("students.txt"));
    			} catch (IOException e) {
     
    				e.printStackTrace();
    			}
    			 String line;
    			 try {
     
    				while((line = reader.readLine()) != null) {
    					 //myArray.add(line);
    					 books.add(new Book(line));
    				 }
    			} catch (IOException e) {
     
    				e.printStackTrace();
    			}
    			 return books;
    	}
     
     
    }


    voilà j'espère que tout ca n'est pas trop compliqué à comprendre...le problème ultime : je ne parviens pas à passer mon observableList en argument depuis mon main. Le coeur du problème est dans la partie en QUOTE dans ma classe 'main' qui se nomme ListViewDemo.java

    un grand merci !!

  2. #2
    Membre à l'essai
    j'ai pu résoudre mon problème en le contournant :
    plutôt que de tenter d'utiliser le bouton de fermeture de fenetre classique, et de déclencher une action à partir de cet événement, j'ai utilisé la propriété UNDECORATED du stage principal comme ceci :
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    primaryStage.initStyle(StageStyle.UNDECORATED);


    et après j'ai crée un nouveau bouton de fermeture dans Main.fxml, controlé dans mon MainController.java, et c'est là que je peux à la fois fermer l'application grâce à Plateform.exit(); ET à cet endroit je peux accéder à mon ObservableList 'books' et la passer en paramètre à la méthode writeToTextFile(books); ceci me permet de sauver mes données à la fermeture de l'application

    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
           @FXML
    	protected void HandleExitButtonAction ( ActionEvent e ) throws IOException {
    		Utils.DataLoadSave.writeToTextFile(books);
                    Plateform.exit();
    	}

  3. #3
    Rédacteur/Modérateur

    C'est une très bonne manière de procédé si tu peux justifier que ton client ne sera pas gêné d'avoir une fenêtre non-décorée et avec un style qui lui est propre (pas mal d'application modernes sont ainsi).

    Tu avais également d'autres alternatives pour procéder à la détection de la fermeture de la fenêtre :
    • Ton contrôleur aurait pu tenter de retrouver la fenêtre dans laquelle ses contrôles sont affichés en faisant monControle.getScene().getWindow() sur n'importe lequel des contrôles qu'il référence. Seul hic : la valeur de retournée par getScene() est null tant que les contrôles ne sont pas attachés à la scène et la getWindow() est null tant que cette scène n'est pas insérée dans une fenêtre. Mais bon rien de bien critique puisqu'on est en JavaFX et que tout est observable.
    • Ton contrôleur aurait pu fournir un callback que la partie principale de l'application aurait pu invoquer lors de la fermeture de la fenêtre. Là c'est juste écrire un getter ou une propriété dans le contrôleur à destination de la partie principale de l'application.
    • Inversement la partie principale de l'application aurait pu fournir dans ton ton contrôleur un callback invoqué lors de la fermeture de la fenêtre. Là il faut juste écrire un setter ou une propriété dans le contrôleur qui sera accédée par la partie principale de l'application.
    Merci de penser au tag quand une réponse a été apportée à votre question. Aucune réponse ne sera donnée à des messages privés portant sur des questions d'ordre technique. Les forums sont là pour que vous y postiez publiquement vos problèmes.

    suivez mon blog sur Développez.

    Programming today is a race between software engineers striving to build bigger and better idiot-proof programs, and the universe trying to produce bigger and better idiots. So far, the universe is winning. ~ Rich Cook

  4. #4
    Membre à l'essai
    merci beaucoup Bouye ! je vais aller faire un tour du côté de tes solutions, voir si j'y arrive

###raw>template_hook.ano_emploi###