CHAPITRE 6.3

TableView

Maîtriser l'affichage de tableaux de données dans JavaFX
Dans cette section, vous allez apprendre à utiliser le composant TableView de JavaFX pour afficher des données sous forme de tableau. Vous découvrirez comment créer des colonnes, lier des données, utiliser PropertyValueFactory pour lier automatiquement les propriétés, et même permettre l'édition des cellules. Le TableView est le composant idéal pour afficher des données structurées en lignes et colonnes.

6.3TableView

6.3.1 – Structure : colonnes, lignes, modèle

Le TableView est un composant JavaFX qui affiche des données sous forme de tableau avec des colonnes et des lignes. C'est l'équivalent d'un tableau HTML ou d'une grille de données. Chaque ligne représente un objet de votre modèle, et chaque colonne affiche une propriété de cet objet.

🎯 Structure d'un TableView

Un TableView est composé de plusieurs éléments :

  • TableView : Le conteneur principal qui affiche le tableau
  • TableColumn : Chaque colonne du tableau (une par propriété)
  • Lignes : Chaque ligne représente un objet de votre modèle
  • Modèle : La classe qui représente vos données (ex: Personne, Produit, etc.)
  • ObservableList : La liste d'objets à afficher dans le tableau
Important - Configuration du module :
Si vous utilisez des propriétés observables (StringProperty, IntegerProperty, etc.) dans votre modèle, vous devez ajouter javafx.base dans le fichier module-info.java :

opens application to javafx.graphics, javafx.fxml, javafx.base;

Sinon, vous obtiendrez l'erreur : "module javafx.base cannot access class application.Personne"

📝 Créer un TableView simple

Pour créer un TableView, vous devez :

  1. Créer une classe modèle (ex: Personne)
  2. Créer des TableColumn pour chaque propriété
  3. Lier les colonnes aux propriétés
  4. Créer une ObservableList avec vos données
  5. Lier la liste au TableView
Exemple TableView - Structure : colonnes, lignes, modèle
Exemple d'application TableView avec structure de base : colonnes, lignes et modèle

Exemple 1 : Classe Personne

package application;

public class Personne {
    private String nom;
    private String prenom;
    private int age;
    private String email;
    
    public Personne(String nom, String prenom, int age, String email) {
        this.nom = nom;
        this.prenom = prenom;
        this.age = age;
        this.email = email;
    }
    
    // Getters
    public String getNom() { return nom; }
    public String getPrenom() { return prenom; }
    public int getAge() { return age; }
    public String getEmail() { return email; }
    
    // Setters
    public void setNom(String nom) { this.nom = nom; }
    public void setPrenom(String prenom) { this.prenom = prenom; }
    public void setAge(int age) { this.age = age; }
    public void setEmail(String email) { this.email = email; }
}

Exemple 2 : TableView avec colonnes

package application;

import javafx.fxml.FXML;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;

public class MainController {
    
    @FXML
    private TableView<Personne> tableViewPersonnes;
    
    @FXML
    private TableColumn<Personne, String> colNom;
    
    @FXML
    private TableColumn<Personne, String> colPrenom;
    
    @FXML
    private TableColumn<Personne, Integer> colAge;
    
    @FXML
    private TableColumn<Personne, String> colEmail;
    
    private ObservableList<Personne> personnes;
    
    @FXML
    private void initialize() {
        // Créer les données
        personnes = FXCollections.observableArrayList(
            new Personne("Dupont", "Jean", 25, "jean@exemple.com"),
            new Personne("Martin", "Marie", 30, "marie@exemple.com"),
            new Personne("Bernard", "Pierre", 28, "pierre@exemple.com")
        );
        
        // Lier les colonnes aux propriétés (voir section 6.3.2 pour PropertyValueFactory)
        // Pour l'instant, on définit juste les colonnes
        colNom.setCellValueFactory(cellData -> new SimpleStringProperty(cellData.getValue().getNom()));
        colPrenom.setCellValueFactory(cellData -> new SimpleStringProperty(cellData.getValue().getPrenom()));
        colAge.setCellValueFactory(cellData -> new SimpleIntegerProperty(cellData.getValue().getAge()).asObject());
        colEmail.setCellValueFactory(cellData -> new SimpleStringProperty(cellData.getValue().getEmail()));
        
        // Définir les titres des colonnes
        colNom.setText("Nom");
        colPrenom.setText("Prénom");
        colAge.setText("Âge");
        colEmail.setText("Email");
        
        // Lier la liste au TableView
        tableViewPersonnes.setItems(personnes);
    }
}
Explication :
TableView<Personne> : TableView qui affiche des objets Personne
TableColumn<Personne, String> : Colonne pour une propriété String de Personne
setCellValueFactory() : Définit comment obtenir la valeur à afficher
setItems() : Lie l'ObservableList au TableView

📝 Structure FXML

Dans le fichier FXML, vous devez définir le TableView et ses colonnes :

<TableView fx:id="tableViewPersonnes">
    <columns>
        <TableColumn fx:id="colNom" text="Nom" />
        <TableColumn fx:id="colPrenom" text="Prénom" />
        <TableColumn fx:id="colAge" text="Âge" />
        <TableColumn fx:id="colEmail" text="Email" />
    </columns>
</TableView>

📝 Types de colonnes

Les colonnes peuvent afficher différents types de données :

  • String : TableColumn<Personne, String>
  • Integer : TableColumn<Personne, Integer>
  • Double : TableColumn<Personne, Double>
  • Boolean : TableColumn<Personne, Boolean>
  • Date : TableColumn<Personne, LocalDate>
Important :
• Pour les types primitifs (int, double, etc.), utilisez les wrappers (Integer, Double)
• Pour Integer, utilisez .asObject() pour convertir en IntegerProperty
• Chaque colonne doit être typée correctement selon la propriété qu'elle affiche

💡 Points clés à retenir

  • TableView : Composant pour afficher des données en tableau
  • TableColumn : Chaque colonne représente une propriété
  • setCellValueFactory() : Définit comment obtenir la valeur à afficher
  • setItems() : Lie l'ObservableList au TableView
  • Modèle : Créez une classe pour représenter vos données
  • ObservableList : Utilisez une liste observable pour la mise à jour automatique
Conseil : Commencez par créer votre modèle (classe Personne), puis créez les colonnes une par une. Testez chaque colonne avant d'en ajouter d'autres !

6.3.2 – PropertyValueFactory

Le PropertyValueFactory est une classe utilitaire qui simplifie la liaison des colonnes aux propriétés de votre modèle. Au lieu d'écrire manuellement le code pour extraire la valeur, vous pouvez utiliser PropertyValueFactory qui le fait automatiquement en utilisant le nom de la propriété.

🎯 Avantages de PropertyValueFactory

  • Simplicité : Moins de code à écrire
  • Automatique : La liaison se fait automatiquement par nom de propriété
  • Maintenabilité : Plus facile à maintenir
  • Standard : Méthode standard recommandée par JavaFX

📝 Utilisation de PropertyValueFactory

Au lieu d'écrire manuellement le code pour chaque colonne :

// Méthode manuelle (longue)
colNom.setCellValueFactory(cellData -> 
    new SimpleStringProperty(cellData.getValue().getNom())
);

// Avec PropertyValueFactory (simple)
colNom.setCellValueFactory(new PropertyValueFactory<>("nom"));
Exemple TableView - PropertyValueFactory
Exemple d'application TableView utilisant PropertyValueFactory pour simplifier la liaison des colonnes

Exemple complet : TableView avec PropertyValueFactory

package application;

import javafx.fxml.FXML;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;

public class MainController {
    
    @FXML
    private TableView<Personne> tableViewPersonnes;
    
    @FXML
    private TableColumn<Personne, String> colNom;
    
    @FXML
    private TableColumn<Personne, String> colPrenom;
    
    @FXML
    private TableColumn<Personne, Integer> colAge;
    
    @FXML
    private TableColumn<Personne, String> colEmail;
    
    private ObservableList<Personne> personnes;
    
    @FXML
    private void initialize() {
        // Créer les données
        personnes = FXCollections.observableArrayList(
            new Personne("Dupont", "Jean", 25, "jean@exemple.com"),
            new Personne("Martin", "Marie", 30, "marie@exemple.com"),
            new Personne("Bernard", "Pierre", 28, "pierre@exemple.com"),
            new Personne("Dubois", "Sophie", 32, "sophie@exemple.com")
        );
        
        // Utiliser PropertyValueFactory pour lier automatiquement
        colNom.setCellValueFactory(new PropertyValueFactory<>("nom"));
        colPrenom.setCellValueFactory(new PropertyValueFactory<>("prenom"));
        colAge.setCellValueFactory(new PropertyValueFactory<>("age"));
        colEmail.setCellValueFactory(new PropertyValueFactory<>("email"));
        
        // Définir les titres des colonnes
        colNom.setText("Nom");
        colPrenom.setText("Prénom");
        colAge.setText("Âge");
        colEmail.setText("Email");
        
        // Lier la liste au TableView
        tableViewPersonnes.setItems(personnes);
    }
}
Comment ça marche :
• PropertyValueFactory cherche une méthode getter correspondant au nom de la propriété
• Pour "nom", il cherche getNom()
• Pour "age", il cherche getAge()
• La méthode getter doit exister dans votre classe modèle

📝 Convention de nommage

PropertyValueFactory suit la convention JavaBean :

  • Propriété "nom" → cherche getNom() ou nomProperty()
  • Propriété "age" → cherche getAge() ou ageProperty()
  • Propriété "email" → cherche getEmail() ou emailProperty()
Important :
  • ✅ Le nom de la propriété doit correspondre au nom du getter (sans "get" et en minuscule)
  • ✅ Les getters doivent suivre la convention JavaBean (get + Nom en CamelCase)
  • ✅ Si vous utilisez des propriétés observables (StringProperty, etc.), PropertyValueFactory les utilisera automatiquement
  • ❌ Ne pas utiliser de noms avec des majuscules au début (ex: "Nom" au lieu de "nom")

📝 Comparaison : Manuelle vs PropertyValueFactory

Aspect Méthode manuelle PropertyValueFactory
Code Plus de code Moins de code
Simplicité Plus complexe Plus simple
Performance Légèrement plus rapide Légèrement plus lent (négligeable)
Maintenabilité Moins maintenable Plus maintenable
Recommandation Cas spéciaux uniquement ✅ Méthode standard

💡 Points clés à retenir

  • PropertyValueFactory : Simplifie la liaison des colonnes aux propriétés
  • Convention JavaBean : Utilise les getters standard (getNom, getAge, etc.)
  • Nom de propriété : Utilisez le nom en minuscule (ex: "nom", pas "Nom")
  • Getters requis : Les méthodes getter doivent exister dans votre classe
  • Méthode standard : C'est la méthode recommandée pour la plupart des cas
Conseil : Utilisez PropertyValueFactory dans la plupart des cas. Utilisez la méthode manuelle seulement si vous avez besoin de logique spéciale (transformation, calcul, etc.).

6.3.3 – TableView + modèle observable

Pour que le TableView se mette à jour automatiquement quand vous modifiez les propriétés des objets, vous devez utiliser des propriétés observables JavaFX (StringProperty, IntegerProperty, etc.) dans votre modèle. Cela permet une mise à jour en temps réel du tableau.

🎯 Pourquoi utiliser des propriétés observables ?

  • Mise à jour automatique : Le TableView se met à jour quand vous modifiez une propriété
  • Temps réel : Les changements sont visibles immédiatement
  • Pas de refresh() : Plus besoin d'appeler refresh() manuellement
  • Binding : Permet de créer des bindings avec d'autres composants

📝 Modèle avec propriétés observables

Personne.java avec propriétés observables

package application;

import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;

public class Personne {
    private StringProperty nom = new SimpleStringProperty();
    private StringProperty prenom = new SimpleStringProperty();
    private IntegerProperty age = new SimpleIntegerProperty();
    private StringProperty email = new SimpleStringProperty();
    
    public Personne(String nom, String prenom, int age, String email) {
        this.nom.set(nom);
        this.prenom.set(prenom);
        this.age.set(age);
        this.email.set(email);
    }
    
    // Getters pour les propriétés (nécessaires pour PropertyValueFactory)
    public StringProperty nomProperty() { return nom; }
    public StringProperty prenomProperty() { return prenom; }
    public IntegerProperty ageProperty() { return age; }
    public StringProperty emailProperty() { return email; }
    
    // Getters classiques
    public String getNom() { return nom.get(); }
    public String getPrenom() { return prenom.get(); }
    public int getAge() { return age.get(); }
    public String getEmail() { return email.get(); }
    
    // Setters
    public void setNom(String nom) { this.nom.set(nom); }
    public void setPrenom(String prenom) { this.prenom.set(prenom); }
    public void setAge(int age) { this.age.set(age); }
    public void setEmail(String email) { this.email.set(email); }
}
Points importants :
• Utilisez StringProperty au lieu de String
• Utilisez IntegerProperty au lieu de int
• Créez les méthodes nomProperty(), ageProperty(), etc.
• PropertyValueFactory utilisera automatiquement ces propriétés

📝 Utilisation avec TableView

package application;

import javafx.fxml.FXML;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;

public class MainController {
    
    @FXML
    private TableView<Personne> tableViewPersonnes;
    
    @FXML
    private TableColumn<Personne, String> colNom;
    
    @FXML
    private TableColumn<Personne, String> colPrenom;
    
    @FXML
    private TableColumn<Personne, Integer> colAge;
    
    @FXML
    private TableColumn<Personne, String> colEmail;
    
    private ObservableList<Personne> personnes;
    
    @FXML
    private void initialize() {
        // Créer les données
        personnes = FXCollections.observableArrayList(
            new Personne("Dupont", "Jean", 25, "jean@exemple.com"),
            new Personne("Martin", "Marie", 30, "marie@exemple.com"),
            new Personne("Bernard", "Pierre", 28, "pierre@exemple.com")
        );
        
        // PropertyValueFactory utilise automatiquement les propriétés observables
        colNom.setCellValueFactory(new PropertyValueFactory<>("nom"));
        colPrenom.setCellValueFactory(new PropertyValueFactory<>("prenom"));
        colAge.setCellValueFactory(new PropertyValueFactory<>("age"));
        colEmail.setCellValueFactory(new PropertyValueFactory<>("email"));
        
        // Définir les titres
        colNom.setText("Nom");
        colPrenom.setText("Prénom");
        colAge.setText("Âge");
        colEmail.setText("Email");
        
        // Lier au TableView
        tableViewPersonnes.setItems(personnes);
    }
    
    @FXML
    private void modifierPersonne() {
        Personne selectionnee = tableViewPersonnes.getSelectionModel().getSelectedItem();
        if (selectionnee != null) {
            // Modifier les propriétés
            selectionnee.setNom("Nouveau nom");
            selectionnee.setAge(30);
            
            // Le TableView se met à jour AUTOMATIQUEMENT !
            // Pas besoin d'appeler refresh()
        }
    }
}
Avantage :
• Quand vous modifiez une propriété avec setNom(), le TableView se met à jour automatiquement
• Plus besoin d'appeler tableView.refresh()
• Les changements sont visibles en temps réel
Exemple TableView - Modèle observable
Exemple d'application TableView avec modèle observable : mise à jour automatique lors de la modification des propriétés

📝 Comparaison : Modèle simple vs Modèle observable

Aspect Modèle simple Modèle observable
Types String, int, etc. StringProperty, IntegerProperty, etc.
Mise à jour Nécessite refresh() Automatique
Complexité Plus simple Légèrement plus complexe
Performance Légèrement plus rapide Légèrement plus lent (négligeable)
Recommandation Données statiques ✅ Données modifiables

💡 Points clés à retenir

  • Propriétés observables : Utilisez StringProperty, IntegerProperty, etc.
  • Méthodes Property : Créez nomProperty(), ageProperty(), etc.
  • Mise à jour automatique : Le TableView se met à jour automatiquement
  • Pas de refresh() : Plus besoin d'appeler refresh() manuellement
  • PropertyValueFactory : Utilise automatiquement les propriétés observables
Conseil : Utilisez des propriétés observables si vous prévoyez de modifier les données après leur création. Pour des données statiques (lecture seule), un modèle simple suffit.

6.3.4 – Édition de cellule (optionnel)

Par défaut, les cellules d'un TableView sont en lecture seule. Vous pouvez activer l'édition pour permettre à l'utilisateur de modifier directement les valeurs dans le tableau. Cette fonctionnalité est optionnelle et doit être activée explicitement.

Note : L'édition de cellule est optionnelle. Activez-la seulement si vous voulez permettre à l'utilisateur de modifier les données directement dans le tableau.

🎯 Quand activer l'édition ?

  • Modification directe : L'utilisateur doit pouvoir modifier les valeurs rapidement
  • Interface simple : Pas besoin de formulaire séparé
  • Données simples : Les données sont simples à modifier (texte, nombres)
  • Expérience utilisateur : Améliore l'expérience utilisateur pour certaines applications

📝 Activer l'édition

Pour activer l'édition, vous devez :

  1. Activer l'édition sur le TableView
  2. Définir un CellFactory éditable pour chaque colonne
  3. Définir un OnEditCommit pour sauvegarder les modifications

Exemple : TableView avec édition

package application;

import javafx.fxml.FXML;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;

public class MainController {
    
    @FXML
    private TableView<Personne> tableViewPersonnes;
    
    @FXML
    private TableColumn<Personne, String> colNom;
    
    @FXML
    private TableColumn<Personne, String> colPrenom;
    
    @FXML
    private TableColumn<Personne, Integer> colAge;
    
    @FXML
    private TableColumn<Personne, String> colEmail;
    
    private ObservableList<Personne> personnes;
    
    @FXML
    private void initialize() {
        // Créer les données
        personnes = FXCollections.observableArrayList(
            new Personne("Dupont", "Jean", 25, "jean@exemple.com"),
            new Personne("Martin", "Marie", 30, "marie@exemple.com")
        );
        
        // Configurer les colonnes avec PropertyValueFactory
        colNom.setCellValueFactory(new PropertyValueFactory<>("nom"));
        colPrenom.setCellValueFactory(new PropertyValueFactory<>("prenom"));
        colAge.setCellValueFactory(new PropertyValueFactory<>("age"));
        colEmail.setCellValueFactory(new PropertyValueFactory<>("email"));
        
        // Activer l'édition sur le TableView
        tableViewPersonnes.setEditable(true);
        
        // Activer l'édition pour chaque colonne
        colNom.setCellFactory(TextFieldTableCell.forTableColumn());
        colNom.setOnEditCommit(event -> {
            Personne personne = event.getRowValue();
            personne.setNom(event.getNewValue());
        });
        
        colPrenom.setCellFactory(TextFieldTableCell.forTableColumn());
        colPrenom.setOnEditCommit(event -> {
            Personne personne = event.getRowValue();
            personne.setPrenom(event.getNewValue());
        });
        
        colEmail.setCellFactory(TextFieldTableCell.forTableColumn());
        colEmail.setOnEditCommit(event -> {
            Personne personne = event.getRowValue();
            personne.setEmail(event.getNewValue());
        });
        
        // Pour Integer, utiliser IntegerTextFieldTableCell
        colAge.setCellFactory(TextFieldTableCell.forTableColumn(new IntegerStringConverter()));
        colAge.setOnEditCommit(event -> {
            Personne personne = event.getRowValue();
            personne.setAge(event.getNewValue());
        });
        
        // Lier au TableView
        tableViewPersonnes.setItems(personnes);
    }
}
Explication :
setEditable(true) : Active l'édition sur le TableView
TextFieldTableCell.forTableColumn() : Crée une cellule éditable pour String
setOnEditCommit() : Définit l'action à exécuter quand l'édition est terminée
event.getRowValue() : Récupère l'objet de la ligne modifiée
event.getNewValue() : Récupère la nouvelle valeur
Exemple TableView - Édition de cellule
Exemple d'application TableView avec édition de cellule : double-clic sur une cellule pour la modifier directement

📝 Types de cellules éditables

JavaFX fournit plusieurs types de cellules éditables :

  • TextFieldTableCell : Pour les String (texte)
  • CheckBoxTableCell : Pour les Boolean (cases à cocher)
  • ChoiceBoxTableCell : Pour les choix dans une liste
  • ComboBoxTableCell : Pour les choix dans une liste déroulante

Exemple : CheckBoxTableCell pour Boolean

// Si vous avez une propriété Boolean "actif"
@FXML
private TableColumn<Personne, Boolean> colActif;

// Dans initialize()
colActif.setCellValueFactory(new PropertyValueFactory<>("actif"));
colActif.setCellFactory(CheckBoxTableCell.forTableColumn(colActif));
// Pas besoin de setOnEditCommit pour CheckBox (automatique)

📝 Comment éditer une cellule

Pour éditer une cellule, l'utilisateur doit :

  1. Double-cliquer sur la cellule (ou appuyer sur F2)
  2. Modifier la valeur dans le champ de texte
  3. Appuyer sur Entrée pour valider (ou Échap pour annuler)
Important :
  • ✅ L'édition doit être activée explicitement avec setEditable(true)
  • ✅ Chaque colonne doit avoir sa propre CellFactory éditable
  • ✅ Utilisez setOnEditCommit() pour sauvegarder les modifications
  • ✅ Pour les types non-String, utilisez un StringConverter approprié
  • ❌ L'édition ne fonctionne pas si le modèle n'a pas de setters

💡 Points clés à retenir

  • setEditable(true) : Active l'édition sur le TableView
  • CellFactory éditable : Définit le type de cellule éditable
  • setOnEditCommit() : Sauvegarde les modifications
  • TextFieldTableCell : Pour les String
  • CheckBoxTableCell : Pour les Boolean
  • StringConverter : Pour convertir les types (Integer, Double, etc.)
  • Optionnel : Utilisez seulement si nécessaire
Conseil : L'édition de cellule est pratique pour des modifications rapides, mais pour des modifications complexes, utilisez plutôt un formulaire séparé. Testez bien l'expérience utilisateur avant de l'activer partout !