IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Voir le flux RSS

Pierre Fauconnier

VBA: Code événementiel qui appelle un code événementiel? JAMAIS!!

Note : 2 votes pour une moyenne de 5,00.
par , 01/01/2020 à 08h59 (1192 Affichages)
Salut.

Sur les forums, je vois régulièrement des solutions proposées avec du code événementiel qui appelle du code évenementiel. A NE JAMAIS FAIRE!!, si toutefois l'on souhaite respecter une architecture professionnelle de son code VBA. D'ailleurs, aucun code ne devrait appeler du code événementiel. Le code événementiel est prévu pour être exécuté à la survenance d'un événement. Ce n'est pas une procédure "classique" que l'on peut appeler par code, même si c'est parfois techniquement possible. J'explique dans ce billet pourquoi un code événementiel ne devrait jamais rien exécuter, mais appeler des procédures ou fonctions dont le rôle sera de "faire le job".

Regardons le code qui suit, issu d'un formulaire contenant deux boutons, le clic sur le second appelant la procédure événementielle du clic sur le premier...
Code vba : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
Private Sub CommandButton1_Click()
  ...
  ...
  ...
End Sub
 
Private Sub CommandButton2_Click()
  CommandButton1_Click
  ...
  ...
  ...
End Sub

Jusqu'ici, tout va bien. Le clic sur le bouton 2 appelle le code du bouton 1 puis exécute ses propres lignes de code. Mais il se pourrait bien que l'évolution de votre application nécessite que le code du bouton 1 soit modifié. Par exemple, on pourrait vouloir informer l'utilisateur qui a cliqué sur le bouton 1 que le process est terminé. Il suffit d'ajouter un msgbox à la fin de la procédure événementielle du bouton 1. Mais on comprend vite à la lecture du code suivant que cela va être bloquant pour le bouton 2, puisque son process va s'arrêter sur l'affichage du message, attendant une action de l'utilisateur pour continuer... Vous avez dit "architecture professionnelle"?
Code vba : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
Private Sub CommandButton1_Click()
  ...
  ...
  ...
  MsgBox "Terminé"
End Sub
 
Private Sub CommandButton2_Click()
  CommandButton1_Click
  ...
  ...
  ...
End Sub

Une architecture PRO interdit le chaînage des procédures événementielles. Dès le début de votre projet, codez toujours de façon professionnelle! Ca ne coûte pas plus cher, ça facilite la maintenance et l'évolution de votre code. Voici une proposition de code qui vous simplifiera la vie
Code vba : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Sub Action1()
  ...
  ...
  ...
End Sub
 
Sub Action2()
  ...
  ...
  ...
End Sub
 
Private Sub CommandButton1_Click()
  Action1
End Sub
 
Private Sub CommandButton2_Click()
  Action1
  Action2
End Sub

Ce code réalise exactement la même chose que le tout premier code donné en début de billet, mais respecte l'architecture PRO qui veut qu'un événement "ne fasse rien" => il appelle une ou plusieurs procédures à la suite et informe éventuellement l'utilisateur par des msgbox. Si maintenant, l'on souhaite ajouter un message de bonne fin après le clic sur le bouton 1, il suffit d'ajouter la ligne à la fin du code événementiel du bouton 1 (pas à la fin de Action1!). Ainsi, le fonctionnement du bouton 2 n'est pas affecté par cette modification qui ne le concerne en rien
Code vba : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
Private Sub CommandButton1_Click()
  Action1
  MsgBox "Terminé"
End Sub
 
Private Sub CommandButton2_Click()
  Action1
  Action2
End Sub


Notion de contrat
Cette façon de coder met en œuvre et respecte la notion de CONTRAT. Lorsqu'une procédure ou une fonction est appelée par une autre, il y a un contrat établi entre elles. Ce contrat stipule ce qui doit être passé à la procédure/fonction, comment elle se comportera et ce qu'elle produira comme valeur de retour (si c'est une fonction). Dès que deux procédures/fonctions sont liées parce que l'une appelle l'autre, ce contrat NE PEUT PLUS ETRE MODIFIE. On comprend vite la galère que représenterait la maintenance d'un code si on modifiait le contrat d'une fonction, appelée parfois par des centaines d'autres. Imaginez que la fonction DateSerial demande un jour les arguments dans un autre ordre que l'ordre actuel. C'est la panique.

Ici, c'est pareil, en ajoutant le MsgBox dans la procédure événementielle du bouton 1, on modifie son comportement, et donc le contrat qu'elle aurait avec d'autres procédures/fonctions qui l'utiliseraient. Cela ne peut se faire dans dégâts. On n'a donc pas le choix de sortir le code applicatif de l'événement pour le placer dans une procédure "qui ne fait que ça". Après, on peut ajouter le MsgBox dans la procédure du bouton 1 sans modifier le comportement, et donc le contrat, de la procédure applicative Action1. Les contrats sont respectés et l'impact des modifications est ainsi très réduit, voire nul.

Le contrat d'un code événementiel est de recevoir éventuellement certains arguments (la plage modifiée d'un événement Change, par exemple), d'appeler du code applicatif et éventuellement informer l'utilisateur par msgbox. En gros, ça s'arrête là.


Gestion des erreurs
De même, il est sain de prévoir une gestion d'erreurs sur chaque événement codé, de manière à ne pas laisser l'utilisateur en rade si un problème non prévisible survient en cours de route. Un événement sur un bouton est en fait la procédure qui est au bas de la pile d'appel, puisqu'il est déclenché par l'utilisateur. C'est donc l'ultime endroit où l'on peut placer une gestion d'erreurs (je préfèrerais le terme Exception) pour éviter d'entrer en débogage ou de planter l'appli. Toute procédure événementielle déclenchée par l'utilisateur devrait donc être munie d'une gestion d'erreur, même minimaliste.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
Private Sub CommandButton1_Click()
  On Error GoTo EndHandler
  
  Action1
  MsgBox "Terminé"
  
EndHandler:
  If Err <> 0 Then MsgBox "Une erreur est survenue:" & vbLf & Err.Description, vbExclamation, "Erreur " & Err.Number
End Sub
On comprend aisément que si la procédure du bouton2, appelée par l'utilisateur, et donc normalement, en bonne architecture, munie d'une gestion d'erreurs, appelle l'événement du bouton1, il risque d'y avoir téléscopage entre les deux gestions d'erreurs.

Conclusions
Un code événementiel n'en appelle jamais un autre. Respecter cette règle de base d'une architecture professionnelle vous permettra de produire un code propre, facilement maintenable, évolutif à moindres frais, et dont le comportement sera contrôlé et prévisible.

Durant cette année 2020, je reviendrai régulièrement sur ces notions d'architecture, car une bonne architecture garantit une approche professionnelle de vos développements.

Envoyer le billet « VBA: Code événementiel qui appelle un code événementiel? JAMAIS!! » dans le blog Viadeo Envoyer le billet « VBA: Code événementiel qui appelle un code événementiel? JAMAIS!! » dans le blog Twitter Envoyer le billet « VBA: Code événementiel qui appelle un code événementiel? JAMAIS!! » dans le blog Google Envoyer le billet « VBA: Code événementiel qui appelle un code événementiel? JAMAIS!! » dans le blog Facebook Envoyer le billet « VBA: Code événementiel qui appelle un code événementiel? JAMAIS!! » dans le blog Digg Envoyer le billet « VBA: Code événementiel qui appelle un code événementiel? JAMAIS!! » dans le blog Delicious Envoyer le billet « VBA: Code événementiel qui appelle un code événementiel? JAMAIS!! » dans le blog MySpace Envoyer le billet « VBA: Code événementiel qui appelle un code événementiel? JAMAIS!! » dans le blog Yahoo

Mis à jour 22/06/2020 à 16h30 par Pierre Fauconnier

Catégories
VBA , MS Office , Bonnes pratiques

Commentaires

  1. Avatar de apt
    • |
    • permalink
    Merci Pierre pour ces précieux conseils