Manipuler les tables en VBA

La manipulation des tables en VBA présente quelques particularités que nous allons voir ici.

Les tables font partie de la collection Listobjects qui a été mise en place avec la version 2003. On peut donc utiliser les propriétés et méthodes de cette collection, et l'enregistreur de macros vous permet d'y accéder facilement.
Mais les tables sont aussi des objets range (dans 2007) et donc on a parfois le choix entre plusieurs écritures pour réaliser la même action.

L'avantage d'utiliser l'objet range c'est que le nom donné par excel à une table est unique dans le classeur et que du coup il n'est pas nécessaire de préciser la feuille. De ce fait, même si vous déplacez votre table sur une autre feuille, votre code VBA restera valide. Si vous copiez une feuille entière contenant des tables, excel renommera automatiquement les tables sur la copie pour éviter les doublons dans les noms.
Cependant certaines fonctions ne sont pas disponibles avec l'objet range. Lorsque l'on utilise la collection listobjects, il est en revanche nécessaire de préciser la feuille contenant l'objet.

Il y a certaines différences entre la manipulation des listes (excel 2003) ou des tables (excel 2007 et suivantes) par VBA. Nous ne parlerons ici que des codes se rapportant aux versions 2007 et suivantes. Ceux qui souhaitent des précisions sur la manipulation des listes excel 2003 en VBA trouveront plus d'informations sur le site de Jan Karel Pieterse qui m'a été fort utile pour écrire cette page.

Manipulation de tables existantes

Reprenons notre exemple des pages précédentes avec la table data_famille située sur la feuille data.

dans lequel dans la ligne total nous avons la somme de la colonne, sauf pour 'courses_pax' (montant des courses par personne) et 'visites_pax' pour lesquelles figurent la moyenne de la colonne.

Déterminer si une cellule fait partie d'une table

Sub Appartenance()
If ActiveCell.ListObject Is Nothing Then
    MsgBox "cette cellule n'appartient pas à une table"
Else
    MsgBox ("cette cellule fait partie de la table " & ActiveCell.ListObject.Name)
End If
End Sub

Pour compter les différents éléments de la table

Nombre de lignes (sans les entêtes et totaux) :
MsgBox (Range("data_famille").Rows.Count)
Nombre de colonnes  (sans les entêtes et totaux) :
MsgBox (Range("data_famille").columns.Count)
Notons qu'il n'est pas nécessaire de préciser le nom de la feuille sur laquelle se trouve la table, ce nom étant unique dans le classeur. Ce code reste donc valide même si vous déplacez la table sur une autre feuille. C'est un des avantages très intéressant des tables.
Récupérer l'adresse de la table :
msgbox(range("data_famille").address)
Ceci vous montre qu'excel stocke comme adresse celle contenant les données de la table en excluant les entêtes et les totaux.
Si vous avez besoin de l'adresse de l'ensemble de la table utilisez cette syntaxe:
MsgBox (Sheets("data").ListObjects("data_famille").Range.Address)

Sélectionner différents éléments de la table

Pour sélectionner le code :
Une colonne entière sans les entêtes ou totaux range("data_famille[courses]").select
Une colonne entière avec les entêtes et totaux range("data_famille[#ALL],[courses]]").select
Toute la table sauf les entêtes et totaux range("data_famille[#ALL]").select
La ligne 5 range("A5:G5").select
ce qui impose de faire référence aux numéros de ligne ou de colonne, ou sinon,en utilisant la collection listobjects
     With Sheets("data")
          .ListObjects("data_famille").ListRows(4).Range.Select
     End With
La dernière ligne With Sheets("data")
      .ListObjects("data_famille").ListRows(range("data_famille").rows.count).Range.Select
End With

Ajouter une ligne

Ajouter une ligne de total automatique et choisir les opérations à y effectuer
Sub AjouterTotal()
With Sheets("data").ListObjects("data_famille")
    .ShowTotals = True
    .ListColumns("nb personnes").TotalsCalculation = xlTotalsCalculationSum
    .ListColumns("courses").TotalsCalculation = xlTotalsCalculationSum
    .ListColumns("visites").TotalsCalculation = xlTotalsCalculationSum
    .ListColumns("courses_pax").TotalsCalculation = xlTotalsCalculationAverage
    .ListColumns("visites_pax").TotalsCalculation = xlTotalsCalculationAverage
    .ListColumns("total dépenses").TotalsCalculation = xlTotalsCalculationSum
End With
End Sub
Pour ajouter une ligne sous la dernière ligne de la table :
Dim LR as ListRows
Set LR = Range("data_famille[#Totals]").ListObject.ListRows.Add(AlwaysInsert:=True)

Il faut noter que si la table contient une ligne de totaux, la nouvelle ligne est insérée au dessus de celle-ci.

Insertion en haut de la table :
Range("data_famille").ListObject.ListRows.Add (1)
Ajouter une ligne en 4° position :
Range("data_famille").ListObject.ListRows.Add Position:=4
ou
Range("data_famille").ListObject.ListRows.Add (4)

On peut ensuite ajouter des données dans la nouvelle ligne comme dans l'exemple ci-dessous.

Modification de la table en utilisant un formulaire pour la saisie des données
Construisons un simple formulaire (userform) qui va nous servir à modifier notre table.
Ce premier formulaire va ajouter une famille et entrer les données correspondantes dans la table.

En cliquant sur le bouton OK, on ajoute une ligne à notre table, au dessus de la ligne des totaux puis on entre les données dans la nouvelle ligne ainsi créée.

Private Sub CmdOK_Click()
Dim LR As ListRow

Range("data_famille[#Totals]").Select 'pas besoin de préciser le nom de la feuille
Set LR = Selection.ListObject.ListRows.Add(AlwaysInsert:=True)
LR.Range.Cells(1, 1) = TxtNom
LR.Range.Cells(1, 2) = CInt(TxtNbPersonnes)
LR.Range.Cells(1, 3) = CSng(TxtCourses)
LR.Range.Cells(1, 4) = CSng(TxtVisites)

Unload UsfNewFamille
End Su

Boucler sur plusieurs tables

Si nous voulons entrer dans plusieurs tables une ligne avec le nom de cette nouvelle famille, on peut boucler sur les différentes tables :
    With Worksheets("data") 'pas obligatoire, on peut boucler sur l'ensemble des tables 
                            'd'un classeur si on ne précise pas la feuille
        For Each tablo In .ListObjects
            TN = tablo.Name
            .Range(TN & "[#Totals]").Select
            Set LR = Selection.ListObject.ListRows.Add(AlwaysInsert:=True)
            LR.Range.Cells(1, 1) = TxtFamille
        Next
    End With

Boucler sur les cellules d'une colonne

Sub BouclerColonne()
Dim MaListe As ListObject
Dim cel As Range
Set MaListe = Sheets("data").ListObjects("data_famille")
For Each cel In MaListe.DataBodyRange.Columns(1).Cells
    MsgBox cel.Address & " : " & CStr(cel.Value)
Next cel
End Sub

Trier et filtrer une table
Avec la version 2007 sont apparues de nouvelles possiblités pour trier et filtrer des plages de données. On peut bien sur les utiliser pour les tables. L'enregistreur des macro vous permettra d'accéder aux différentes possiblités facilement.
Voici par exemple comment trier la table sur la colonne "nb personnes" et cacher les lignes correspondant aux familles d'une seule personne :

Sub TrierEtFiltrer()
With Worksheets("data").ListObjects("data_famille")
.Sort.SortFields.Clear
.Sort.SortFields.Add Key:=Range("data_famille[[#Headers],[#Data],[nb personnes]]"), _
 SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:=xlSortNormal
    With .Sort
        .Header = xlYes
        .MatchCase = False
        .Orientation = xlTopToBottom
        .SortMethod = xlPinYin
        .Apply
    End With
End With

Créer et annuler la création d'une table

Créer une table à partir d'une plage de données :

Sub CréerTable()
    ActiveSheet.ListObjects.Add(xlSrcRange, Range("$A$7:$F$16"), , xlYes).Name = _
        "data_famille"
    Range("data_famille[#All]").Select
    ActiveSheet.ListObjects("data_famille").TableStyle = "TableStyleMedium2 2"
End Sub

L'enregistreur de macro vous permettra d'explorer le formatage de la table en fonction de vos choix. Il n'y a rien de bien particulier.

Transformer une table en plage de données "classique" :

Sub déListe()
    Sheets("data").ListObjects("data_famille").Unlist
End Sub

Mots clefs associés à cette page : , ,

Merci Misange pour cette page

Merci Misange pour cette page qui m'a été très utile.
J'en profite pour apporter mon p'tit caillou ;-) pour corriger une petite coquille en rapport avec le tableau placé sous "Sélectionner différents éléments de la table"

range("data_famille[#ALL]").select ==> TOUT le tableau (y compris les en-têtes et la ligne de total si elle existe)

range("data_famille[#DATA]").select ==> QUE les données du tableau

@+ peper-eliot

Nouveau

1 Nouvelle page ajoutée ce mois-ci dont :

Mises à jour

3 pages modifiées ce mois-ci dont :