Dans un état Access, apprendre à numéroter les pages d'un groupe « page x sur y »

Claude Leloup

Centres d'intérêt dans ce tutoriel :

- [Page] et [Pages] sous la loupe ;

- création d'une table de paires Clé/Valeur en mémoire (utilisation de Collection).

Les commentaires et les suggestions d'amélioration sont les bienvenus, alors, après votre lecture, n'hésitez pas : Commentez

- Titre : Dans un état Access, apprendre à numéroter les pages d'un groupe « page x sur y »

- Auteur : Claude Leloup

- Parution : 13 décembre 2016

- Licence : Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2016 Claude Leloup. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Ce que l'on veut réaliser

- II. Si vous êtes pressé

- III. L'idée qui sous-tend la programmation

- III-A. À propos des propriétés Page et Pages d'un état dans Access

- III-B. On va forcer Access à construire l'état en deux temps

- IV. Analyse détaillée du code VBA

- IV-A. Définition des variables

- IV-B. Dans l'événement Sur ouverture

- IV-C. Dans l'événement « Au formatage » entête de groupe

- IV-D. Dans l'événement « Au formatage » du pied de page

- IV-E. À la fermeture de l'état

- V. Téléchargement

- VI. Remerciements

I - Ce que l'on veut réaliser

Soit une table de mouvements comptables :

On voudrait un état dans lequel chacun des journaux (Achats, Caisse…) est paginé individuellement :

II - Si vous êtes pressé

… que vous n'aimez pas lire et que seul le résultat vous intéresse, voici comment y arriver :

- construisez votre état en vous inspirant de ce schéma :

Cliquez sur l'image pour l'agrandir

- dans le module associé à l'état, vous collez aveuglément ce code :

Option Compare Database
Option Explicit
Dim sCtlGroupe As String
Dim sValGroup As String
Dim sChampGroupe As String
Dim oColTable As Collection
Public Sub MaJoColTable(Groupe As String, NumPage As Integer)
On Error GoTo GestionErreurs
If NumPage > oColTable(Groupe) Then
oColTable.Remove Groupe
oColTable.Add NumPage, Groupe
End If
GestionErreurs:
Select Case Err.Number
Case 0 'pas d'erreur
Case 5 'pas encore dans la collection
oColTable.Add NumPage, Groupe
End Select
End Sub

Public Function GetPages(Groupe As String) As Integer
GetPages = oColTable(Groupe)
End Function

Private Sub Report_Open(Cancel As Integer)
On Error GoTo GestionErreurs
'Recherche du nom du contrôle qui contient le champ de groupage
sChampGroupe = Me.Report.GroupLevel(0).ControlSource  '<----- le niveau de regroupement le plus élevé
'Construire la propriété Contrôle source du champ txtNumerotation
Me.txtNumerotation.ControlSource = "=""  Page "" & [Page] & "" sur "" & getpages([" & sChampGroupe & "])"
'Initialiser la collection
Set oColTable = New Collection
GestionErreurs:
Select Case Err.Number
Case 0 'pas d'erreur
Case Else
MsgBox "Erreur dans Report_Open N° " & Err.Number & " " & Err.Description
End Select
End Sub

Private Sub EntêteGroupe0_Format(Cancel As Integer, FormatCount As Integer)
' Provoquer la réinitialisation de [Page] au changement de Groupe
Me.Page = 1
End Sub

Private Sub ZonePiedPage_Format(Cancel As Integer, FormatCount As Integer)
Call MaJoColTable(Me(sChampGroupe), Me.Page)
End Sub

Private Sub Report_Close()
Dim E As Integer
'Libérer l'objet collection de la mémoire
For E = 1 To oColTable.Count
oColTable.Remove 1
Next
Set oColTable = Nothing
End Sub

Mais, si vous voulez comprendre pourquoi ça marche, faites l'effort de lire la suite…

III - L'idée qui sous-tend la programmation

III-A - À propos des propriétés Page et Pages d'un état dans Access

La propriété Page spécifie le numéro de la page en cours lors de l'impression.
La propriété Pages spécifie le nombre total de pages.
Par défaut, Page commence sa numérotation à 1 et s'incrémente d'une unité à chaque page suivante.
Quand, dans un état, un champ fait référence à la propriété Pages, on oblige Access à procéder en deux temps :
- d'abord Access construit l'état sans l'imprimer, jusqu'à atteindre la dernière ligne pour déterminer le nombre de pages que contiendra finalement l'état. À ce stade, Access n'a pas encore garni les contrôles qui se réfèrent à Pages, mais il connaît maintenant le nombre de pages ;
- ensuite, en imprimant cette fois, Access recommence depuis le début en complétant la valeur de Pages dans les contrôles qui utilisent cette propriété.
Pages est disponible en lecture seule : l'utilisateur ne peut modifier sa valeur.
Par contre, avec des instructions VBA, on peut modifier la valeur de Page.
Par exemple, on peut forcer de recommencer à 1 à chaque changement d'une valeur de regroupement.
Dans l'événement Au formatage de la section qui contient le groupe, ce code :

Private Sub EntêteGroupe0_Format(Cancel As Integer, FormatCount As Integer)
' Provoquer la réinitialisation de [Page] au changement de Groupe
Me.Page = 1
End Sub

recommencera la numérotation à partir de 1 pour le groupe en cours de traitement.

III-B - On va forcer Access à construire l'état en deux temps

- On place dans l'état, un contrôle avec comme source =[Pages].

- À chaque changement de groupe, dans l'événement Au formatage de l'entête de groupe, on remet la propriété [Page] à 1.

- À chaque formatage d'une page, dans l'événement Au formatage du pied de page, on mémorise dans un tableau interne la paire : Nom du groupe/N° de page. Dans ce tableau, on ne retient pour un même Nom du groupe que la ligne qui contient le plus grand N° de page.
En d'autres mots, quand Access a fini sa première passe, le tableau contient une ligne par Nom de groupe et son N° de page le plus élevé, donc le nombre de pages de ce groupe.

- Au deuxième passage, Access sera en mesure de compléter la valeur du contrôle txtNumerotation dont la source a été construite à l'ouverture, dans notre exemple :

IV - Analyse détaillée du code VBA

IV-A - Définition des variables

Option Compare Database
Option Explicit
Dim sCtlGroupe As String
Dim sValGroup As String
Dim sChampGroupe As String
Dim oColTable As Collection

Elles sont définies en tête de module pour que leur valeur soit disponible tant que l'état est ouvert.

IV-B - Dans l'événement Sur ouverture

1. Private Sub Report_Open(Cancel As Integer)

2. On Error GoTo GestionErreurs

3. 'Recherche du nom du contrôle qui contient le champ de groupage

4. sChampGroupe = Me.Report.GroupLevel(0).ControlSource  '<----- le niveau de regroupement le plus élevé

5. 'Construire la propriété Contrôle source du champ txtNumerotation

6. Me.txtNumerotation.ControlSource = "=""  Page "" & [Page] & "" sur "" & getpages([" & sChampGroupe & "])"

7. 'Initialiser la collection

8. Set oColTable = New Collection

9. GestionErreurs:

10. Select Case Err.Number

11. Case 0 'pas d'erreur

12. Case Else

13. MsgBox "Erreur dans Report_Open N° " & Err.Number & " " & Err.Description

14. End Select

15. End Sub

Commentaires

4 : syntaxe pour retrouver le nom de la colonne qui contient les valeurs à regrouper.

6 : par exemple, si l'instruction 4 a révélé que « Journal » est le nom de la colonne à regrouper, la propriété Contrôle source du champ txtNumerotation sera :

=" Page " & [Page] & " sur " & getpages([Journal]).

IV-C - Dans l'événement « Au formatage » entête de groupe

Private Sub EntêteGroupe0_Format(Cancel As Integer, FormatCount As Integer)
' Provoquer la réinitialisation de [Page] au changement de Groupe
Me.Page = 1
End Sub

On recommence la numérotation à 1.

IV-D - Dans l'événement « Au formatage » du pied de page

Private Sub ZonePiedPage_Format(Cancel As Integer, FormatCount As Integer)
Call MaJoColTable(Me(sChampGroupe), Me.Page)
End Sub

Avec en paramètres la valeur du groupe et le N° de la page en cours, on appelle la routine de mise à jour de la table interne.

L'algorithme est le suivant :

1. Public Sub MaJoColTable(Groupe As String, NumPage As Integer)

2. On Error GoTo GestionErreurs

3. If NumPage > oColTable(Groupe) Then

4. oColTable.Remove Groupe

5. oColTable.Add NumPage, Groupe

6. End If

7. GestionErreurs:

8. Select Case Err.Number

9. Case 0 'pas d'erreur

10. Case 5 'pas encore dans la collection

11. oColTable.Add NumPage, Groupe

12. End Select

13. End Sub

Commentaire du code

3 : si la clé Groupe (la rubrique du journal) n'est pas encore dans la collection, une erreur N° 5 sera générée. On récupère cette dernière aux instructions 10 et 11, où on ajoute l'item à la collection.
Par contre, si la clé est déjà présente avec un N° de page inférieur, on l'élimine pour la remplacer par une paire Nouveau N° de page/Rubrique.

IV-E - À la fermeture de l'état

Private Sub Report_Close()
Dim E As Integer
'Libérer l'objet collection de la mémoire
For E = 1 To oColTable.Count
oColTable.Remove 1
Next
Set oColTable = Nothing
End Sub

V - Téléchargement

La base de données de test au format Access2000 est disponible ici.

VI - Remerciements

Merci à tee_grandbois pour ses remarques.

Merci à Jean-Philippe AMBROSINO (argyronet) pour la relecture technique et les corrections pour améliorer la qualité du code.

Merci à f-leb, Winjerome et dourouc05 pour la correction orthographique.