7.2Fenêtres et dialogues
7.2.1 – Alert, Confirm, TextInputDialog
Les dialogues (ou boîtes de dialogue) sont des fenêtres qui apparaissent au-dessus de votre application pour afficher des messages, demander des confirmations, ou récupérer des informations de l'utilisateur. JavaFX fournit plusieurs types de dialogues prêts à l'emploi.
🎯 Types de dialogues disponibles
JavaFX propose plusieurs types de dialogues :
- Alert : Pour afficher des messages (information, avertissement, erreur, confirmation)
- TextInputDialog : Pour demander à l'utilisateur de saisir du texte
- ChoiceDialog : Pour faire choisir parmi plusieurs options
- Dialog : Pour créer des dialogues personnalisés
📝 Alert : Les boîtes de dialogue simples
La classe Alert permet d'afficher différents types de messages. Il existe plusieurs types prédéfinis :
- INFORMATION : Pour afficher une information
- WARNING : Pour avertir l'utilisateur
- ERROR : Pour signaler une erreur
- CONFIRMATION : Pour demander une confirmation (Oui/Non)
- NONE : Pour créer un dialogue personnalisé
Exemple 1 : Alert générique (NONE)
Un Alert de type NONE vous permet de personnaliser complètement les boutons :
AlertType.NONE affichée@FXML
private void showAlert() {
Alert alert = new Alert(AlertType.NONE);
alert.setTitle("Alert");
alert.setHeaderText("Ceci est une boîte de dialogue Alert");
alert.setContentText("Message d'alerte générique.");
alert.getButtonTypes().setAll(ButtonType.OK);
alert.showAndWait();
}
•
AlertType.NONE : Type personnalisé•
setTitle() : Le titre de la fenêtre•
setHeaderText() : Le texte principal•
setContentText() : Le texte de contenu•
getButtonTypes().setAll(ButtonType.OK) : Définit les boutons (ici juste OK)•
showAndWait() : Affiche le dialogue et attend que l'utilisateur clique
Exemple 2 : Alert d'information
Pour afficher une simple information :
AlertType.INFORMATION affichée@FXML
private void showInformationAlert() {
Alert alert = new Alert(AlertType.INFORMATION);
alert.setTitle("Information");
alert.setHeaderText("Information");
alert.setContentText("Ceci est une boîte de dialogue d'information.");
alert.showAndWait();
}
AlertType.INFORMATION, le bouton OK est ajouté automatiquement. Vous n'avez pas besoin de le définir manuellement.
Exemple 3 : Alert d'avertissement
Pour afficher une simple avertissement :
AlertType.WARNING affichée@FXML
private void showWarningAlert() {
Alert alert = new Alert(AlertType.WARNING);
alert.setTitle("Avertissement");
alert.setHeaderText("Attention !");
alert.setContentText("Ceci est une boîte de dialogue d'avertissement.");
alert.showAndWait();
}
Exemple 4 : Alert d'erreur
Pour afficher une simple erreur :
AlertType.ERROR affichée@FXML
private void showErrorAlert() {
Alert alert = new Alert(AlertType.ERROR);
alert.setTitle("Erreur");
alert.setHeaderText("Une erreur s'est produite");
alert.setContentText("Ceci est une boîte de dialogue d'erreur.");
alert.showAndWait();
}
Exemple 5 : Alert de confirmation
Pour demander une confirmation à l'utilisateur, utilisez CONFIRMATION et récupérez le résultat :
AlertType.CONFIRMATION affichée@FXML
private void showConfirmationAlert() {
Alert alert = new Alert(AlertType.CONFIRMATION);
alert.setTitle("Confirmation");
alert.setHeaderText("Confirmation requise");
alert.setContentText("Voulez-vous continuer ?");
Optional<ButtonType> result = alert.showAndWait();
if (result.isPresent() && result.get() == ButtonType.OK) {
System.out.println("Vous avez cliqué sur OK !");
} else {
System.out.println("Vous avez annulé l'opération.");
}
}
•
showAndWait() retourne un Optional<ButtonType>•
result.isPresent() : Vérifie si l'utilisateur a cliqué sur un bouton (et n'a pas fermé la fenêtre)•
result.get() == ButtonType.OK : Vérifie si c'est le bouton OK qui a été cliqué• Les boutons par défaut sont OK et Annuler
Exemple 6 : Confirmation avec plusieurs boutons personnalisés
Vous pouvez créer une confirmation avec plusieurs boutons personnalisés :
AlertType.CONFIRMATION avec plusieurs boutons personnalisés affichée@FXML
private void showMultiButtonConfirmation() {
Alert dialog = new Alert(Alert.AlertType.CONFIRMATION);
dialog.setTitle("logout");
dialog.setHeaderText(null);
dialog.setContentText("Merci de confirmer votre choix");
ButtonType yes = new ButtonType("OUI");
ButtonType non = new ButtonType("NON");
ButtonType later = new ButtonType("Plus tard");
ButtonType cancel = new ButtonType("Annuler", ButtonBar.ButtonData.CANCEL_CLOSE);
dialog.getButtonTypes().setAll(yes, non, later, cancel);
Optional<ButtonType> answer = dialog.showAndWait();
if (answer.isPresent()) {
if (answer.get() == yes) {
System.out.println("User chose OUI");
} else if (answer.get() == non) {
System.out.println("User chose Non");
} else if (answer.get() == later) {
System.out.println("User chose Plus tard");
} else {
System.out.println("Annuler ou fermer");
}
}
}
ButtonBar.ButtonData.CANCEL_CLOSE pour le bouton d'annulation qui fermera la fenêtre.
📝 TextInputDialog : Saisie de texte
TextInputDialog permet de demander à l'utilisateur de saisir du texte :
TextInputDialog affichée@FXML
private void showTextInputDialog() {
TextInputDialog dialog = new TextInputDialog("Valeur par défaut");
dialog.setTitle("Saisie de texte");
dialog.setHeaderText("Entrez votre nom");
dialog.setContentText("Nom :");
Optional<String> result = dialog.showAndWait();
result.ifPresent(name -> {
System.out.println("Vous avez entré : " + name);
});
}
•
new TextInputDialog("Valeur par défaut") : Crée un dialogue avec une valeur par défaut•
showAndWait() retourne un Optional<String>•
result.ifPresent() : Exécute le code seulement si l'utilisateur a saisi quelque chose et cliqué sur OK• Si l'utilisateur annule,
result sera vide
📝 ChoiceDialog : Choix parmi plusieurs options
ChoiceDialog permet de faire choisir l'utilisateur parmi une liste d'options :
ChoiceDialog affichée@FXML
private void showChoiceDialog() {
List<String> choices = Arrays.asList("Option 1", "Option 2", "Option 3", "Option 4");
ChoiceDialog<String> dialog = new ChoiceDialog<>("Option 1", choices);
dialog.setTitle("Choix");
dialog.setHeaderText("Sélectionnez une option");
dialog.setContentText("Choisissez :");
Optional<String> result = dialog.showAndWait();
result.ifPresent(choice -> {
System.out.println("Vous avez choisi : " + choice);
});
}
•
Arrays.asList() : Crée une liste d'options•
new ChoiceDialog<>("Option 1", choices) : "Option 1" est la sélection par défaut• L'utilisateur verra une liste déroulante avec les 4 options
• Le résultat est la chaîne choisie par l'utilisateur
📝 Dialog personnalisé : Créer votre propre dialogue
Pour créer un dialogue complètement personnalisé avec vos propres champs, utilisez la classe Dialog :
Dialog affichée@FXML
private void showCustomDialog() {
Dialog<String> dialog = new Dialog<>();
dialog.setTitle("Boîte de dialogue personnalisée");
dialog.setHeaderText("Exemple de boîte de dialogue personnalisée");
dialog.setResizable(true);
// Créer le contenu personnalisé
Label label1 = new Label("Nom :");
TextField textField1 = new TextField();
Label label2 = new Label("Email :");
TextField textField2 = new TextField();
VBox vbox = new VBox(10);
vbox.getChildren().addAll(label1, textField1, label2, textField2);
vbox.getStyleClass().add("dialog-content");
dialog.getDialogPane().setContent(vbox);
// Ajouter les boutons
ButtonType okButtonType = new ButtonType("OK", ButtonData.OK_DONE);
ButtonType cancelButtonType = new ButtonType("Annuler", ButtonData.CANCEL_CLOSE);
dialog.getDialogPane().getButtonTypes().addAll(okButtonType, cancelButtonType);
// Désactiver le bouton OK si les champs sont vides
Button okButton = (Button) dialog.getDialogPane().lookupButton(okButtonType);
okButton.setDisable(true);
textField1.textProperty().addListener((observable, oldValue, newValue) -> {
okButton.setDisable(newValue.trim().isEmpty() || textField2.getText().trim().isEmpty());
});
textField2.textProperty().addListener((observable, oldValue, newValue) -> {
okButton.setDisable(newValue.trim().isEmpty() || textField1.getText().trim().isEmpty());
});
// Convertir le résultat
dialog.setResultConverter(dialogButton -> {
if (dialogButton == okButtonType) {
return "Nom: " + textField1.getText() + ", Email: " + textField2.getText();
}
return null;
});
Optional<String> result = dialog.showAndWait();
result.ifPresent(data -> {
System.out.println("Données saisies :\n" + data);
});
}
•
Dialog<String> : Le type entre <> est le type de résultat retourné•
dialog.getDialogPane().setContent(vbox) : Définit le contenu personnalisé•
vbox.getStyleClass().add("dialog-content") : Ajoute une classe CSS (définie dans votre fichier CSS externe)•
lookupButton() : Récupère un bouton pour le modifier (désactiver, etc.)•
textProperty().addListener() : Écoute les changements dans les champs de texte•
setResultConverter() : Convertit le bouton cliqué en résultat (ici, une chaîne avec les données)• Important : Utilisez
getStyleClass().add() pour ajouter des classes CSS au lieu de setStyle(). Définissez les styles dans votre fichier CSS externe.
Dialog personnalisé en important un fichier FXML !Utilisez
FXMLLoader pour charger le contenu (par exemple un formulaire complexe créé dans Scene Builder), puis faites dialog.getDialogPane().setContent(fxmlNode).C'est idéal si votre dialogue nécessite une interface plus riche ou de la réutilisation de code graphique.
- ✅ Les dialogues sont créés en Java (pas besoin de FXML)
- ✅ Utilisez
showAndWait()pour attendre la réponse de l'utilisateur - ✅ Le résultat est toujours un
Optional(peut être vide si l'utilisateur annule) - ✅ Utilisez
ifPresent()ouisPresent()pour vérifier si l'utilisateur a répondu
💡 Points clés à retenir
- Alert : Pour afficher des messages simples (information, erreur, confirmation)
- TextInputDialog : Pour demander une saisie de texte
- ChoiceDialog : Pour faire choisir parmi plusieurs options
- Dialog : Pour créer des dialogues complètement personnalisés
- showAndWait() : Affiche le dialogue et attend la réponse
- Optional : Le résultat est toujours un Optional (peut être vide)
- ifPresent() : Méthode pratique pour traiter le résultat seulement s'il existe
MainController.java pour comprendre comment ils fonctionnent. C'est le meilleur moyen d'apprendre !
7.2.2 – Stage secondaire (fenêtre enfant)
Un Stage secondaire (ou fenêtre enfant) est une fenêtre indépendante qui s'ouvre depuis votre fenêtre principale. Contrairement aux dialogues qui sont modaux (ils bloquent l'interaction avec le reste de l'application), les Stages secondaires permettent à l'utilisateur de continuer à utiliser la fenêtre principale.
🎯 Quand utiliser un Stage secondaire ?
Utilisez un Stage secondaire quand :
- Vous voulez ouvrir une nouvelle fenêtre indépendante
- L'utilisateur doit pouvoir utiliser les deux fenêtres en même temps
- Vous voulez afficher un formulaire, une fenêtre de paramètres, ou une fenêtre d'aide
- Vous avez besoin de plusieurs fenêtres ouvertes simultanément
📝 Exemple simple : Créer une fenêtre secondaire
Créons un exemple simple : une fenêtre principale avec un bouton qui ouvre une fenêtre secondaire.
Étape 1 : Créer l'interface de la fenêtre secondaire dans Scene Builder
Créez un fichier fenetreSecondaire.fxml dans Scene Builder :
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.VBox?>
<BorderPane xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="application.FenetreSecondaireController"
stylesheets="style="".css">
<center>
<VBox alignment="CENTER" spacing="20.0">
<children>
<Label text="Ceci est une fenêtre secondaire" />
<Label text="Vous pouvez utiliser les deux fenêtres en même temps" />
<Button fx:id="btnFermer" onAction="#fermer" text="Fermer" />
</children>
</VBox>
</center>
</BorderPane>
Étape 2 : Créer le contrôleur de la fenêtre secondaire
Créez un fichier FenetreSecondaireController.java :
package application;
import javafx.fxml.FXML;
import javafx.stage.Stage;
public class FenetreSecondaireController {
private Stage stage;
// Cette méthode est appelée quand on clique sur "Fermer"
@FXML
private void fermer() {
if (stage != null) {
stage.close();
}
}
// Cette méthode permet de donner la référence au Stage
public void setStage(Stage stage) {
this.stage = stage;
}
}
•
stage.close() : Ferme la fenêtre secondaire•
setStage() : Permet à la classe qui crée la fenêtre de donner la référence au Stage
Étape 3 : Créer la fenêtre secondaire depuis le contrôleur principal
Dans votre contrôleur principal, créez une méthode pour ouvrir la fenêtre secondaire :
package application;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class MainController {
private Stage stagePrincipal; // La fenêtre principale
// Cette méthode est appelée quand on clique sur "Ouvrir fenêtre secondaire"
@FXML
private void ouvrirFenetreSecondaire() {
try {
// Charger le FXML de la fenêtre secondaire
FXMLLoader loader = new FXMLLoader(getClass().getResource("fenetreSecondaire.fxml"));
Scene scene = new Scene(loader.load());
// Créer un nouveau Stage (fenêtre)
Stage stageSecondaire = new Stage();
stageSecondaire.setScene(scene);
stageSecondaire.setTitle("Fenêtre secondaire");
stageSecondaire.setWidth(400);
stageSecondaire.setHeight(300);
// Définir la fenêtre principale comme parent (optionnel mais recommandé)
stageSecondaire.initOwner(stagePrincipal);
// Donner la référence au contrôleur
FenetreSecondaireController controller = loader.getController();
controller.setStage(stageSecondaire);
// Afficher la fenêtre
stageSecondaire.show();
} catch (Exception e) {
e.printStackTrace();
}
}
// Cette méthode permet de donner la référence au Stage principal
public void setStagePrincipal(Stage stage) {
this.stagePrincipal = stage;
}
}
•
new Stage() : Crée une nouvelle fenêtre•
stageSecondaire.setScene(scene) : Définit le contenu de la fenêtre•
initOwner(stagePrincipal) : Définit la fenêtre principale comme parent (la fenêtre secondaire se fermera si la principale se ferme)•
stageSecondaire.show() : Affiche la fenêtre (non-modale par défaut)
🔧 Propriétés importantes d'un Stage secondaire
Définir le propriétaire (initOwner)
Il est recommandé de définir la fenêtre principale comme propriétaire de la fenêtre secondaire :
stageSecondaire.initOwner(stagePrincipal);
Avantages :
- La fenêtre secondaire se ferme automatiquement si la principale se ferme
- La fenêtre secondaire reste toujours au-dessus de la principale
- Meilleure gestion de la hiérarchie des fenêtres
Rendre la fenêtre modale (optionnel)
Par défaut, une fenêtre secondaire est non-modale. Pour la rendre modale (comme un dialogue) :
stageSecondaire.initModality(Modality.APPLICATION_MODAL);
// ou
stageSecondaire.initModality(Modality.WINDOW_MODAL);
•
Modality.NONE : Non-modale (par défaut) - vous pouvez utiliser les deux fenêtres•
Modality.WINDOW_MODAL : Modale par rapport à la fenêtre parente•
Modality.APPLICATION_MODAL : Modale pour toute l'application
Positionner la fenêtre
Vous pouvez positionner la fenêtre secondaire par rapport à la fenêtre principale :
// Centrer la fenêtre secondaire par rapport à la principale
stageSecondaire.setX(stagePrincipal.getX() + (stagePrincipal.getWidth() - stageSecondaire.getWidth()) / 2);
stageSecondaire.setY(stagePrincipal.getY() + (stagePrincipal.getHeight() - stageSecondaire.getHeight()) / 2);
// Ou simplement utiliser centerOnScreen() pour centrer sur l'écran
stageSecondaire.centerOnScreen();
📝 Exemple complet : Fenêtre de paramètres
Créons un exemple plus complet : une fenêtre principale avec un bouton "Paramètres" qui ouvre une fenêtre secondaire de paramètres.
FenetreParametresController.java
package application;
import javafx.fxml.FXML;
import javafx.scene.control.TextField;
import javafx.stage.Stage;
public class FenetreParametresController {
@FXML
private TextField txtNom;
@FXML
private TextField txtEmail;
private Stage stage;
// Méthode appelée automatiquement après le chargement
@FXML
private void initialize() {
// Charger les valeurs par défaut (exemple)
txtNom.setText("Jean Dupont");
txtEmail.setText("jean@exemple.com");
}
// Méthode appelée quand on clique sur "Enregistrer"
@FXML
private void enregistrer() {
String nom = txtNom.getText();
String email = txtEmail.getText();
System.out.println("Nom : " + nom);
System.out.println("Email : " + email);
// Fermer la fenêtre après enregistrement
if (stage != null) {
stage.close();
}
}
// Méthode appelée quand on clique sur "Annuler"
@FXML
private void annuler() {
if (stage != null) {
stage.close();
}
}
public void setStage(Stage stage) {
this.stage = stage;
}
}
💡 Points clés à retenir
- Stage secondaire : Une fenêtre indépendante créée avec
new Stage() - Non-modale par défaut : L'utilisateur peut utiliser les deux fenêtres en même temps
- initOwner() : Définit la fenêtre principale comme parent (recommandé)
- initModality() : Pour rendre la fenêtre modale si nécessaire
- show() : Affiche la fenêtre (non-bloquant)
- close() : Ferme la fenêtre secondaire
- FXML + Scene Builder : Créez l'interface de la fenêtre secondaire dans Scene Builder
- ✅ Utilisez
initOwner()pour définir la fenêtre parente - ✅ Donnez la référence au Stage au contrôleur pour qu'il puisse fermer la fenêtre
- ✅ Les fenêtres secondaires sont non-modales par défaut (contrairement aux dialogues)
7.2.3 – Fenêtres modales
Une fenêtre modale est une fenêtre qui bloque l'interaction avec les autres fenêtres de l'application jusqu'à ce qu'elle soit fermée. C'est le comportement par défaut des dialogues (Alert, TextInputDialog, etc.), mais vous pouvez aussi rendre n'importe quelle fenêtre secondaire modale.
🎯 Comprendre la modalité
En JavaFX, il existe trois niveaux de modalité :
- NONE : La fenêtre est non-modale. L'utilisateur peut interagir avec toutes les fenêtres.
- WINDOW_MODAL : La fenêtre bloque uniquement l'interaction avec sa fenêtre parente.
- APPLICATION_MODAL : La fenêtre bloque l'interaction avec toute l'application.
Modality.NONE). Les dialogues (Alert, etc.) sont automatiquement modaux (APPLICATION_MODAL).
📝 Exemple 1 : Fenêtre modale par rapport à la fenêtre parente (WINDOW_MODAL)
Utilisez WINDOW_MODAL quand vous voulez que la fenêtre bloque uniquement l'interaction avec sa fenêtre parente, mais permette l'utilisation d'autres fenêtres de l'application :
package application;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Modality;
import javafx.stage.Stage;
public class MainController {
private Stage stagePrincipal;
@FXML
private void ouvrirFenetreModale() {
try {
// Charger le FXML de la fenêtre modale
FXMLLoader loader = new FXMLLoader(getClass().getResource("fenetreModale.fxml"));
Scene scene = new Scene(loader.load());
// Créer un nouveau Stage
Stage stageModale = new Stage();
stageModale.setScene(scene);
stageModale.setTitle("Fenêtre modale");
stageModale.setWidth(400);
stageModale.setHeight(300);
// Définir la fenêtre principale comme parent
stageModale.initOwner(stagePrincipal);
// Rendre la fenêtre modale par rapport à la fenêtre parente
stageModale.initModality(Modality.WINDOW_MODAL);
// Donner la référence au contrôleur
FenetreModaleController controller = loader.getController();
controller.setStage(stageModale);
// Afficher la fenêtre (bloquera l'interaction avec la fenêtre principale)
stageModale.showAndWait();
// Ce code s'exécutera seulement après la fermeture de la fenêtre modale
System.out.println("La fenêtre modale a été fermée");
} catch (Exception e) {
e.printStackTrace();
}
}
public void setStagePrincipal(Stage stage) {
this.stagePrincipal = stage;
}
}
•
initModality(Modality.WINDOW_MODAL) : Rend la fenêtre modale par rapport à sa fenêtre parente•
showAndWait() : Affiche la fenêtre et attend qu'elle soit fermée (bloque l'exécution)• L'utilisateur ne peut pas interagir avec la fenêtre principale tant que la fenêtre modale est ouverte
• Mais il peut toujours utiliser d'autres fenêtres de l'application (si elles existent)
📝 Exemple 2 : Fenêtre modale pour toute l'application (APPLICATION_MODAL)
Utilisez APPLICATION_MODAL quand vous voulez que la fenêtre bloque l'interaction avec toute l'application :
@FXML
private void ouvrirFenetreApplicationModale() {
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("fenetreModale.fxml"));
Scene scene = new Scene(loader.load());
Stage stageModale = new Stage();
stageModale.setScene(scene);
stageModale.setTitle("Fenêtre modale (Application)");
stageModale.setWidth(400);
stageModale.setHeight(300);
// Définir la fenêtre principale comme parent (optionnel mais recommandé)
stageModale.initOwner(stagePrincipal);
// Rendre la fenêtre modale pour toute l'application
stageModale.initModality(Modality.APPLICATION_MODAL);
FenetreModaleController controller = loader.getController();
controller.setStage(stageModale);
// Afficher et attendre
stageModale.showAndWait();
System.out.println("Fenêtre modale fermée");
} catch (Exception e) {
e.printStackTrace();
}
}
•
WINDOW_MODAL : Bloque uniquement la fenêtre parente•
APPLICATION_MODAL : Bloque toute l'application (comme les dialogues)•
APPLICATION_MODAL est le comportement par défaut des dialogues (Alert, etc.)
📝 Exemple 3 : Fenêtre non-modale (NONE)
Par défaut, les Stages secondaires sont non-modaux. Vous pouvez l'expliciter avec Modality.NONE :
@FXML
private void ouvrirFenetreNonModale() {
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("fenetreSecondaire.fxml"));
Scene scene = new Scene(loader.load());
Stage stageSecondaire = new Stage();
stageSecondaire.setScene(scene);
stageSecondaire.setTitle("Fenêtre non-modale");
stageSecondaire.setWidth(400);
stageSecondaire.setHeight(300);
stageSecondaire.initOwner(stagePrincipal);
// Expliciter que la fenêtre est non-modale (optionnel, c'est le défaut)
stageSecondaire.initModality(Modality.NONE);
FenetreSecondaireController controller = loader.getController();
controller.setStage(stageSecondaire);
// Utiliser show() au lieu de showAndWait() pour ne pas bloquer
stageSecondaire.show();
// Ce code s'exécutera immédiatement (sans attendre)
System.out.println("Fenêtre non-modale ouverte");
} catch (Exception e) {
e.printStackTrace();
}
}
•
Modality.NONE : La fenêtre est non-modale (comportement par défaut)•
show() : Affiche la fenêtre sans bloquer l'exécution• L'utilisateur peut utiliser toutes les fenêtres en même temps
• Le code continue à s'exécuter immédiatement après
show()
🔄 Comparaison : show() vs showAndWait()
La méthode que vous utilisez pour afficher la fenêtre est importante :
- show() : Affiche la fenêtre et continue l'exécution immédiatement (non-bloquant)
- showAndWait() : Affiche la fenêtre et attend qu'elle soit fermée (bloquant)
Exemple avec show()
@FXML
private void exempleShow() {
Stage stage = new Stage();
stage.setScene(new Scene(new Label("Fenêtre")));
stage.show(); // N'attend pas
// Ce code s'exécute immédiatement
System.out.println("Fenêtre ouverte, code continue");
}
Exemple avec showAndWait()
@FXML
private void exempleShowAndWait() {
Stage stage = new Stage();
stage.setScene(new Scene(new Label("Fenêtre")));
stage.showAndWait(); // Attend la fermeture
// Ce code s'exécute seulement après la fermeture de la fenêtre
System.out.println("Fenêtre fermée, code continue");
}
• Utilisez
show() pour les fenêtres non-modales (l'utilisateur peut continuer à utiliser l'application)• Utilisez
showAndWait() pour les fenêtres modales (vous voulez attendre la réponse de l'utilisateur)• Les dialogues utilisent automatiquement
showAndWait()
📝 Exemple complet : Fenêtre modale de saisie d'informations
Créons un exemple pratique d'une fenêtre modale qui demande à l'utilisateur de saisir son nom, prénom et âge. Après avoir cliqué sur OK, les informations saisies doivent être affichées dans la fenêtre parente. Si l'utilisateur clique sur Annuler, la fenêtre modale doit simplement se fermer sans rien afficher.
FenetreSaisie.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<BorderPane xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="application.FenetreSaisieController"
stylesheets="style="".css"
prefWidth="400.0"
prefHeight="250.0">
<center>
<VBox spacing="20.0" style="-fx-padding: 30px;">
<children>
<Label text="Saisissez vos informations"
style="-fx-font-size: 18px; -fx-font-weight: bold;" />
<GridPane hgap="10.0" vgap="15.0">
<columnConstraints>
<ColumnConstraints minWidth="80.0" />
<ColumnConstraints minWidth="200.0" />
</columnConstraints>
<children>
<Label text="Prénom :" GridPane.columnIndex="0" GridPane.rowIndex="0" />
<TextField fx:id="txtPrenom" GridPane.columnIndex="1" GridPane.rowIndex="0" />
<Label text="Nom :" GridPane.columnIndex="0" GridPane.rowIndex="1" />
<TextField fx:id="txtNom" GridPane.columnIndex="1" GridPane.rowIndex="1" />
<Label text="Âge :" GridPane.columnIndex="0" GridPane.rowIndex="2" />
<TextField fx:id="txtAge" GridPane.columnIndex="1" GridPane.rowIndex="2" />
</children>
</GridPane>
<HBox spacing="15.0" alignment="CENTER">
<children>
<Button fx:id="btnOK"
onAction="#valider"
text="OK"
style="-fx-min-width: 100px;"
defaultButton="true" />
<Button fx:id="btnAnnuler"
onAction="#annuler"
text="Annuler"
style="-fx-min-width: 100px;"
cancelButton="true" />
</children>
</HBox>
</children>
</VBox>
</center>
</BorderPane>
FenetreSaisieController.java
package application;
import javafx.fxml.FXML;
import javafx.scene.control.TextField;
import javafx.stage.Stage;
public class FenetreSaisieController {
@FXML
private TextField txtPrenom;
@FXML
private TextField txtNom;
@FXML
private TextField txtAge;
private Stage stage;
private boolean valide = false;
private String prenom;
private String nom;
private String age;
// Méthodes pour récupérer les données saisies
public String getPrenom() {
return prenom;
}
public String getNom() {
return nom;
}
public String getAge() {
return age;
}
// Méthode pour vérifier si l'utilisateur a validé
public boolean estValide() {
return valide;
}
@FXML
private void valider() {
// Récupérer les valeurs saisies
prenom = txtPrenom.getText().trim();
nom = txtNom.getText().trim();
age = txtAge.getText().trim();
// Vérifier que les champs ne sont pas vides
if (prenom.isEmpty() || nom.isEmpty() || age.isEmpty()) {
// Ne pas fermer la fenêtre, l'utilisateur doit remplir tous les champs
return;
}
valide = true;
if (stage != null) {
stage.close();
}
}
@FXML
private void annuler() {
valide = false;
if (stage != null) {
stage.close();
}
}
public void setStage(Stage stage) {
this.stage = stage;
}
}
style.css
Créez un fichier style.css dans le même package que vos fichiers FXML pour définir les styles :
/* Styles pour les messages */
.message-success {
-fx-text-fill: green;
}
.message-warning {
-fx-text-fill: orange;
}
/* Style pour le contenu des dialogues */
.dialog-content {
-fx-padding: 20px;
}
/* Styles généraux */
Label {
-fx-font-size: 14px;
}
Button {
-fx-font-size: 14px;
-fx-padding: 8px 16px;
}
TextField {
-fx-font-size: 14px;
-fx-padding: 5px;
}
stylesheets="style="".css". Assurez-vous que le fichier CSS se trouve dans le même répertoire que les fichiers FXML (dans le package application).
main.fxml (fenêtre principale)
Dans votre fenêtre principale, vous devez avoir des Labels pour afficher les informations et les messages :
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.VBox?>
<BorderPane xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="application.MainController"
stylesheets="style="".css"
prefWidth="600.0"
prefHeight="400.0">
<center>
<VBox spacing="20.0" alignment="CENTER" style="-fx-padding: 30px;">
<children>
<Label text="Exemple Fenêtre Modale"
style="-fx-font-size: 20px; -fx-font-weight: bold;" />
<Button fx:id="btnOuvrirSaisie"
onAction="#ouvrirFenetreSaisie"
text="Saisir mes informations"
style="-fx-min-width: 200px; -fx-min-height: 40px;" />
<Label fx:id="lblMessage"
text=""
style="-fx-font-size: 14px;" />
<Label fx:id="lblAffichage"
text=""
style="-fx-font-size: 16px; -fx-font-weight: bold;"
wrapText="true" />
</children>
</VBox>
</center>
</BorderPane>
Utilisation dans le contrôleur principal
package application;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.stage.Modality;
import javafx.stage.Stage;
public class MainController {
@FXML
private Label lblAffichage; // Label dans la fenêtre principale pour afficher les infos
@FXML
private Label lblMessage; // Label pour afficher les messages (erreurs, annulation, etc.)
private Stage stagePrincipal;
@FXML
private void ouvrirFenetreSaisie() {
try {
// Charger la fenêtre de saisie
FXMLLoader loader = new FXMLLoader(getClass().getResource("FenetreSaisie.fxml"));
Scene scene = new Scene(loader.load());
// Créer le Stage modale
Stage stageSaisie = new Stage();
stageSaisie.setScene(scene);
stageSaisie.setTitle("Saisie d'informations");
stageSaisie.setResizable(false); // Taille définie dans FXML
// Définir comme modale pour toute l'application
stageSaisie.initOwner(stagePrincipal);
stageSaisie.initModality(Modality.APPLICATION_MODAL);
// Configurer le contrôleur
FenetreSaisieController controller = loader.getController();
controller.setStage(stageSaisie);
// Afficher et attendre la réponse
stageSaisie.showAndWait();
// Vérifier le résultat
if (controller.estValide()) {
// L'utilisateur a cliqué sur OK et les champs sont remplis
String prenom = controller.getPrenom();
String nom = controller.getNom();
String age = controller.getAge();
// Afficher les informations dans la fenêtre parente
String message = String.format("Prénom : %s\nNom : %s\nÂge : %s ans",
prenom, nom, age);
lblAffichage.setText(message);
// Afficher un message de confirmation
if (lblMessage != null) {
lblMessage.setText("Informations saisies avec succès !");
lblMessage.getStyleClass().setAll("message-success");
}
} else {
// L'utilisateur a cliqué sur Annuler ou fermé la fenêtre
if (lblMessage != null) {
lblMessage.setText("Saisie annulée");
lblMessage.getStyleClass().setAll("message-warning");
}
// Effacer l'affichage précédent
lblAffichage.setText("");
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void setStagePrincipal(Stage stage) {
this.stagePrincipal = stage;
}
}
• La fenêtre de saisie est modale (
APPLICATION_MODAL)• L'utilisateur doit remplir les champs et cliquer sur OK ou Annuler
• Le contrôleur stocke les données saisies dans des variables privées
• Après
showAndWait(), on vérifie si l'utilisateur a validé• Si OK : on récupère les données et on les affiche dans
lblAffichage de la fenêtre parente• Un message de confirmation s'affiche dans
lblMessage avec la classe CSS message-success• Si Annuler : un message d'annulation s'affiche dans
lblMessage avec la classe CSS message-warning, et lblAffichage est effacé• Les méthodes
getPrenom(), getNom(), getAge() permettent de récupérer les données après la fermeture de la fenêtre• Tous les messages sont affichés dans la fenêtre principale via des Labels, pas dans la console
• Important : La taille de la fenêtre est définie dans le FXML avec
prefWidth="400.0" et prefHeight="250.0" sur le BorderPane. Ne pas utiliser setWidth() et setHeight() dans le code Java.• Important : Utilisez
getStyleClass().setAll() pour définir les classes CSS au lieu de setStyle(). Définissez les styles dans votre fichier CSS externe (ex: .message-success { -fx-text-fill: green; } et .message-warning { -fx-text-fill: orange; })
💡 Quand utiliser une fenêtre modale ?
Utilisez une fenêtre modale quand :
- Vous avez besoin d'une confirmation avant une action importante
- Vous voulez que l'utilisateur saisisse des informations avant de continuer
- Vous voulez afficher un message critique qui nécessite l'attention de l'utilisateur
- Vous avez besoin d'une interface personnalisée (plus complexe qu'un simple
Alert)
N'utilisez pas une fenêtre modale pour :
- Des fenêtres d'aide ou de documentation (l'utilisateur doit pouvoir consulter l'aide tout en utilisant l'application)
- Des fenêtres de paramètres simples (sauf si vous voulez forcer la configuration avant de continuer)
- Des fenêtres d'information non-critiques
- ✅ Utilisez
APPLICATION_MODALpour les confirmations critiques - ✅ Utilisez
WINDOW_MODALpour les fenêtres liées à une fenêtre spécifique - ✅ Utilisez
NONE(oushow()) pour les fenêtres d'aide, de paramètres non-critiques - ✅ Utilisez
showAndWait()avec les fenêtres modales pour récupérer le résultat - ✅ Donnez toujours un moyen à l'utilisateur de fermer la fenêtre (bouton Annuler, etc.)
💡 Points clés à retenir
- Modality.NONE : Fenêtre non-modale (par défaut pour les Stages)
- Modality.WINDOW_MODAL : Bloque uniquement la fenêtre parente
- Modality.APPLICATION_MODAL : Bloque toute l'application (comme les dialogues)
- show() : Affiche sans bloquer (pour les fenêtres non-modales)
- showAndWait() : Affiche et attend (pour les fenêtres modales)
- initModality() : Définit le niveau de modalité
- initOwner() : Définit la fenêtre parente (nécessaire pour WINDOW_MODAL)
7.2.4 – FileChooser (ouvrir/enregistrer un fichier)
Le FileChooser est un dialogue système qui permet à l'utilisateur de sélectionner un fichier sur son ordinateur. Il est très utile pour ouvrir des fichiers existants ou enregistrer de nouveaux fichiers. JavaFX fournit la classe FileChooser qui affiche automatiquement le dialogue natif du système d'exploitation.
🎯 Quand utiliser FileChooser ?
Utilisez FileChooser quand :
- Vous voulez permettre à l'utilisateur d'ouvrir un fichier (image, document, etc.)
- Vous voulez permettre à l'utilisateur d'enregistrer un fichier
- Vous avez besoin d'un dialogue natif du système (Windows, Mac, Linux)
- Vous voulez filtrer les types de fichiers (par exemple, seulement les images)
FileChooser utilise le dialogue natif du système d'exploitation. Sur Windows, vous verrez l'explorateur Windows, sur Mac le Finder, et sur Linux le gestionnaire de fichiers par défaut.
📝 Exemple 1 : Ouvrir un fichier
Pour permettre à l'utilisateur d'ouvrir un fichier, utilisez showOpenDialog() :
package application;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import java.io.File;
public class MainController {
private Stage stagePrincipal;
@FXML
private Label lblFichier; // Label pour afficher le fichier sélectionné
@FXML
private void ouvrirFichier() {
FileChooser fileChooser = new FileChooser();
fileChooser.setTitle("Ouvrir un fichier");
// Optionnel : définir le répertoire initial
fileChooser.setInitialDirectory(new File(System.getProperty("user.home")));
// Afficher le dialogue et récupérer le fichier sélectionné
File fichierSelectionne = fileChooser.showOpenDialog(stagePrincipal);
// Vérifier si l'utilisateur a sélectionné un fichier
if (fichierSelectionne != null) {
String chemin = fichierSelectionne.getAbsolutePath();
lblFichier.setText("Fichier sélectionné : " + chemin);
System.out.println("Fichier ouvert : " + chemin);
} else {
lblFichier.setText("Aucun fichier sélectionné");
System.out.println("L'utilisateur a annulé");
}
}
public void setStagePrincipal(Stage stage) {
this.stagePrincipal = stage;
}
}
•
new FileChooser() : Crée un nouveau FileChooser•
setTitle() : Définit le titre du dialogue•
setInitialDirectory() : Définit le répertoire initial (optionnel)•
showOpenDialog(stagePrincipal) : Affiche le dialogue d'ouverture et retourne le fichier sélectionné• Le résultat est un
File ou null si l'utilisateur a annulé•
getAbsolutePath() : Récupère le chemin complet du fichier
📝 Exemple 2 : Enregistrer un fichier
Pour permettre à l'utilisateur d'enregistrer un fichier, utilisez showSaveDialog() :
@FXML
private void enregistrerFichier() {
FileChooser fileChooser = new FileChooser();
fileChooser.setTitle("Enregistrer un fichier");
// Optionnel : définir le nom de fichier par défaut
fileChooser.setInitialFileName("mon_fichier.txt");
// Optionnel : définir le répertoire initial
fileChooser.setInitialDirectory(new File(System.getProperty("user.home")));
// Afficher le dialogue d'enregistrement
File fichierSelectionne = fileChooser.showSaveDialog(stagePrincipal);
if (fichierSelectionne != null) {
String chemin = fichierSelectionne.getAbsolutePath();
lblFichier.setText("Fichier à enregistrer : " + chemin);
System.out.println("Fichier à enregistrer : " + chemin);
// Ici, vous pouvez écrire dans le fichier
// Par exemple : Files.write(fichierSelectionne.toPath(), contenu.getBytes());
} else {
lblFichier.setText("Enregistrement annulé");
System.out.println("L'utilisateur a annulé l'enregistrement");
}
}
•
showSaveDialog(stagePrincipal) : Affiche le dialogue d'enregistrement•
setInitialFileName() : Définit un nom de fichier par défaut (optionnel)• Le dialogue permet à l'utilisateur de choisir l'emplacement et le nom du fichier
• Si l'utilisateur confirme, vous pouvez ensuite écrire dans le fichier
📝 Exemple 3 : Filtrer les types de fichiers
Vous pouvez limiter les types de fichiers que l'utilisateur peut sélectionner en ajoutant des filtres d'extension :
@FXML
private void ouvrirImage() {
FileChooser fileChooser = new FileChooser();
fileChooser.setTitle("Ouvrir une image");
// Créer un filtre pour les images
FileChooser.ExtensionFilter filtreImage =
new FileChooser.ExtensionFilter("Images", "*.png", "*.jpg", "*.jpeg", "*.gif");
// Créer un filtre pour tous les fichiers
FileChooser.ExtensionFilter filtreTous =
new FileChooser.ExtensionFilter("Tous les fichiers", "*.*");
// Ajouter les filtres au FileChooser
fileChooser.getExtensionFilters().addAll(filtreImage, filtreTous);
// Définir le filtre par défaut (le premier sera sélectionné)
fileChooser.setSelectedExtensionFilter(filtreImage);
File fichierSelectionne = fileChooser.showOpenDialog(stagePrincipal);
if (fichierSelectionne != null) {
lblFichier.setText("Image sélectionnée : " + fichierSelectionne.getName());
System.out.println("Image : " + fichierSelectionne.getAbsolutePath());
}
}
•
ExtensionFilter : Crée un filtre pour limiter les types de fichiers• Le premier paramètre est la description ("Images")
• Les paramètres suivants sont les extensions acceptées ("*.png", "*.jpg", etc.)
•
getExtensionFilters().addAll() : Ajoute plusieurs filtres•
setSelectedExtensionFilter() : Définit le filtre sélectionné par défaut• L'utilisateur pourra choisir parmi les filtres dans le menu déroulant du dialogue
📝 Exemple 4 : Filtrer plusieurs types de fichiers
Vous pouvez créer plusieurs filtres pour différents types de fichiers :
@FXML
private void ouvrirDocument() {
FileChooser fileChooser = new FileChooser();
fileChooser.setTitle("Ouvrir un document");
// Filtre pour les fichiers texte
FileChooser.ExtensionFilter filtreTexte =
new FileChooser.ExtensionFilter("Fichiers texte", "*.txt", "*.md");
// Filtre pour les fichiers PDF
FileChooser.ExtensionFilter filtrePDF =
new FileChooser.ExtensionFilter("Fichiers PDF", "*.pdf");
// Filtre pour les fichiers Word
FileChooser.ExtensionFilter filtreWord =
new FileChooser.ExtensionFilter("Fichiers Word", "*.doc", "*.docx");
// Filtre pour tous les fichiers
FileChooser.ExtensionFilter filtreTous =
new FileChooser.ExtensionFilter("Tous les fichiers", "*.*");
// Ajouter tous les filtres
fileChooser.getExtensionFilters().addAll(
filtreTexte,
filtrePDF,
filtreWord,
filtreTous
);
// Sélectionner le filtre texte par défaut
fileChooser.setSelectedExtensionFilter(filtreTexte);
File fichierSelectionne = fileChooser.showOpenDialog(stagePrincipal);
if (fichierSelectionne != null) {
String nomFichier = fichierSelectionne.getName();
String extension = nomFichier.substring(nomFichier.lastIndexOf(".") + 1);
lblFichier.setText("Document sélectionné : " + nomFichier + " (." + extension + ")");
}
}
📝 Exemple complet : Ouvrir et afficher une image
Créons un exemple complet : un bouton qui ouvre une image et l'affiche dans l'interface :
main.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.VBox?>
<BorderPane xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="application.MainController"
prefWidth="600.0"
prefHeight="500.0">
<center>
<VBox spacing="20.0" alignment="CENTER" style="-fx-padding: 30px;">
<children>
<Label text="Exemple FileChooser"
style="-fx-font-size: 20px; -fx-font-weight: bold;" />
<Button fx:id="btnOuvrirImage"
onAction="#ouvrirEtAfficherImage"
text="Ouvrir une image"
style="-fx-min-width: 200px; -fx-min-height: 40px;" />
<Label fx:id="lblChemin"
text=""
style="-fx-font-size: 12px; -fx-text-fill: gray;"
wrapText="true" />
<ImageView fx:id="imageView"
fitWidth="400.0"
fitHeight="300.0"
preserveRatio="true"
style="-fx-border-color: #ccc; -fx-border-width: 1px;" />
</children>
</VBox>
</center>
</BorderPane>
MainController.java
package application;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import java.io.File;
public class MainController {
@FXML
private ImageView imageView; // ImageView pour afficher l'image
@FXML
private Label lblChemin; // Label pour afficher le chemin du fichier
private Stage stagePrincipal;
@FXML
private void ouvrirEtAfficherImage() {
FileChooser fileChooser = new FileChooser();
fileChooser.setTitle("Sélectionner une image");
// Filtrer pour n'afficher que les images
FileChooser.ExtensionFilter filtreImage =
new FileChooser.ExtensionFilter("Images", "*.png", "*.jpg", "*.jpeg", "*.gif", "*.bmp");
FileChooser.ExtensionFilter filtreTous =
new FileChooser.ExtensionFilter("Tous les fichiers", "*.*");
fileChooser.getExtensionFilters().addAll(filtreImage, filtreTous);
fileChooser.setSelectedExtensionFilter(filtreImage);
// Ouvrir le dialogue
File fichierSelectionne = fileChooser.showOpenDialog(stagePrincipal);
if (fichierSelectionne != null) {
try {
// Afficher le chemin du fichier
String chemin = fichierSelectionne.getAbsolutePath();
lblChemin.setText("Fichier : " + chemin);
// Charger et afficher l'image
Image image = new Image("file:" + chemin);
imageView.setImage(image);
System.out.println("Image chargée : " + chemin);
} catch (Exception e) {
lblChemin.setText("Erreur lors du chargement de l'image : " + e.getMessage());
System.err.println("Erreur : " + e.getMessage());
e.printStackTrace();
}
} else {
lblChemin.setText("Aucun fichier sélectionné");
}
}
public void setStagePrincipal(Stage stage) {
this.stagePrincipal = stage;
}
}
• Le FileChooser filtre pour n'afficher que les images
• Quand l'utilisateur sélectionne un fichier, on récupère son chemin
•
new Image("file:" + chemin) : Charge l'image depuis le fichier•
imageView.setImage(image) : Affiche l'image dans l'ImageView• Le préfixe
"file:" est nécessaire pour charger un fichier local•
preserveRatio="true" dans le FXML maintient les proportions de l'image
📝 Exemple 5 : Enregistrer du texte dans un fichier
Créons un exemple pour enregistrer du texte saisi par l'utilisateur dans un fichier :
package application;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
public class MainController {
@FXML
private TextArea textArea; // TextArea pour saisir du texte
@FXML
private Label lblMessage; // Label pour afficher les messages
private Stage stagePrincipal;
@FXML
private void enregistrerTexte() {
// Récupérer le texte saisi
String texte = textArea.getText();
if (texte.trim().isEmpty()) {
lblMessage.setText("Veuillez saisir du texte avant d'enregistrer");
return;
}
FileChooser fileChooser = new FileChooser();
fileChooser.setTitle("Enregistrer le texte");
fileChooser.setInitialFileName("mon_texte.txt");
// Filtrer pour les fichiers texte
FileChooser.ExtensionFilter filtreTexte =
new FileChooser.ExtensionFilter("Fichiers texte", "*.txt");
FileChooser.ExtensionFilter filtreTous =
new FileChooser.ExtensionFilter("Tous les fichiers", "*.*");
fileChooser.getExtensionFilters().addAll(filtreTexte, filtreTous);
fileChooser.setSelectedExtensionFilter(filtreTexte);
// Ouvrir le dialogue d'enregistrement
File fichierSelectionne = fileChooser.showSaveDialog(stagePrincipal);
if (fichierSelectionne != null) {
try {
// Écrire le texte dans le fichier
FileWriter writer = new FileWriter(fichierSelectionne);
writer.write(texte);
writer.close();
lblMessage.setText("Fichier enregistré : " + fichierSelectionne.getAbsolutePath());
System.out.println("Fichier enregistré : " + fichierSelectionne.getAbsolutePath());
} catch (IOException e) {
lblMessage.setText("Erreur lors de l'enregistrement : " + e.getMessage());
System.err.println("Erreur : " + e.getMessage());
e.printStackTrace();
}
} else {
lblMessage.setText("Enregistrement annulé");
}
}
public void setStagePrincipal(Stage stage) {
this.stagePrincipal = stage;
}
}
• On récupère le texte depuis le TextArea
• On vérifie que le texte n'est pas vide
•
showSaveDialog() : Ouvre le dialogue d'enregistrement•
FileWriter : Écrit le texte dans le fichier sélectionné• On gère les exceptions avec un try-catch
• Un message de confirmation ou d'erreur est affiché dans le Label
- ✅ Toujours vérifier si le fichier sélectionné n'est pas
null(l'utilisateur peut annuler) - ✅ Utiliser
try-catchpour gérer les erreurs lors de la lecture/écriture de fichiers - ✅ Le préfixe
"file:"est nécessaire pour charger des fichiers locaux avecImage - ✅ Utilisez
FileWriterouFiles.write()pour écrire dans un fichier - ✅ Les filtres d'extension améliorent l'expérience utilisateur
💡 Points clés à retenir
- FileChooser : Dialogue natif pour sélectionner des fichiers
- showOpenDialog() : Pour ouvrir un fichier existant
- showSaveDialog() : Pour enregistrer un nouveau fichier
- ExtensionFilter : Pour filtrer les types de fichiers
- setInitialDirectory() : Pour définir le répertoire initial
- setInitialFileName() : Pour définir un nom de fichier par défaut (enregistrement)
- Résultat null : Si l'utilisateur annule, le résultat est
null - File.getAbsolutePath() : Pour obtenir le chemin complet du fichier
7.2.5 – DirectoryChooser (sélection de dossier)
Le DirectoryChooser est un dialogue système qui permet à l'utilisateur de sélectionner un dossier (répertoire) sur son ordinateur. Contrairement au FileChooser qui sélectionne des fichiers, le DirectoryChooser permet de choisir un dossier entier. C'est très utile pour des opérations comme sauvegarder plusieurs fichiers dans un dossier, parcourir le contenu d'un dossier, ou configurer un répertoire de travail.
🎯 Quand utiliser DirectoryChooser ?
Utilisez DirectoryChooser quand :
- Vous voulez permettre à l'utilisateur de choisir un dossier pour y enregistrer des fichiers
- Vous avez besoin de parcourir le contenu d'un dossier
- Vous voulez configurer un répertoire de travail ou de sauvegarde
- Vous devez sélectionner un emplacement pour des exports ou des sauvegardes multiples
FileChooser sélectionne un fichier spécifique, tandis que le DirectoryChooser sélectionne un dossier entier. Utilisez DirectoryChooser quand vous travaillez avec des dossiers plutôt qu'avec des fichiers individuels.
📝 Exemple 1 : Sélectionner un dossier simple
Pour permettre à l'utilisateur de sélectionner un dossier, utilisez showDialog() :
package application;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.stage.DirectoryChooser;
import javafx.stage.Stage;
import java.io.File;
public class MainController {
private Stage stagePrincipal;
@FXML
private Label lblDossier; // Label pour afficher le dossier sélectionné
@FXML
private void selectionnerDossier() {
DirectoryChooser directoryChooser = new DirectoryChooser();
directoryChooser.setTitle("Sélectionner un dossier");
// Optionnel : définir le répertoire initial
directoryChooser.setInitialDirectory(new File(System.getProperty("user.home")));
// Afficher le dialogue et récupérer le dossier sélectionné
File dossierSelectionne = directoryChooser.showDialog(stagePrincipal);
// Vérifier si l'utilisateur a sélectionné un dossier
if (dossierSelectionne != null) {
String chemin = dossierSelectionne.getAbsolutePath();
lblDossier.setText("Dossier sélectionné : " + chemin);
System.out.println("Dossier sélectionné : " + chemin);
} else {
lblDossier.setText("Aucun dossier sélectionné");
System.out.println("L'utilisateur a annulé");
}
}
public void setStagePrincipal(Stage stage) {
this.stagePrincipal = stage;
}
}
•
new DirectoryChooser() : Crée un nouveau DirectoryChooser•
setTitle() : Définit le titre du dialogue•
setInitialDirectory() : Définit le répertoire initial (optionnel)•
showDialog(stagePrincipal) : Affiche le dialogue et retourne le dossier sélectionné• Le résultat est un
File représentant le dossier ou null si l'utilisateur a annulé•
getAbsolutePath() : Récupère le chemin complet du dossier
📝 Exemple 2 : Lister les fichiers d'un dossier
Une fois qu'un dossier est sélectionné, vous pouvez lister son contenu :
@FXML
private Label lblContenu; // Label pour afficher le contenu du dossier
@FXML
private void listerContenuDossier() {
DirectoryChooser directoryChooser = new DirectoryChooser();
directoryChooser.setTitle("Sélectionner un dossier à lister");
directoryChooser.setInitialDirectory(new File(System.getProperty("user.home")));
File dossierSelectionne = directoryChooser.showDialog(stagePrincipal);
if (dossierSelectionne != null) {
try {
StringBuilder contenu = new StringBuilder();
contenu.append("Contenu du dossier : ").append(dossierSelectionne.getName()).append("\n\n");
// Lister les fichiers et dossiers
File[] fichiers = dossierSelectionne.listFiles();
if (fichiers != null) {
for (File fichier : fichiers) {
if (fichier.isDirectory()) {
contenu.append("[DOSSIER] ").append(fichier.getName()).append("\n");
} else {
contenu.append("[FICHIER] ").append(fichier.getName())
.append(" (").append(fichier.length()).append(" octets)\n");
}
}
}
lblContenu.setText(contenu.toString());
System.out.println("Contenu listé pour : " + dossierSelectionne.getAbsolutePath());
} catch (Exception e) {
lblContenu.setText("Erreur lors de la lecture du dossier : " + e.getMessage());
System.err.println("Erreur : " + e.getMessage());
e.printStackTrace();
}
}
}
•
listFiles() : Retourne un tableau de tous les fichiers et dossiers dans le répertoire•
isDirectory() : Vérifie si c'est un dossier•
fichier.length() : Retourne la taille du fichier en octets•
fichier.getName() : Retourne le nom du fichier ou dossier
📝 Exemple 3 : Enregistrer plusieurs fichiers dans un dossier
Vous pouvez utiliser DirectoryChooser pour sélectionner un dossier où enregistrer plusieurs fichiers :
@FXML
private void enregistrerFichiersDansDossier() {
DirectoryChooser directoryChooser = new DirectoryChooser();
directoryChooser.setTitle("Sélectionner un dossier pour enregistrer les fichiers");
directoryChooser.setInitialDirectory(new File(System.getProperty("user.home")));
File dossierSelectionne = directoryChooser.showDialog(stagePrincipal);
if (dossierSelectionne != null) {
try {
// Créer plusieurs fichiers dans le dossier sélectionné
File fichier1 = new File(dossierSelectionne, "fichier1.txt");
File fichier2 = new File(dossierSelectionne, "fichier2.txt");
File fichier3 = new File(dossierSelectionne, "fichier3.txt");
// Écrire dans les fichiers
java.nio.file.Files.write(fichier1.toPath(), "Contenu du fichier 1".getBytes());
java.nio.file.Files.write(fichier2.toPath(), "Contenu du fichier 2".getBytes());
java.nio.file.Files.write(fichier3.toPath(), "Contenu du fichier 3".getBytes());
lblMessage.setText("3 fichiers créés dans : " + dossierSelectionne.getAbsolutePath());
lblMessage.getStyleClass().setAll("message-success");
System.out.println("Fichiers créés dans : " + dossierSelectionne.getAbsolutePath());
} catch (Exception e) {
lblMessage.setText("Erreur lors de l'enregistrement : " + e.getMessage());
lblMessage.getStyleClass().setAll("message-error");
System.err.println("Erreur : " + e.getMessage());
e.printStackTrace();
}
}
}
•
new File(dossier, "nomFichier") : Crée un fichier dans le dossier spécifié•
Files.write() : Écrit le contenu dans le fichier• Vous pouvez créer autant de fichiers que nécessaire dans le dossier sélectionné
📝 Exemple 4 : Filtrer les fichiers d'un dossier
Vous pouvez filtrer les fichiers d'un dossier selon leur extension :
@FXML
private void listerImagesDansDossier() {
DirectoryChooser directoryChooser = new DirectoryChooser();
directoryChooser.setTitle("Sélectionner un dossier contenant des images");
directoryChooser.setInitialDirectory(new File(System.getProperty("user.home")));
File dossierSelectionne = directoryChooser.showDialog(stagePrincipal);
if (dossierSelectionne != null) {
try {
StringBuilder images = new StringBuilder();
images.append("Images trouvées dans : ").append(dossierSelectionne.getName()).append("\n\n");
// Extensions d'images acceptées
String[] extensions = {".png", ".jpg", ".jpeg", ".gif", ".bmp"};
File[] fichiers = dossierSelectionne.listFiles();
if (fichiers != null) {
int compteur = 0;
for (File fichier : fichiers) {
if (fichier.isFile()) {
String nom = fichier.getName().toLowerCase();
for (String ext : extensions) {
if (nom.endsWith(ext)) {
images.append("• ").append(fichier.getName())
.append(" (").append(fichier.length()).append(" octets)\n");
compteur++;
break;
}
}
}
}
images.append("\nTotal : ").append(compteur).append(" image(s)");
}
lblContenu.setText(images.toString());
System.out.println("Images listées dans : " + dossierSelectionne.getAbsolutePath());
} catch (Exception e) {
lblContenu.setText("Erreur : " + e.getMessage());
System.err.println("Erreur : " + e.getMessage());
e.printStackTrace();
}
}
}
• On parcourt tous les fichiers du dossier
• On vérifie si le nom du fichier se termine par une extension d'image
• On affiche uniquement les fichiers qui correspondent aux critères
• On compte le nombre total d'images trouvées
📝 Exemple complet : Gestionnaire de dossiers
Créons un exemple complet qui permet de sélectionner un dossier, lister son contenu, filtrer les fichiers par type, et créer de nouveaux fichiers dans le dossier :
main.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextArea?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<BorderPane xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="application.MainController"
stylesheets="style="".css"
prefWidth="700.0"
prefHeight="600.0">
<top>
<VBox spacing="10.0" style="-fx-padding: 20px;">
<children>
<Label text="Gestionnaire de dossiers - DirectoryChooser"
style="-fx-font-size: 20px; -fx-font-weight: bold;" />
<Label fx:id="lblDossier"
text="Aucun dossier sélectionné"
style="-fx-font-size: 12px; -fx-text-fill: gray;"
wrapText="true" />
</children>
</VBox>
</top>
<center>
<VBox spacing="15.0" style="-fx-padding: 20px;">
<children>
<HBox spacing="10.0">
<children>
<Button fx:id="btnSelectionnerDossier"
onAction="#selectionnerDossier"
text="Sélectionner un dossier"
style="-fx-min-width: 180px;" />
<Button fx:id="btnListerContenu"
onAction="#listerContenu"
text="Lister le contenu"
style="-fx-min-width: 150px;" />
<Button fx:id="btnListerImages"
onAction="#listerImages"
text="Lister les images"
style="-fx-min-width: 150px;" />
<Button fx:id="btnCreerFichiers"
onAction="#creerFichiers"
text="Créer des fichiers"
style="-fx-min-width: 150px;" />
</children>
</HBox>
<TextArea fx:id="textArea"
promptText="Le contenu du dossier s'affichera ici..."
wrapText="true"
VBox.vgrow="ALWAYS" />
<Label fx:id="lblMessage"
text=""
style="-fx-font-size: 12px;"
wrapText="true" />
</children>
</VBox>
</center>
</BorderPane>
MainController.java
package application;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.stage.DirectoryChooser;
import javafx.stage.Stage;
import java.io.File;
public class MainController {
@FXML
private Label lblDossier; // Label pour afficher le dossier sélectionné
@FXML
private TextArea textArea; // TextArea pour afficher le contenu
@FXML
private Label lblMessage; // Label pour afficher les messages
private Stage stagePrincipal;
private File dossierCourant; // Dossier actuellement sélectionné
/**
* Sélectionne un dossier avec DirectoryChooser
*/
@FXML
private void selectionnerDossier() {
DirectoryChooser directoryChooser = new DirectoryChooser();
directoryChooser.setTitle("Sélectionner un dossier");
directoryChooser.setInitialDirectory(new File(System.getProperty("user.home")));
File dossierSelectionne = directoryChooser.showDialog(stagePrincipal);
if (dossierSelectionne != null) {
dossierCourant = dossierSelectionne;
String chemin = dossierCourant.getAbsolutePath();
lblDossier.setText("Dossier sélectionné : " + chemin);
lblMessage.setText("Dossier sélectionné avec succès !");
lblMessage.getStyleClass().setAll("message-success");
textArea.clear();
System.out.println("Dossier sélectionné : " + chemin);
} else {
lblMessage.setText("Aucun dossier sélectionné");
lblMessage.getStyleClass().setAll("message-warning");
}
}
/**
* Liste tout le contenu du dossier sélectionné
*/
@FXML
private void listerContenu() {
if (dossierCourant == null) {
lblMessage.setText("Veuillez d'abord sélectionner un dossier");
lblMessage.getStyleClass().setAll("message-warning");
return;
}
try {
StringBuilder contenu = new StringBuilder();
contenu.append("=== CONTENU DU DOSSIER ===\n");
contenu.append("Chemin : ").append(dossierCourant.getAbsolutePath()).append("\n\n");
File[] fichiers = dossierCourant.listFiles();
if (fichiers != null && fichiers.length > 0) {
contenu.append("DOSSIERS :\n");
for (File fichier : fichiers) {
if (fichier.isDirectory()) {
contenu.append(" 📁 ").append(fichier.getName()).append("\n");
}
}
contenu.append("\nFICHIERS :\n");
for (File fichier : fichiers) {
if (fichier.isFile()) {
long taille = fichier.length();
String tailleStr = taille < 1024 ? taille + " o" :
taille < 1048576 ? (taille / 1024) + " Ko" :
(taille / 1048576) + " Mo";
contenu.append(" 📄 ").append(fichier.getName())
.append(" (").append(tailleStr).append(")\n");
}
}
contenu.append("\nTotal : ").append(fichiers.length).append(" élément(s)");
} else {
contenu.append("Le dossier est vide");
}
textArea.setText(contenu.toString());
lblMessage.setText("Contenu listé avec succès");
lblMessage.getStyleClass().setAll("message-success");
} catch (Exception e) {
lblMessage.setText("Erreur lors de la lecture : " + e.getMessage());
lblMessage.getStyleClass().setAll("message-error");
System.err.println("Erreur : " + e.getMessage());
e.printStackTrace();
}
}
/**
* Liste uniquement les images du dossier sélectionné
*/
@FXML
private void listerImages() {
if (dossierCourant == null) {
lblMessage.setText("Veuillez d'abord sélectionner un dossier");
lblMessage.getStyleClass().setAll("message-warning");
return;
}
try {
StringBuilder images = new StringBuilder();
images.append("=== IMAGES TROUVÉES ===\n");
images.append("Dossier : ").append(dossierCourant.getName()).append("\n\n");
String[] extensions = {".png", ".jpg", ".jpeg", ".gif", ".bmp", ".webp"};
File[] fichiers = dossierCourant.listFiles();
if (fichiers != null) {
int compteur = 0;
for (File fichier : fichiers) {
if (fichier.isFile()) {
String nom = fichier.getName().toLowerCase();
for (String ext : extensions) {
if (nom.endsWith(ext)) {
long taille = fichier.length();
String tailleStr = taille < 1024 ? taille + " o" :
taille < 1048576 ? (taille / 1024) + " Ko" :
(taille / 1048576) + " Mo";
images.append("🖼️ ").append(fichier.getName())
.append(" (").append(tailleStr).append(")\n");
compteur++;
break;
}
}
}
}
images.append("\nTotal : ").append(compteur).append(" image(s)");
}
textArea.setText(images.toString());
lblMessage.setText("Images listées avec succès");
lblMessage.getStyleClass().setAll("message-success");
} catch (Exception e) {
lblMessage.setText("Erreur : " + e.getMessage());
lblMessage.getStyleClass().setAll("message-error");
System.err.println("Erreur : " + e.getMessage());
e.printStackTrace();
}
}
/**
* Crée des fichiers d'exemple dans le dossier sélectionné
*/
@FXML
private void creerFichiers() {
if (dossierCourant == null) {
lblMessage.setText("Veuillez d'abord sélectionner un dossier");
lblMessage.getStyleClass().setAll("message-warning");
return;
}
try {
// Créer plusieurs fichiers d'exemple
File fichier1 = new File(dossierCourant, "exemple_texte.txt");
File fichier2 = new File(dossierCourant, "exemple_data.txt");
File fichier3 = new File(dossierCourant, "exemple_info.txt");
java.nio.file.Files.write(fichier1.toPath(),
"Ceci est un fichier d'exemple créé par l'application JavaFX.".getBytes());
java.nio.file.Files.write(fichier2.toPath(),
"Données d'exemple : ligne 1\nDonnées d'exemple : ligne 2".getBytes());
java.nio.file.Files.write(fichier3.toPath(),
"Informations :\n- Fichier créé automatiquement\n- Date : " +
new java.util.Date().toString().getBytes());
lblMessage.setText("3 fichiers créés avec succès dans le dossier sélectionné !");
lblMessage.getStyleClass().setAll("message-success");
// Actualiser l'affichage
listerContenu();
System.out.println("Fichiers créés dans : " + dossierCourant.getAbsolutePath());
} catch (Exception e) {
lblMessage.setText("Erreur lors de la création : " + e.getMessage());
lblMessage.getStyleClass().setAll("message-error");
System.err.println("Erreur : " + e.getMessage());
e.printStackTrace();
}
}
public void setStagePrincipal(Stage stage) {
this.stagePrincipal = stage;
}
}
•
dossierCourant : Variable pour stocker le dossier sélectionné•
selectionnerDossier() : Ouvre le DirectoryChooser et stocke le résultat•
listerContenu() : Liste tous les fichiers et dossiers avec leurs tailles•
listerImages() : Filtre et affiche uniquement les fichiers images•
creerFichiers() : Crée des fichiers d'exemple dans le dossier sélectionné• Les tailles sont formatées en octets, Ko ou Mo pour une meilleure lisibilité
• Après création de fichiers, le contenu est automatiquement rafraîchi
- ✅ Toujours vérifier si le dossier sélectionné n'est pas
null(l'utilisateur peut annuler) - ✅ Utiliser
try-catchpour gérer les erreurs lors de la lecture/écriture - ✅ Vérifier que
listFiles()ne retourne pasnull(dossier vide ou inaccessible) - ✅ Utiliser
isDirectory()etisFile()pour distinguer les dossiers des fichiers - ✅ Le DirectoryChooser utilise le dialogue natif du système d'exploitation
💡 Points clés à retenir
- DirectoryChooser : Dialogue natif pour sélectionner un dossier
- showDialog() : Affiche le dialogue et retourne le dossier sélectionné
- setInitialDirectory() : Pour définir le répertoire initial
- Résultat null : Si l'utilisateur annule, le résultat est
null - File.listFiles() : Pour lister le contenu d'un dossier
- isDirectory() : Pour vérifier si c'est un dossier
- isFile() : Pour vérifier si c'est un fichier
- new File(dossier, "nomFichier") : Pour créer un fichier dans un dossier