VBA - Appel d'une macro dans une autre macro

Décembre 2016




Introduction

Pour appeler une macro depuis une autre, il va nous falloir distinguer plusieurs cas. L'appel d'une Sub ou d'une fonction ne se fait pas tout à fait de la même façon. De plus, il faudra étudier les cas à part de l'appel d'une procédure événementielle et de l'appel d'une procédure contenue dans un autre classeur.

Appel d'une Sub depuis une Sub

Sans paramètres

Les deux Sub sont dans le même Module, et n'ont pas de paramètres. La Macro2 appelle la Macro1 :

Sub Macro1()
    MsgBox "Hello world!"
End Sub

Sub Macro2()
    Call Macro1
End Sub


On peut éventuellement se passer de l'instruction Call :

Sub Macro1()
    MsgBox "Hello world!"
End Sub

Sub Macro2()
    Macro1
End Sub


Je ne le recommande cependant pas. En effet, cette instruction Call rend le code beaucoup plus lisible. En la lisant, le programmeur voit tout de suite qu'il s'agit de l'appel d'une procédure.

Les deux Sub sont dans des Modules différents, et n'ont pas de paramètres. Il n'y a pas d'obligation à spécifier le module. L'exemple précédent fonctionnera de la même manière. Mais il vous faut penser, dès la création de votre code, à sa maintenance. Le fait de préciser dans quel Module se trouve la procédure appelée vous fera gagner du temps de recherche inutile lors d'éventuels débogages.
Donc si la Macro1 est dans le Module1 et la Macro2 dans le Module2 :

Sub Macro1()
    MsgBox "Hello world!"
End Sub

Sub Macro2()
    Call Module1.Macro1
End Sub

Avec paramètres

La méthode est identique. Il suffit d'ajouter les paramètres entre parenthèses. Attention toutefois au typage de ces paramètres. Pour ne pas générer d'erreur (de type 13). Si votre fonction attend un paramètre typé en String, ne lui envoyez pas un Double...


Avec l'instruction Call
Sub Macro1(Nb1 As Long, Nb2 As Long)
    Range("A1") = Nb1
    Range("A2") = Nb2
End Sub

Sub Macro2()
    Call Macro1(18254, 654897)
End Sub


Sans l'instruction Call
Sub Macro1(Nb1 As Long, Nb2 As Long)
    Range("A1") = Nb1
    Range("A2") = Nb2
End Sub

Sub Macro2()
    Macro1 18254321, 654897
End Sub


A noter ici l'absence de parenthèses.
Si les macros sont dans des Modules différents, même sanction :
Sub Macro2()
    Call Module1.Macro1(918254321, 654897)
End Sub

Appel d'une Function depuis une Sub

Déjà qu'est-ce qu'une fonction ? Qu'est-ce qui la différencie d'une Sub ?
Une fonction (Function) est une instruction qui dépend de paramètres et qui retourne une valeur. Une Sub est également une instruction dépendante de paramètres (éventuels) mais qui ne retourne rien.
Exemple de Function :
Function Addition(Nb1 As Double, Nb2 As Double) As Double
Addition = Nb1 + Nb2
End Function


On voit ici que notre Function est déclarée As Double (cette déclaration de type est facultative). La valeur qu'elle retournera sera donc de type Double. Comme une fonction retourne une valeur, il va falloir, dans la Sub appelante, prévoir le stockage de cette valeur. Cela peut être indifféremment dans une cellule Excel, dans une variable (attention de déclarer cette variable dans le bon type), dans un contrôle etc... Ici, nous allons la « recevoir » dans une variable de même type (Double).
 Sub Macro2()
  Dim Somme As Double
  Somme = Addition(1234.56, 654.32)
  MsgBox Somme
End Sub

Function Addition(Nb1 As Double, Nb2 As Double) As Double
Addition = Nb1 + Nb2
End Function 


A noter ici l'obligation de passer les paramètres entourés de parenthèses, et séparés par des virgules.

Appel d'une Function depuis une Function

Euh... Rien à dire, si ce n'est : même principe !

Exemple avec stockage du résultat dans une variable intermédiaire :
 Sub Macro2()
  Dim Somme As Double
  Somme = Addition(1234.56, 654.32)
  MsgBox Somme
End Sub

Function Addition(Nb1 As Double, Nb2 As Double) As Double
  Dim VarNb As Double
  VarNb = MultiplieParDeux(Nb2)
  Addition = Nb1 + VarNb
End Function

Function MultiplieParDeux(Nb As Double) As Double
  MultiplieParDeux = Nb * 2
End Function


Même exemple sans variable intermédiaire :
 Sub Macro2()
  Dim Somme As Double
  Somme = Addition(1234.56, 654.32)
  MsgBox Somme
End Sub

Function Addition(Nb1 As Double, Nb2 As Double) As Double
  Addition = Nb1 + MultiplieParDeux(Nb2)
End Function

Function MultiplieParDeux(Nb As Double) As Double
  MultiplieParDeux = Nb * 2
End Function

Appel d'une procédure événementielle

Qu'est-ce qu'une procédure événementielle ?
Une procédure événementielle est une instruction qui se déclenche automatiquement lors d'une action de l'utilisateur sur un objet. L'objet peut être aussi bien une feuille, un classeur, un contrôle... L'événement peut être l'ouverture, la fermeture d'un classeur, le changement de feuille, le choix dans une liste, la saisie dans un contrôle etc... Les procédures événementielles permettent d'intercepter ces actions afin de lancer automatiquement des procédures que vous avez créées.
Exemple :
Nous souhaiterions, lorsqu'il y a une modification de la valeur de la cellule A1, que s'affiche alors, en B1, « pas mal ! » si A1 est supérieur à 10.
Pour cela, allons dans le code du Module de la feuille concernée. Clic droit sur l'onglet de la feuille / Visualiser le code.
Dans la fenêtre de code, insérez ceci :
Private Sub Worksheet_Change(ByVal Target As Range)
'Si la cellule concernée par le changement n'a pas pour adresse A1 => on quitte
If Target.Address <> "$A$1" Then Exit Sub
'Si la cellule (donc A1) est > 10 alors on place "Pas mal!" en B1
If Target.Value > 10 Then Target.Offset(0, 1) = "Pas mal!" Else Target.Offset(0, 1) = "Pas terrible!"
End Sub


Vous pouvez déjà tester l'effet en modifiant la valeur de la cellule A1 de la feuille concernée.

Nota : Vous me direz : « suffit de changer, depuis notre macro, la valeur de A1 et ça va fonctionner... » Oui, vous avez raison. Mais ce n'est qu'un exemple !!!

Avec l'instruction Call

Si votre procédure appelante est située dans le même module que votre procédure événementielle, il suffira de l'appeler simplement, comme ceci :

Private Sub Worksheet_Change(ByVal Target As Range)
'Si la cellule concernée par le changement n'a pas pour adresse A1 => on quitte
If Target.Address <> "$A$1" Then Exit Sub
'Si la cellule (donc A1) est > 10 alors on place "Pas mal!" en B1
If Target.Value > 10 Then Target.Offset(0, 1) = "Pas mal!" Else Target.Offset(0, 1) = "Pas terrible!"
End Sub

Sub MaMacro()
Dim monRange As Range
Set monRange = Sheets("Feuil1").Range("A1")
Call Worksheet_Change(monRange)
End Sub


Par contre, si vous placez votre procédure appelante dans le Module1 par exemple, quoique vous fassiez, même en la déclarant comme Public au lieu de Private, vous aurez toujours un message d'erreur : Erreur de compilation Sub ou Fonction non définie. En clair VBA ne trouve pas l'accès à la procédure événementielle.

Comment lui donner cet accès ?

Avec la fonction CallByName

La syntaxe :
CallByName(Objet, NomProcédure, TypeAppel, Args())

Objet : Obligatoire. De type Object. Désigne l'objet concerné par la procédure.
NomProcédure : Obligatoire. De type String. Représente le nom de la procédure appelée.
TypeAppel : Obligatoire. De type CallType. Peut être Method, Set, Let ou Get.
Args() : Facultatif. Tableau des paramètres à passer à la fonction appelée.

Dans notre exemple, plaçons maMacro dans le Module1 et essayons d'appeler la Sub Worksheet_Change. Avant cela, il est indispensable de déclarer notre procédure événementielle à un autre niveau. En effet, déclarée comme Private, elle ne pourra servir que dans le Module de la feuille. Enlevons donc le mot Private, pour obtenir, dans le Module de la feuille :

Sub Worksheet_Change(ByVal Target As Range)
'Si la cellule concernée par le changement n'a pas pour adresse A1 => on quitte
If Target.Address <> "$A$1" Then Exit Sub
'Si la cellule (donc A1) est > 10 alors on place "Pas mal!" en B1
If Target.Value > 10 Then Target.Offset(0, 1) = "Pas mal!" Else Target.Offset(0, 1) = "Pas terrible!"
End Sub


Plaçons maintenant la Sub maMacro dans le Module2.

 Sub MaMacro()
Dim monRange As Range
Set monRange = Sheets("Feuil1").Range("A1")
CallByName Worksheets("Feuil1"), "Worksheet_Change", VbMethod, monRange
End Sub


Autre exemple :
Lors du clic sur un bouton d'un UserForm (UserForm2), je souhaite déclencher l'événement Combobox1_Change d'une liste déroulante située dans un UserForm1...

 Private Sub CommandButton1_Click()
CallByName UserForm1, "ComboBox1_Change", VbMethod
End Sub


En ayant pris soin, au préalable, de supprimer le Private de l'événement concerné : Sub ComboBox1_Change()

Appel d'une procédure dans un autre classeur

Dans ce cas, il conviendra d'utiliser la méthode Run. On précisera, notamment, le classeur contenant la procédure à appeler ainsi que le nom de la procédure.

Cas de l'appel d'une Sub

Deux choix :
Soit le classeur est préalablement ouvert. Dans ce cas, inutile d'en préciser le chemin d'accès :

Sub TestRun()
Application.Run "'Classeur1.xlsm'!Module2.Macro2"
End Sub


Soit le classeur est fermé. Auquel cas, le chemin d'accès complet doit être référencé :

Sub TestRun()
Application.Run "'C:\Users\franck\Desktop\Classeur1.xlsm'!Module2.Macro2"
End Sub


Nota : Notez la présence impérative des quote (apostrophes) autour du nom du fichier.

Cas de l'appel d'une Function

Comme l'appel d'une fonction depuis le même classeur, il va falloir prévoir un « lieu de stockage ». Ici une variable. Il va falloir également communiquer depuis la Sub appelante, tous les paramètres à transmettre à la fonction appelée.

Reprenons notre Function Addition située dans le Classeur1.xlsm :

Function Addition(Nb1 As Double, Nb2 As Double) As Double
Addition = Nb1 + Nb2
End Function 


Pour l'appeler dans notre procédure Principale() située dans le classeur Classeur2.xlsm, il nous faut le code suivant :

Classeur déjà ouvert
Sub Principale ()
Dim Somme As Double
  Somme = Run("'Classeur1.xlsm'!Module2.Addition", 1234.56, 654.32)
  MsgBox Somme
End Sub



Classeur fermé
Sub Principale ()
Dim Somme As Double
  Somme = Run("'C:\Users\franck\Desktop\Classeur1.xlsm'!Module2.Addition", 1234.56, 654.32)
  MsgBox Somme
End Sub


Nota : Important. Si votre classeur était préalablement fermé, il sera ouvert après l'exécution de cette macro. Pensez donc à le refermer... par code !

Notre dernier exemple, en ajoutant la fermeture du classeur, deviendrait donc :

Sub TestRun()
Dim Somme As Double
  Somme = Run("'Classeur1.xlsm'!Module2.Addition", 1234.56, 654.32)
  Workbooks("Classeur1.xlsm").Close False
  MsgBox Somme
End Sub

Conclusion

N'hésitez pas à me faire part de toutes remarques à propos de ces quelques explications. En cas de questions, n'hésitez pas à les poser sur le forum adéquat.

A voir également :

Ce document intitulé «  VBA - Appel d'une macro dans une autre macro  » issu de CommentCaMarche (www.commentcamarche.net) est mis à disposition sous les termes de la licence Creative Commons. Vous pouvez copier, modifier des copies de cette page, dans les conditions fixées par la licence, tant que cette note apparaît clairement.