"FAQ : VBA - Appel d'une macro dans une autre macro"
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:UsersfranckDesktopClasseur1.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:UsersfranckDesktopClasseur1.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.