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 :
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.
Voici le rendu de ce formulaire :
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 :
Dans l'onglet Autres :
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. |
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 :
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.
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.
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.
Le contrôle lblProgressBack est une étiquette.
Donnez lui un style de fond standard et une couleur de fond bleu clair par exemple.
Cette étiquette servira de fond à la barre d'avancement.
Le contrôle lblProgressBar est une étiquette.
Donnez lui un style de fond standard et une couleur de fond bleu foncé par exemple.
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.
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.
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.
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.
[...
]
' 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 :
On
Error
GoTo
Gestion_Erreurs
[...
]
Exit
Sub
Gestion_Erreurs
:
MsgBox
"Erreur lors du traitement, n° "
&
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▲
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, n° "
&
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.
Dans ce chapitre, nous ne changerons rien au rendu de ce formulaire :
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
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).
Le formulaire devient alors un objet de classe, et on peut le voir dans l'explorateur d'objets de VBA :
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 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.
'------------------------------------------------------------
' 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
'------------------------------------------------------------
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 :
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 n° "
&
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 :
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.
[...
]
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.
[...
]
' 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.
[...
]
' 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 n° "
&
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.
Voici le rendu du formulaire avec l'animation :
Et voici un schéma récapitulatif de ce que nous allons implémenter :
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 :
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 :
IV-B. Ajout des images au formulaire▲
Ajoutez des contrôles images de cette manière :
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.
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.
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.
'------------------------------------------------------------
' 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.
[...
]
' 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.
'------------------------------------------------------------
' 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.
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.
Choisissez oProgressForm dans la liste, puis ouvrez la liste en haut à droite.
Choisissez Timer dans cette liste.
La procédure de gestion de la minuterie est ajoutée :
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].
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.
'-----------------------------------------------------------
' 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 :
[...
]
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 :
[...
]
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.