ACCESS : Créez un formulaire d'attente
pour les longs traitements

Créez un formulaire d'attente pour les longs traitements.

1 - Création d'un formulaire simple.
2 - Puis ajout d'un module de classe.
3 - Et enfin ajout d'une animation.

26 commentaires Donner une note à l'article (5)

Article lu   fois.

L'auteur

Site personnel

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

I. Introduction

Lors de traitements assez longs, nous souhaitons afficher leur progression pour indiquer à l'utilisateur qu'il doit attendre.

Nous allons apprendre à développer un formulaire d'attente comme celui-ci :

Image non disponible

Ce tutoriel ne s'applique que si vous avez une boucle de traitement.
Il faut en effet pouvoir mettre à jour régulièrement le formulaire.

II. Formulaire simple

Pour débuter, nous allons créer un formulaire qui sera directement piloté par le code de traitement.

Image non disponible

Voici le rendu de ce formulaire :

Image non disponible

II-A. Création du formulaire


Créez un nouveau formulaire et sauvegardez-le sous le nom FormAttente.

II-B. Propriétés du formulaire

Modifiez les propriétés du formulaire :

Image non disponible  Dans l'onglet Autres :

Image non disponible

Propriété Valeur Explication
Fen indépendante Oui La fenêtre est ainsi flottante, et se déplace et se redimensionne indépendamment des autres fenêtres.
Fen modale Oui Une fenêtre modale empêche l'utilisateur de cliquer sur autre fenêtre.
Ainsi, l'utilisateur ne pourra agir que sur la fenêtre d'attente durant le traitement.


Image non disponible  Dans l'onglet Format :

Image non disponible

Propriété Valeur Explication
Légende Patientez ... Ceci est le texte affiché dans la barre de titre de la fenêtre.
Auto centrer Oui Pour centrer la fenêtre...
Taille ajustée Oui Pour ajuster la taille de la fenêtre à son contenu.
Style bordure Trait simple fixe On ne laisse pas la possibilité de redimensionner la fenêtre.
Afficher sélecteur Non Le sélecteur d'enregistrement est inutile ici.
Boutons de déplacement Non Les boutons de déplacements entre les enregistrements sont inutiles ici.
Diviseurs d'enregistrements Non Les diviseurs d'enregistrements sont inutiles ici.
Barre de défilement Aucune Les barres de défilements ne sont pas utiles, puisqu'on ajuste la taille de la fenêtre.
Bouton MinMax Aucun On n'autorise pas l'agrandissement ou la réduction de la fenêtre.
Bouton Fermer Non On n'autorise pas la fermeture de la fenêtre.

II-C. Contrôles du formulaire

Créez les contrôles sur le formulaire de cette manière :

Image non disponible

Vous aurez peut-être besoin d'écrire au minimum un caractère (espace par exemple) dans les étiquettes (propriété Légende) sinon elles seront automatiquement supprimées.

Image non disponible  Le contrôle lblInfo est une étiquette.
Choisissez par exemple une taille de caractères de 14 et une écriture en gras.
Cette étiquette servira à afficher un texte d'information fixe.

Image non disponible  Le contrôle lblProgress est une étiquette.
Choisissez par exemple une taille de caractères de 10.
Cette étiquette servira à afficher un texte d'information variable.

Image non disponible  Le contrôle lblProgressBack est une étiquette.
Donnez lui un style de fond standard et une couleur de fond bleu clair par exemple.
Image non disponible
Cette étiquette servira de fond à la barre d'avancement.

Image non disponible  Le contrôle lblProgressBar est une étiquette.
Donnez lui un style de fond standard et une couleur de fond bleu foncé par exemple.
Image non disponible
Cette étiquette servira de barre d'avancement.


Concernant la barre d'avancement, on utilise deux étiquettes colorées superposées.
On fera varier la taille de l'étiquette lblProgressBar pour obtenir une barre de progression.

II-D. Affichage du formulaire d'attente

II-D-1. Création d'une boucle de traitement

Créons un formulaire de test nommé FormTest qui ne contient qu'un bouton.
On va programmer un traitement dans l'évènement Sur Click de ce bouton.

Pour que le traitement soit suffisamment long, on imbrique 2 boucles dans lesquelles on place une instruction de traitement de chaîne de caractères.

Long traitement dans le formulaire de test
Sélectionnez

    Option Compare Database
    Option Explicit
    
    Private Sub Commande0_Click()
    Dim lCptIteration1 As Long
    Dim lCptIteration2 As Long
    Dim lString As String
    Const lNbIterations As Long = 10000
    ' Boucle de traitement
    For lCptIteration1 = 1 To lNbIterations
        For lCptIteration2 = 1 To 1000
            lString = Chr((lCptIteration1 + lCptIteration2) Mod 256)
        Next
    Next
    End Sub
    


Si on affiche le formulaire de test et qu'on clique sur le bouton, le traitement s'exécute.
Durant le traitement, Access ne répond plus.
Après un instant, le curseur sablier s'affiche et "(Ne répond pas)" s'inscrit dans la barre de titre.
L'utilisateur peut penser que l'application ne fonctionne plus, ce que nous souhaitons éviter.

II-D-2. Affichage du formulaire le temps du traitement

Nous allons encadrer notre traitement par une ouverture et une fermeture de notre formulaire d'attente Form_Attente.

Ouverture et fermeture du formulaire d'attente
Sélectionnez

Private Sub Commande0_Click()
Dim lCptIteration1 As Long
Dim lCptIteration2 As Long
Dim lString As String
Const lNbIterations As Long = 10000
' Ouverture du formulaire d'attente
DoCmd.OpenForm "FormAttente"
Forms("FormAttente").lblInfo.Caption = "Veuillez patienter durant le traitement ... "
Forms("FormAttente").lblProgressBar.Width = 0
' Nécessaire pour redonner la main à windows le temps de traiter les messages dans la pile
DoEvents
' Boucle de traitement
For lCptIteration1 = 1 To lNbIterations
    For lCptIteration2 = 1 To 1000
        lString = Chr((lCptIteration1 + lCptIteration2) Mod 256)
    Next
Next
' Fermeture du formulaire d'attente
DoCmd.Close acForm, "FormAttente"
End Sub


Explication du code ci-dessus :
- On commence par ouvrir le formulaire FormAttente.
- Ensuite on met à jour l'étiquette lblInfo avec un texte qui sera fixe durant le traitement.
- Puis on initialise la barre de progression en donnant une largeur nulle à la barre lblProgressBar.
- Enfin on utilise DoEvents pour redonner la main à Windows qui peut alors afficher notre fenêtre à l'écran.

- En fin de traitement, on ferme le formulaire d'attente.

=> On affiche donc bien le formulaire d'attente, par contre il est figé.

II-D-3. Mise à jour du formulaire durant le traitement

A moins d'avoir besoin d'afficher le détail de chaque itération, il n'est pas nécessaire de mettre à jour le formulaire d'attente à chaque itération de notre traitement.
Pour le test nous allons nous fixer un intervalle de mise à jour : par exemple toutes les 500 itérations.

L'opérateur Modulo (Mod en VBA) nous sera utile : il donne le reste d'une division.
On divise le compteur par 500; si le reste est égal à zéro c'est qu'on a effectué un multiple de 500 itérations.

Pour trouver le pourcentage d'avancement on est dans un cas simple :
Il suffit de diviser le compteur d'itérations par le nombre d'itérations totales.

Une fois le pourcentage d'avancement calculé on peut alors :
- modifier le texte de l'étiquette d'avancement lblProgress.
- modifier la largeur de l'étiquette lblProgressBar pour simuler une barre de progression.

Code de mise à jour de l'avancement du formulaire d'attente
Sélectionnez

Dim lPercent As Single
[...]
' Boucle de traitement
For lCptIteration1 = 1 To lNbIterations
    For lCptIteration2 = 1 To 1000
        lString = Chr((lCptIteration1 + lCptIteration2) Mod 256)
    Next
    ' Toutes les 500 itérations
    If lCptIteration1 Mod 500 = 0 Then
        ' Calcul du pourcentage d'avancement
        lPercent = lCptIteration1 / lNbIterations
        ' Met à jour l'étiquette d'avancement
        Forms("FormAttente").lblProgress.Caption = "Traitement en cours ... " & Format(lPercent, "00%")
        ' Met à jour la barre de progression
        Forms("FormAttente").lblProgressBar.Width = Forms("FormAttente").lblProgressBack.Width * lPercent
        ' Repeint le formulaire
        Forms("FormAttente").Repaint
    End If
Next 
[...]


Exécutez le traitement.
Le formulaire est bien affiché, avec mise à jour de l'image et de la progression.
Par contre l'application ne répond toujours pas.
Par exemple :
- on ne peut pas déplacer le formulaire.
- si on bascule sur une autre fenêtre, on ne peut pas réactiver l'application Access.

Il faut ajouter un DoEvents dans la boucle.

 
Sélectionnez

[...]
    ' Evite que l'application ne soit figée
    DoEvents
Next
[...]

II-D-4. Gestion des erreurs

Dans toute fonction, il est nécessaire de gérer les erreurs :
- pour afficher un message d'erreur personnalisé
- pour éviter un plantage de l'application en exécution avec le runtime

Ajoutons une gestion des erreurs :

Initialisation de la gestion d'erreurs, en début de procédure
Sélectionnez

On Error GoTo Gestion_Erreurs
Gestion d'erreurs, en fin de procédure
Sélectionnez

[...]
Exit Sub
Gestion_Erreurs:
    MsgBox "Erreur lors du traitement,  " & Err.Number & ", " & Err.Description, vbOKOnly
    DoCmd.Close acForm, "FormAttente"
End Sub 

En cas d'erreur on affiche alors un message et on ferme le formulaire d'attente.

II-D-5. Rappel du code complet

Code complet du formulaire de test du formulaire d'attente
Sélectionnez

Option Compare Database
Option Explicit

    
Private Sub Commande0_Click()
Dim lCptIteration1 As Long
Dim lCptIteration2 As Long
Dim lString As String
Dim lPercent As Single
Const lNbIterations As Long = 10000
On Error GoTo Gestion_Erreurs
' Ouverture du formulaire d'attente
DoCmd.OpenForm "FormAttente"
Forms("FormAttente").lblInfo.Caption = "Veuillez patienter durant le traitement ... "
Forms("FormAttente").lblProgressBar.Width = 0
' Nécessaire pour redonner la main à windows le temps de traiter les messages dans la pile
DoEvents
' Boucle de traitement
For lCptIteration1 = 1 To lNbIterations
    For lCptIteration2 = 1 To 1000
        lString = Chr((lCptIteration1 + lCptIteration2) Mod 256)
    Next
    ' Toutes les 500 itérations
    If lCptIteration1 Mod 500 = 0 Then
        ' Calcul du pourcentage d'avancement
        lPercent = lCptIteration1 / lNbIterations
        ' Met à jour l'étiquette d'avancement
        Forms("FormAttente").lblProgress.Caption = "Traitement en cours ... " & Format(lPercent, "00%")
        ' Met à jour la barre de progression
        Forms("FormAttente").lblProgressBar.Width = Forms("FormAttente").lblProgressBack.Width * lPercent
        ' Repeint le formulaire
        Forms("FormAttente").Repaint
    End If
    ' Evite que l'application ne soit figée
    DoEvents
Next
' Fermeture du formulaire d'attente
DoCmd.Close acForm, "FormAttente"
Exit Sub
Gestion_Erreurs:
    MsgBox "Erreur lors du traitement,  " & Err.Number & ", " & Err.Description, vbOKOnly
    DoCmd.Close acForm, "FormAttente"
End Sub

II-E. Conclusion

Le formulaire ainsi créé est très simple mais néanmoins efficace.

Téléchargez la base Access de ce chapitre au format ACCESS 2000

III. Pilotage par un module de classe

Pour continuer, nous allons créer un module de classe qui pilotera le formulaire d'avancement.

Image non disponible

Dans ce chapitre, nous ne changerons rien au rendu de ce formulaire :

Image non disponible

III-A. Pourquoi un module de classe?

Créer un module de classe nous permet ensuite de gérer le formulaire plus simplement à l'aide de méthodes et de propriétés.
Pourquoi ne pas ajouter ces propriétés et méthodes directement dans le formulaire?
En créant un module de classe on les regroupe dans un objet à part.
Ainsi on accède plus facilement aux méthodes et propriétés nécessaires au formulaire d'attente; elles ne sont, en effet, pas "noyées" parmi les autres propriétés et méthodes du formulaire :
- Le module de classe contient les propriétés et méthodes.
- Le formulaire contient uniquement les contrôles.

On pourra également facilement remplacer le formulaire par un autre, la seule condition étant que ce formulaire contienne les contrôles requis.

III-B. Création du module de classe

Dans l'éditeur VBE (ALT + F11), insérer un nouveau module de classe (Insérer => Module de classe).
Si l'instruction n'y est pas, ajoutez Option explicit en en-tête du module.
Sauvegardez le module sous le nom clProgress.

III-C. Les méthodes et propriétés du module de classe clProgress

Le module de classe clProgress doit nous aider à piloter le formulaire.
Il doit donc exposer toutes les méthodes et propriétés utiles afin de ne plus avoir besoin d'agir directement sur le formulaire.

Il nous faut pouvoir :
- Définir les textes d'information générale et détaillée (lblInfo et lblProgress).
- Définir les valeurs de la barre de progression : valeurs mini, maxi, courante, et le pas de mise à jour
- Afficher ou masquer le formulaire.
- Rafraîchir l'affichage du formulaire.

Nom Type Description
Visible Propriété en lecture/écriture Rend visible ou invisible le formulaire d'attente
GeneralInfo Propriété en lecture/écriture Texte d'information général.
ProgressInfo Propriété en lecture/écriture Texte d'information détaillée, affiché au dessus de la barre de progression.
ProgressForm Propriété en lecture/écriture Formulaire utilisée pour afficher la progression du traitement.
ProgressMin Propriété en lecture/écriture Valeur minimum de la progression.
ProgressMax Propriété en lecture/écriture Valeur maximum de la progression.
ProgressValue Propriété en lecture/écriture Valeur courante de la progression.
ProgressPercent Propriété en lecture/écriture Valeur courante de la progression, en pourcent.
Repaint Méthode Méthode à appeler pour rafraichir l'affichage du formulaire.
On placera le DoEvents ici.


Remarque : Pour la barre de progression, on définit les propriétés en double : une pour des valeurs discrètes et une pour des valeurs en pourcentage.

Les propriétés qui ne sont pas gérées par le formulaire doivent être stockées dans ce module de classe.
Par exemple la propriété GeneralInfo est stockée dans l'étiquette lblInfo du formulaire : inutile donc de rajouter une variable dans le module de classe.
Les propriétés calculées (comme la valeur en pourcentage) ne sont pas stockées.

On va donc stocker les valeurs des propriétés suivantes :
- ProgressForm
- ProgressMin
- ProgressMax
- ProgressValue

Variables de stockage, en en-tête de module
Sélectionnez

Private oProgressForm As Access.Form
Private gProgressMin As Long
Private gProgressMax As Long
Private gProgressValue As Long


Ensuite, à l'initialisation du module de classe, nous devons créer un formulaire et initialiser les valeurs des variables.

Pour pouvoir facilement créer un objet formulaire, définissez la propriété Avec Module du formulaire à Oui (voir onglet Autres des propriétés du formulaire).

Image non disponible
Le formulaire devient alors un objet de classe, et on peut le voir dans l'explorateur d'objets de VBA :

Image non disponible
La création d'une instance de cet objet se fait avec le mot-clé New.
Pour en savoir plus sur l'utilisation des formulaires en tant qu'objet de classe, lisez le tutoriel La notion de la classe formulaire Access par l'exemple, de Maxence Hubiche.

Le principal avantage de cette méthode par rapport à l'utilisation de OpenForm est que la liaison avec le formulaire se fait à la compilation.
Si on appelle une propriété ou méthode inconnue, on le sait à la compilation.
De plus, on bénéficie de la complétion automatique lors de l'écriture du code.

Initialisation des variables et création de l'objet formulaire
Sélectionnez

'------------------------------------------------------------
' Initialisation du module de classe
'------------------------------------------------------------
Private Sub Class_Initialize()
' Initialisation de la barre de progression
gProgressMin = 1
gProgressMax = 100
gProgressValue = 0
' Création d'un nouvel objet formulaire
Set oProgressForm = New Form_FormAttente
End Sub


Pour la création des propriétés et méthodes, si vous n'êtes pas familier avec les modules de classe, vous pouvez lire ces articles :
- Tout comprendre à propos des modules de classes sous Access, par Michel Blavin.
- Création et utilisation de classes personnalisées en VB 6.0 et VBA, par Pierre Fauconnier.

Concernant la gestion d'erreurs, il est souvent conseillé de laisser le code appelant gérer les erreurs.
Nous allons appliquer ce conseil : si une erreur est détectée dans le module de classe, elle sera renvoyée à l'appelant (notre code de traitement dans le formulaire de tests) qui se chargera d'effectuer les actions nécessaires (écriture de l'erreur dans un fichier log, affichage d'un message, poursuite ou arrêt du traitement, ...).

Nous pourrions nous contenter de ne pas capturer les erreurs.
Mais, pour faciliter la prise de décision lors d'une erreur, nous allons renvoyer depuis le module de classe l'erreur dont nous modifions la source.
Pour renvoyer l'erreur nous utiliserons la méthode Raise de l'objet Err en modifiant le paramètre Source.
Ainsi nous pourrons déterminer en fonction de la source si l'erreur provient du formulaire d'attente ou du traitement lui-même.

Définitions des propriétés et méthodes
Sélectionnez

'------------------------------------------------------------
' Visibilité du formulaire
'------------------------------------------------------------
Public Property Get Visible() As Boolean
On Error GoTo Gestion_Erreurs
    Visible = oProgressForm.Visible
Exit Property
Gestion_Erreurs:
Err.Raise Err.Number, "clProgress"
End Property
Public Property Let Visible(pVisible As Boolean)
On Error GoTo Gestion_Erreurs
    oProgressForm.Visible = pVisible
Exit Property
Gestion_Erreurs:
Err.Raise Err.Number, "clProgress"
End Property
'------------------------------------------------------------
' Information générale
'------------------------------------------------------------
Public Property Get GeneralInfo() As String
On Error GoTo Gestion_Erreurs
    GeneralInfo = oProgressForm.lblInfo.Caption
Exit Property
Gestion_Erreurs:
Err.Raise Err.Number, "clProgress"
End Property
Public Property Let GeneralInfo(pInfo As String)
On Error GoTo Gestion_Erreurs
    oProgressForm.lblInfo.Caption = pInfo
Exit Property
Gestion_Erreurs:
Err.Raise Err.Number, "clProgress"
End Property
'-------------------------------------------------------------
' Information détaillée (au-dessus de la barre de progression)
'-------------------------------------------------------------
Public Property Get ProgressInfo() As String
On Error GoTo Gestion_Erreurs
    ProgressInfo = oProgressForm.lblProgress.Caption
Exit Property
Gestion_Erreurs:
Err.Raise Err.Number, "clProgress"
End Property
Public Property Let ProgressInfo(pInfo As String)
On Error GoTo Gestion_Erreurs
    oProgressForm.lblProgress.Caption = pInfo
Exit Property
Gestion_Erreurs:
Err.Raise Err.Number, "clProgress"
End Property
'------------------------------------------------------------
' Valeur mini de la progression
'------------------------------------------------------------
Public Property Get ProgressMin() As Long
On Error GoTo Gestion_Erreurs
    ProgressMin = gProgressMin
Exit Property
Gestion_Erreurs:
Err.Raise Err.Number, "clProgress"
End Property
Public Property Let ProgressMin(pMin As Long)
On Error GoTo Gestion_Erreurs
    gProgressMin = pMin
Exit Property
Gestion_Erreurs:
Err.Raise Err.Number, "clProgress"
End Property
'------------------------------------------------------------
' Valeur maxi de la progression
'------------------------------------------------------------
Public Property Get ProgressMax() As Long
On Error GoTo Gestion_Erreurs
    ProgressMax = gProgressMax
Exit Property
Gestion_Erreurs:
Err.Raise Err.Number, "clProgress"
End Property
Public Property Let ProgressMax(pMax As Long)
On Error GoTo Gestion_Erreurs
    gProgressMax = pMax
Exit Property
Gestion_Erreurs:
Err.Raise Err.Number, "clProgress"
End Property
'-----------------------------------------------------------
' Progression en valeur
'-----------------------------------------------------------
Public Property Get ProgressValue() As Long
On Error GoTo Gestion_Erreurs
    ProgressValue = gProgressValue
Exit Property
Gestion_Erreurs:
Err.Raise Err.Number, "clProgress"
End Property
Public Property Let ProgressValue(pValue As Long)
On Error GoTo Gestion_Erreurs
    ' Stocke la valeur
    gProgressValue = pValue
    ' On met à jour la barre
    oProgressForm.lblProgressBar.Width = oProgressForm.lblProgressBack.Width / _
                (gProgressMax - gProgressMin + 1) * (ProgressValue - gProgressMin + 1)
Exit Property
Gestion_Erreurs:
Err.Raise Err.Number, "clProgress"
End Property
'-----------------------------------------------------------
' Progression en pourcent
'-----------------------------------------------------------
Public Property Get ProgressPercent() As Currency
On Error GoTo Gestion_Erreurs
    ProgressPercent = (ProgressValue - gProgressMin + 1) / (gProgressMax - gProgressMin + 1)
Exit Property
Gestion_Erreurs:
Err.Raise Err.Number, "clProgress"
End Property
Public Property Let ProgressPercent(pProgress As Currency)
On Error GoTo Gestion_Erreurs
    ProgressValue = gProgressMin + pProgress * (gProgressMax - gProgressMin + 1)
Exit Property
Gestion_Erreurs:
Err.Raise Err.Number, "clProgress"
End Property

'-----------------------------------------------------------
' Raffraîchit l'affichage du formulaire
'-----------------------------------------------------------
Public Sub Repaint()
On Error GoTo Gestion_Erreurs
    ' Repeint le formulaire
    oProgressForm.Repaint
    ' Evite que l'application ne soit figée
    DoEvents
Exit Sub
Gestion_Erreurs:
Err.Raise Err.Number, "clProgress"
End Sub


Ajoutons également la gestion d'erreurs dans Class_Initialize :

Initialisation du module de classe avec gestion d'erreur
Sélectionnez

'------------------------------------------------------------
' Initialisation du module de classe
'------------------------------------------------------------
Private Sub Class_Initialize()
On Error GoTo Gestion_Erreurs
' Initialisation de la barre de progression
gProgressMin = 1
gProgressMax = 100
gProgressValue = 0
' Création d'un nouvel objet formulaire
Set oProgressForm = New Form_FormAttente
Exit Sub
Gestion_Erreurs:
Err.Raise Err.Number, "clProgress"
End Sub

III-D. Modification du code de traitement pour affichage du formulaire d'attente

Une fois que notre module est prêt, il faut l'utiliser dans la boucle de traitement.

On va repartir de notre boucle de traitement initiale :

Long traitement dans le formulaire de test
Sélectionnez

Option Compare Database
Option Explicit

Private Sub Commande0_Click()
Dim lCptIteration1 As Long
Dim lCptIteration2 As Long
Dim lString As String
Const lNbIterations As Long = 10000
On Error GoTo Gestion_Erreurs
' Boucle de traitement
For lCptIteration1 = 1 To lNbIterations
    For lCptIteration2= 1 To 1000
        lString = Chr((lCptIteration1 + lCptIteration2) Mod 256)
    Next
Next
Exit Sub
Gestion_Erreurs:
    If Err.Source = "clProgress" Then
        ' Ajoutez ici un éventuel code de journalisation des erreurs (fichier log)
        ' XXXXXX
        ' Puis on continue le traitement
        Resume Next
    Else
        MsgBox "Erreur dans le traitement  " & Err.Number & ", " & Err.Description, vbCritical
    End If
End Sub
    


Notez la gestion des erreurs :
- on vérifie si la source de l'erreur est le module de classe clProgress.
- si c'est le cas on peut écrire l'erreur dans un fichier de log.
- puis on continue le traitement si on décide par exemple qu'une erreur dans le formulaire d'attente ne doit pas arrêter le traitement.


Pour ouvrir un formulaire d'attente, nous allons désormais créer un objet de type clProgress.
Cette objet se chargera d'ouvrir le formulaire (dans sa procédure Class_Initialize)
Le formulaire ainsi créé ne sera visible que lorsqu'on définira la propriété Visible à True, ce qui nous permet d'initialiser l'affichage avant d'afficher le formulaire.

En début de la procédure de test (Commande0_Click), on déclare l'objet :

Déclaration de l'objet
Sélectionnez

Private Sub Commande0_Click()
Dim oProgress As clProgress
[...]


Puis on crée l'objet et on initialise l'affichage avant le début de la boucle :
- Création de l'objet avec le mot-clé New.
- Définition des bornes de la barre de progression : ProgressMin et ProgressMax.
- Initialisation de la valeur de la progression : ProgressValue.
- Initialisation du texte d'information générale : GeneralInfo.
- Affichage du formulaire avec la propriété Visible.

Initialisation de l'objet
Sélectionnez

[...]
On Error GoTo Gestion_Erreurs
' Ouverture et initialisation du formulaire d'attente
Set oProgress = New clProgress
oProgress.ProgressMin = 1
oProgress.ProgressMax = lNbIterations
oProgress.ProgressValue = 0
oProgress.GeneralInfo = "Veuillez patienter durant le traitement ... "
oProgress.Visible = True
' Boucle de traitement
[...]


Pour mettre à jour le formulaire durant le traitement nous allons :
- Définir ProgressValue et ProgressInfo en fonction de l'avancement du traitement.
- Rafraichir le formulaire à l'aide de Repaint.

Mise à jour de l'avancement
Sélectionnez

[...]
' Boucle de traitement
For lCptIteration1 = 1 To lNbIterations
    For lCptIteration2 = 1 To 1000
        lString = Chr((lCptIteration1 + lCptIteration2) Mod 256)
    Next
    ' Met à jour la progression
    oProgress.ProgressValue = lCptIteration1
    ' Met à jour l'étiquette
    oProgress.ProgressInfo = "Traitement en cours ... " & Format(oProgress.ProgressPercent, "00%")
    ' Repeint le formulaire
    oProgress.Repaint
Next
[...]


On note ici que l'utilisation du module de classe est aisée : il suffit de donner à ProgressValue la valeur du compteur.
Comme on a défini au début les valeurs mini et maxi de la progression, le calcul est automatique et la barre de progression est mise à jour.
On récupère également le pourcentage à travers la propriété ProgressPercent.

Pour terminer, en fin de procédure, on libère l'objet oProgress.
Lorsqu'on libère cet objet, l'objet formulaire qu'il contient est également libéré et le formulaire FormAttente se ferme.

Libération de l'objet
Sélectionnez

[...]
' Fermeture du formulaire d'attente
Set oProgress = Nothing
Exit Sub
Gestion_Erreurs:
    If Err.Source = "clProgress" Then
        ' Ajoutez ici un éventuel code de journalisation des erreurs (fichier log)
        ' XXXXXX
        ' Puis on continue le traitement
        Resume Next
    Else
        MsgBox "Erreur dans le traitement  " & Err.Number & ", " & Err.Description, vbCritical
    End If
    Set oProgress = Nothing
End Sub



III-E. Conclusion

Avec l'utilisation d'un module de classe, le code est mieux structuré et y gagne en clarté.

Téléchargez la base Access de ce chapitre au format ACCESS 2000

IV. Ajout d'une animation

Dans ce chapitre nous allons ajouter une animation.

Image non disponible  Voici le rendu du formulaire avec l'animation :

Image non disponible

Image non disponible  Et voici un schéma récapitulatif de ce que nous allons implémenter :

Image non disponible

IV-A. Préparation des images

Recherchez d'abord un gif animé.
Par exemple dans les Cliparts de Microsoft.
Voici le gif animé que nous allons utiliser :

Image non disponible

Access n'étant pas capable d'afficher nativement une image animée (gif animé), nous allons la décomposer en images simples.

On peut trouver des petits utilitaires gratuits pour extraire les images d'un gif animé.

Si vous n'en trouvez pas, en voici un créé avec Access et Gdiplus :
Téléchargez l'extracteur d'images de gif animé au format ACCESS 2000
Si vous utilisez Window XP, Gdiplus est déjà intégré.
Sinon téléchargez la librairie et placez le fichier gdiplus.dll dans le même répertoire que le fichier GifExtract.mdb.

J'ai extrait pour le tutoriel les images à l'aide de GifExtract.mdb, au format PNG :
Image non disponibleImage non disponibleImage non disponibleImage non disponible

IV-B. Ajout des images au formulaire

Ajoutez des contrôles images de cette manière :

Image non disponible

Image non disponible  Le contrôle ImgProgress est un contrôle image standard.
C'est dans ce contrôle que s'affichera l'animation.
Choisissez un mode d'affichage égal à Zoom (onglet de propriétés Format).
Ne mettez pas d'image dans ce contrôle :
En fonction de la version d'Access utilisée, vous devrez peut-être choisir une image quelconque puis vider la propriété Image de l'onglet Format.
Image non disponible  Les contrôles ImgFrameXX sont des contrôles image standards.
Chacun de ces contrôles contient une image de l'animation.
Insérez chacune des images (4 images dans l'exemple) dans un contrôle séparé nommé ImgFrameXX, en faisant varier XX de 01 jusqu'au nombre d'images total.
Modifiez la propriété Visible (onglet Format) de chacune de ces images, car elles ne sont pas directement affichées.

Pour visualiser l'animation dans le contrôle ImgProgress, on copiera chacune des images à tour de rôle dans ce contrôle.
Pour copier une image d'un contrôle à un autre, nous utiliserons la propriété PictureData des contrôles image.

IV-C. Gestion de l'animation

J'entrevois deux solutions pour afficher l'animation :
- la première solution serait de gérer un pas de mise à jour de l'image, de la même manière qu'on gère un pas de mise à jour de la barre de progression.
- la deuxième solution que nos allons développer est d'utiliser la minuterie du formulaire.

En utilisant la minuterie du formulaire (merci à Domi2 pour l'idée), on obtient une animation indépendante de l'avancement du traitement.
L'image sera mise à jour toutes les xxx millisecondes.

IV-C-1. Préparation des variables et propriétés

Dans le module de classe clProgress, ajoutons les variables et propriétés nécessaires.

L'intervalle de minuterie est stocké dans le formulaire, nous n'avons donc pas besoin de variable supplémentaire pour cette valeur.
Mais il est nécessaire de créer une propriété AnimationTimer dans le module de classe pour définir cette minuterie.

Nous avons besoin de définir le préfixe des images utilisées (ImgFrame dans l'exemple).
Ce sera une propriété AnimationPrefix.
Nous stockerons non pas la valeur de ce préfixe mais la liste des noms de contrôles dans une collection gAnimationImages.

Enfin nous ajoutons une variable gAnimationValue qui contient le numéro de l'image en cours.
Cette variable sera incrémentée de 1 jusqu'au nombre d'images dans la collection.

Déclaration des variabes en en-tête du module clProgress
Sélectionnez

Private gAnimationValue As Long
Private gAnimationImages As Collection


Dans la propriété AnimationPrefix, nous allons rechercher les contrôles images dont le nom commence par ce préfixe.
Nous ajoutons ces noms de contrôle à la collection, en les rangeant par ordre alphabétique.

Propriété AnimationPrefix du module clProgress
Sélectionnez

'------------------------------------------------------------
' Prefixe des images d'animation
'------------------------------------------------------------
Public Property Let AnimationPrefix(pPrefix As String)
Dim loCtrl As Control
Dim lCpt As Long
On Error GoTo Gestion_Erreurs
    ' Nouvelle collection
    Set gAnimationImages = New Collection
    ' Si le formulaire est défini
    If Not oProgressForm Is Nothing Then
        ' Si le préfixe n'est pas vide
        If pPrefix <> "" Then
            ' Parcourt les contrôles du formulaire
            For Each loCtrl In oProgressForm.Controls
                ' Si c'est un control image
                If TypeOf loCtrl Is Access.Image Then
                    ' Si le nom du contrôle commence par le préfixe
                    If loCtrl.Name Like pPrefix & "*" Then
                        ' Si c'est la première image on l'ajoute
                        If gAnimationImages.Count = 0 Then
                            gAnimationImages.Add loCtrl.Name, loCtrl.Name
                        Else
                            ' Sinon on ajoute l'image rangée par ordre alphabétique
                            For lCpt = 1 To gAnimationImages.Count
                                If loCtrl.Name > gAnimationImages.Item(lCpt) Then
                                    gAnimationImages.Add loCtrl.Name, loCtrl.Name, , gAnimationImages.Item(lCpt)
                                    Exit For
                                End If
                            Next
                        End If
                    End If
                End If
            Next
        End If
    End If
Exit Property
Gestion_Erreurs:
Err.Raise Err.Number, "clProgress"
End Property


Dans cette propriété on crée donc une nouvelle collection.
Puis on remplit la collection avec les noms des contrôles images composant l'animation.

Notez qu'on ne stocke pas la valeur du préfixe.
On utilise la valeur donnée en paramètre pour remplir la collection.
Ensuite on n'utilise plus ce préfixe, on utilisera directement les noms des contrôles dans la collection.

Il faut enfin initialiser la collection d'images lors du chargement de formulaire.
Par défaut on recherche les images qui commencent par ImgFrame, celles de notre formulaire par défaut FormAttente.

Initialise la collection d'images dans la procédure Class_Initialize
Sélectionnez

[...]
    ' Création d'un nouvel objet formulaire
    Set oProgressForm = New Form_FormAttente
    ' Images d'animation
    AnimationPrefix = "ImgFrame" ' <= Ajout de l'initialisation de la collection d'images
[...]


Passons maintenant à la propriété AnimationTimer.
Cette propriété est simple, elle met à jour ou lit la valeur de la minuterie du formulaire.

Propriété AnimationTimer du module clProgress
Sélectionnez

'------------------------------------------------------------
' Minuterie de l'animation
'------------------------------------------------------------
Public Property Get AnimationTimer() As Long
On Error GoTo Gestion_Erreurs
    AnimationTimer = oProgressForm.TimerInterval
Exit Property
Gestion_Erreurs:
Err.Raise Err.Number, "clProgress"
End Property
Public Property Let AnimationTimer(pTimer As Long)
On Error GoTo Gestion_Erreurs
    oProgressForm.TimerInterval = pTimer
Exit Property
Gestion_Erreurs:
Err.Raise Err.Number, "clProgress"
End Property


Les propriétés sont désormais définies, il va falloir s'occuper de la minuterie.

IV-C-2. Utilisation de la minuterie du formulaire dans le module de classe

La minuterie s'exécute dans le formulaire, mais on souhaite n'écrire du code que dans le module de classe clProgress.
Il faut donc "capturer" l'évenement Sur Minuterie du formulaire dans ce module de classe.

Pour pouvoir accéder aux événements du formulaire dans le module de classe clProgress, il faut tout d'abord ajouter WithEvents à la déclaration de l'objet oProgressForm.

Modification de la déclaration du formulaire en en-tête du module
Sélectionnez

Private WithEvents oProgressForm As Access.Form


On a alors accès à l'objet oProgressForm dans la liste déroulante en haut à gauche de la page de code.

Image non disponible

Choisissez oProgressForm dans la liste, puis ouvrez la liste en haut à droite.

Image non disponible

Choisissez Timer dans cette liste.

La procédure de gestion de la minuterie est ajoutée :

Minuterie
Sélectionnez

Private Sub oProgressForm_Timer()

End Sub


Attention : cela ne suffit pas pour capturer le message dans le module de classe.
Il faut également préciser dans l'événement du formulaire qu'il doit exécuter une procédure événementielle sur minuterie.

Dans les propriétés du formulaire, onglet Evénement, définissez Sur Minuterie = [Procédure événementielle].

Image non disponible

Maintenant le code défini dans la procédure oProgressForm_Timer s'exécutera sur minuterie en fonction de l'intervalle défini.

Pour animer l'image, c'est assez simple.
Nous avons défini une variable gAnimationValue que nous allons incrémenter à chaque passage dans le code de la minuterie.
Cette valeur est l'indice de la collection d'image gAnimationImages : il doit donc varier entre 1 et le nombre d'images dans la collection.
On utilise l'opérateur Mod (modulo) pour rester entre ces valeurs.

Ensuite nous affectons à l'image ImgProgress les données d'image du contrôle de la collection correspondant à cet indice.
Pour transférer une image d'un contrôle à un autre, nous utilisons la propriété PictureData.

Animation sur minuterie
Sélectionnez

'-----------------------------------------------------------
' Sur minuterie du formulaire
'-----------------------------------------------------------
Private Sub oProgressForm_Timer()
On Error GoTo Gestion_Erreurs
    ' Test si au moins une image dans la collection
    If gAnimationImages.Count > 0 Then
        ' Avance d'une image
        gAnimationValue = 1 + gAnimationValue Mod gAnimationImages.Count
        ' Affiche l'image
        oProgressForm.ImgProgress.PictureData = oProgressForm.Controls(gAnimationImages.Item(gAnimationValue)).PictureData
    End If
Exit Sub
Gestion_Erreurs:
Err.Raise Err.Number, "clProgress"
End Sub


Nous souhaitons ne pas attendre le premier déclenchement de la minuterie et ainsi afficher la première image dès l'activation de la minuterie.
Dans la propriété Let AnimationTimer, ajoutons un appel à la procédure de minuterie :

 
Sélectionnez

[...]
    oProgressForm.TimerInterval = pTimer
    Call oProgressForm_Timer ' <= Appel la minuterie sans attendre
[...]


IV-D. Activation de l'animation

Pour lancer l'animation, il faut définir l'intervalle de minuterie à l'aide de la propriété AnimationTimer. Dans le formulaire de test, dans la partie sur l'initialisation du formulaire d'attente et juste avant de rendre visible le formulaire :

Lance l'animation
Sélectionnez

[...]
oProgress.AnimationTimer = 500 ' <== Lance l'animation
oProgress.Visible = True
' Boucle de traitement
[...]


On définit ici un intervalle de 500 millisecondes.
On ne définit pas la propriété AnimationPrefix qui n'est pas utile si on n'utilise pas un formulaire personnalisé avec des noms de contrôles différents.

IV-E. Conclusion

Il est donc facile d'ajouter une animation au formulaire.
Il suffit de décomposer l'animation en images simples et de définir le préfixe des contrôles utilisés pour stocker ces images.
Le module de classe gère tout sans avoir besoin d'ajouter ou de modifier du code dans la boucle de traitement.

Téléchargez la base Access de ce chapitre au format ACCESS 2000

V. Intrusivité du formulaire d'attente


Vous l'avez sans doute remarqué, l'affichage du formulaire d'attente ralenti le traitement.
Pour un traitement dont les itérations sont assez rapides, ne mettez pas à jour le formulaire à chaque itération.
A moins bien sûr qu'il soit nécessaire d'afficher un texte d'avancement différent à chaque itération.
Sinon utilisez par exemple l'opérateur modulo comme exliqué dans le chapitre II.

Pour un traitement rapide, qui ne dure par exemple que 10 ou 20 secondes, on peut même envisager de n'afficher qu'un texte statique et ne pas mettre à jour le formulaire durant le traitement.

VI. Conclusion

Nous avons vu comment afficher un formulaire afin de renseigner l'utilisateur sur le traitement en cours.
Il serait à présent intéressant d'améliorer ce formulaire, par exemple en :
- gérant 2 barres de progression.
- et en encapsulant le formulaire et les modules de code dans une librairie réutilisable.

Merci à l'équipe Office pour son aide en conseils et relectures.

VII. Téléchargements


Retrouvez les bases de données de ce tutoriel à la fin de chaque chapitre.

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

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 © 2009 Thierry GASPERMENT. 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. Droits de diffusion permanents accordés à Developpez LLC.