I. Introduction

I-A. Word, Macros, VBA et WordBasic

Word offre un outil puissant qui vous permet d'enregistrer des macros, c'est-à-dire des séquences d'actions servant à automatiser vos tâches habituelles ou répétitives.
L'enregistreur de macros enregistre les déplacements du curseur pour positionner le point d'insertion et définir la zone de travaiL. Lors de son exécution, la macro va refaire les mêmes actions y compris les déplacements. Cette manière de faire provient de la manière dont l'outil a été conçu. Initialement, Word est un traitement de texte et l'utilisateur l'utilise pour créer et modifier un document, il est naturel que l'utilisateur puisse voir l'endroit sur lequel il agit pour visualiser son travail et donc la majorité des actions consiste soit à ajouter, supprimer des caractères soit à appliquer des actions sur le texte sélectionné. Et historiquement, le langage de Word de macro (WordBasic) est un langage qui offre un jeu d'instructions qui miment ce fonctionnement et donc l'enregistreur de macros fournissait du code compatible avec ce fonctionnement. Par exemple, si vous déplaciez le point d'insertion de 5 caractères vers la droite, l'instruction WordBasic est "CharRight 5". On comprend que le langage Wordbasic (comme la plupart des langages de l'époque d'ailleurs) est essentiellement procédural : une instruction représente l'éxecution d'une action. L'arrivée des langages orientés objet a modifié le principe de programmation : on modifie des propriétés des objets grâce à des services qu'ils proposent. Prenons un exemple pour comprendre, en Wordbasic, pour passer le 1er mot d'un texte en gras, le programme va être :

 
Sélectionnez

DébutDocument
MotDroite 1, 1
Gras

soit pour un version anglaise

 
Sélectionnez

tartOfDocument
WordRight 1, 1
Bold

Ce qui donne en VBA par l'enregistreur de macro

 
Sélectionnez

Selection.HomeKey Unit:=wdStory
Selection.MoveRight Unit:=wdWord, Count:=1, Extend:=wdExtend
Selection.Font.Bold = wdToggle

On voit qu'on reste proche, voire très proche : on utilise l'objet Sélection pour travailler comme en WordBasic mais le code correct en VBA s'écrit

 
Sélectionnez

ActiveDocument.Words(1).Bold = True

On voit qu'on a modifié la propriété du 1er mot mais on n'avait pas besoin de le sélectionner. Attention, ceci est un exemple choisi, tout, hélàs ne se passe pas de cette façon, loin de là. Mais c'est le principe.

I-B. Enregistreur de macros

Cet enregistreur est très utile car il fournit une base du code à utiliser sans connaître l'ensemble des possibilités, y compris pour les plus experts d'entre nous mais les actions enregistrées sont directement liées à votre document et l'enregistreur de macros ne permet pas de généraliser ces séquences et ne permet pas de tout faire, en particulier l'enregistreur ne sait pas gérer les collections, les procédures ou les fonctions. De plus, l'enregistreur définit en général tous les paramètres ce qui est souvent inutile.
Ce tutoriel décrit un ensemble de règles basées sur l'expérience personnelle de l'auteur pour transformer une macro enregistrée en code VBA en vue de la généraliser pour en augmenter sa réutilisation.

I-C. Tutoriels de référence

Ce tutoriel nécessite que vous possédiez des notions essentielles sur Word et sur VBA. Vous pouvez vous référer à des tutoriels de base disponibles sur DVP :

Pour plus d'informations sur l'utilisation de l'enregistreur de macros, vous pouvez vous référer à ce tutoriel sur DVP : Comment utiliser l'enregistreur de macros

N'oubliez pas d'utiliser l'aide en ligne de Word disponible avec <F1> sur Mac ou PC et bien sûr la section Word et VBA Word de Developpez.

Attention, les exemples dans ce tutoriel ont parfois été volontairement non optimisés pour présenter des exemples plus pédagogiques.

II. Utilisation de l'enregistreur de macros

II-A. Quand doit-on utiliser l'enregistreur de macros ?

La bonne réponse est à chaque fois que vous commencez une nouvelle macro. En effet, même si vous connaissez bien le langage VBA, la méthode la plus efficace consiste à commencer par enregistrer le plus d'actions possibles pour disposer d'une base fiable puis à la mettre au point avec l'éditeur VBA et enfin à la généraliser.

II-B. Exemple de travail

Sélectionnez le texte suivant, copiez-le et collez-le dans un document Word vierge puis sauvegardez-le sous un nom et dans un emplacement de travail que vous désirez

 
Sélectionnez
Exemple de text
Cet exemple de texte est fourni par Developpez.com pour illustrer le tutoriel de passage du code de macro à du VBA
Voici un exemple de texte dans Word
Ceci est un autre exemple de texte dans Word
Ceci est un 3ième exemple de texte dans Word
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor.

II-C. Faire passse le 1er paragraphe en centré, gras et souligné

Lancez l'enregistreur de macro et saisissez "DvpTitreEnr" comme nom de macro. Rappel : lorsque l'enregistreur de macros fonctionne, la souris ne peut pas être utilisée.

  1. Placez-vous au début du document en tapant <Ctrl>+<Début>
  2. Sélectionnez le 1er paragraphe du document (donc ici "Exemple de text") en tapant <Ctrl>+<Maj>+<Flêche vers le bas>
  3. Faites passer le paragraphe en centré
  4. Faites passer le texte en gras
  5. Faites passer le texte en souligné
  6. Ajoutez le "e" pour corriger le mot et passer de "text" à "texte"

Arrêtez l'enregistreur de macro, ouvrez les macros et sélectionnez "DvpTitreEnr" puis cliquez sur "Modifier". Vous obtenez le code suivant :

 
Sélectionnez

Sub DvpTitreEnr()
'
' DvpTitreEnr Macro
'
'
  Selection.HomeKey Unit:=wdStory
  Selection.MoveDown Unit:=wdParagraph, Count:=1, Extend:=wdExtend
  Selection.ParagraphFormat.Alignment = wdAlignParagraphCenter
  Selection.Font.Bold = wdToggle
  Selection.Font.UnderlineColor = wdColorAutomatic
  Selection.Font.Underline = wdUnderlineSingle
  Selection.EndKey Unit:=wdLine
  Selection.TypeText Text:="e"
End Sub
		

Quelques explications

Selection.HomeKey Unit:=wdStory <=> on déplace le point d'insertion en début de document puisqu'on veut travailler sur le 1er paragraphe.

Selection.MoveDown Unit:=wdParagraph, Count:=1, Extend:=wdExtend <=> on sélectionne le 1er paragraphe (comme à la souris) en déplaçant le point d'insertion en fin d'un seul paragraphe en "appuyant" sur la touche "<Shift>".

Selection.ParagraphFormat.Alignment = wdAlignParagraphCenter <=> on passe la sélection (donc ici le 1er paragraphe) en paragraphe centré.

Selection.Font.Bold = wdToggle <=> on passe la sélection (donc ici le 1er paragraphe) en caractères gras s'ils ne le sont pas (ce qui est le cas ici) et en non-gras, s'ils l'étaient (mais nous on voulait passer en gras toujours) et donc si on applique une nouvelle fois la macro on repasse en normal (ce qui n'est pas ce qu'on veut, nous on veut du gras).

Selection.Font.UnderlineColor = wdColorAutomatic Selection.Font.Underline = wdUnderlineSingle <=> on passe la sélection (donc ici le 1er paragraphe) en caractères souligné avec la couleur par défaut.

Selection.EndKey Unit:=wdLine <=> on déplace le point d'insertion en fin de sélection donc on déselectionne et on passe en fin du 1er paragraphe.

Selection.TypeText Text:="e" <=> on ajoute la lettre "e" au niveau du point d'insertion et donc en fin du 1er paragraphe.

Un des principes du VBA est de ne pas utiliser l'objet Sélection qui déplace le point d'insertion mais d'utiliser les collections fournies par VBA Word.
Ces collections contiennent soit d'autres collections (on parle de hiérarchies) soit des objets directs. Ces collections sont très nombreuses et couvrent l'ensemble de la structure de ce que Word offre :

  • un document est composé de paragraphes, eux-mêmes composés de mots, eux-mêmes composés de cacaratères... (Documents > Paragraphs > Characters...)
  • un tableau est composé de lignes, elles-mêmes composées de cellules... (Tables > Rows > Cells...)

Chaque collection et chaque objet possèdent des propriétés (<=> les valeurs de l'objet) et des méthodes (<=> des actions possibles pour interagir avec Word).

Attention, tous les objets manipulés par Word ne sont pas toujours représentés par des collections. Par exemple, un paragraphe contient des caractères qui sont soit des lettres, des chiffres, des espaces, soit des signes de ponctuation... mais les collections de lettres n'existent pas en tant que telles et donc il faut apprendre à connaître le modèle de Word pour manipuler correctement ces objets et ces collections.

Ici un des codes possibles pour réaliser cette fonction est le suivant :

 
Sélectionnez

Sub DvpTitre()
  ActiveDocument.Paragraphs(1).Alignment = wdAlignParagraphCenter
  ActiveDocument.Paragraphs(1).Range.Bold = True
  ActiveDocument.Paragraphs(1).Range.Underline = wdUnderlineSingle
  ActiveDocument.Range(Start:=ActiveDocument.Paragraphs(1).Range.End - 1, End:=ActiveDocument.Paragraphs(1).Range.End - 1).InsertAfter "e"
End Sub
		

Quelques explications

ActiveDocument.Paragraphs(1).Alignment = wdAlignParagraphCenter correspond à travailler sur le document courant "ActiveDocument", à utiliser dans sa collections de paragraphs "Paragraphs" le 1er "(1)" puis à lui appliquer la mise en forme de paragraphes centré.
Jusque là c'est simple on utilise la même chose que la sélection sauf qu'on travaille directement sur l'objet concerné.

ActiveDocument.Paragraphs(1).Range.Bold = True pose le problème du fameux objet Range. Pour pouvoir travailler dans Word, on peut manipuler les collections (cf. la ligne de code précédente) mais c'est très rare de le faire directement, il faut souvent passer un objet de manipulation qui fait référence à une zone contiguë d'un document et cet objet en Word, c'est l'objet Range.
Par exemple, si vous désirez insérer une lettre en fin de paragraphe, vous vous dites que vous allez prendre la collection Paragraphs, prendre le 1er et lui ajouter "e", ce qui est très légitime mais non. Comme Word est un logiciel basé sur une interface qui exécute des actions et qui déclenchent des événements qui vont eux-mêmes pouvoir générer d'autres actions, il faut utiliser cet objet de manipulation. Ne vous inquiétez pas, il m'a fallu du temps pour le comprendre aussi. En effet, l'ajout d'un caractère peut provoquer un changement de ligne, qui va lui même provoquer un changement de page et qui va provoquer la création d'un nouvel entête/pied de page si vous étiez par exemple en début de section avec un 1ère page différente. L'ajout d'un simple retour chariot (<=> nouveau paragraphe) peut provoquer une nouvelle pagination, renuméroter des titres, des légendes, provoquer des changements de page... On voit que ces actions en cascade ne peuvent pas être un simple ajout dans une seule liste mais il faut bien demander à Word à faire la même chose que si on travaillait sur la sélection sauf qu'on veut travailler irectement sur l'objet concerné et donc on utilise alors l'objet Range. Il faut bien comprendre ces différences entre d'un côté l'objet Sélection (en gros la souris et/ou le clavier) et les collections et en VBA, pour simplifier, dites-vous que l'objet Range finalement fait le lien entre ces concepts différents.
Donc ici, on prend le 1er paragraphe du document (""ActiveDocument.Paragraphs(1)") et comme on veut lui appliquer une mise en forme (donc des actions graphiques), on utilise l'objet Range qui détermine la zone de travail (ici la zone du 1er paragraphe) donc ".Range" et puis on applique la mise en forme "gras" que l'on force ici à être toujours en gras (ce aque l'on voulait).

Ici, on va voir si l'utilisation du Range a été bien assimilée. On veut ajouter un caractère "e" en fin de parapagraphe. Le principe est donc :
ActiveDocument.Range(Start:=ActiveDocument.Paragraphs(1).Range.End - 1, End:=ActiveDocument.Paragraphs(1).Range.End - 1).InsertAfter "e".

II-D. Transformer les points du texte en paragraphes

Lancez l'enregistreur de macro et saisissez "DvpRechercherMultipleEnr" comme nom de macro.

  1. Placez-vous au début du document en tapant <Ctrl>+<Début>
  2. Effectuez une recherche sur "." suivi d'un espace
  3. Placez à la fin de la sélection
  4. Supprimez le caractère précédent et tapez un retour chariot <=> nouveau paragraphe
  5. Recommencez la recherche jusqu'à ce que vous soyez en fin de document

Rappel, les exemples dans ce tutoriel ont parfois été volontairement non optimisés pour présenter des exemples plus pédagogiques.
Ici un remplacement global aurait été une meilleure solution en Word. Mais ce n'est pas le sujet du tutoriel.

Arrêtez l'enregistreur de macro, ouvrez les macros et sélectionnez "DvpRechercherMultipleEnr" puis cliquez sur "Modifier". Vous obtenez le code suivant :

 
Sélectionnez

Sub DvpRechercherMultipleEnr()
'
' DvpRechercherMultipleEnr Macro
'
'
  	Selection.HomeKey Unit:=wdStory
    Selection.Find.ClearFormatting
    With Selection.Find
        .Text = ".^w"
        .Forward = True
        .Wrap = wdFindContinue
        .Format = False
        .MatchCase = False
        .MatchWholeWord = False
        .MatchWildcards = False
        .MatchSoundsLike = False
        .MatchAllWordForms = False
    End With
    Selection.Find.Execute
    Selection.MoveRight Unit:=wdCharacter, Count:=1
    Selection.TypeBackspace
    Selection.TypeParagraph
    Application.Browser.Next
    Selection.MoveRight Unit:=wdCharacter, Count:=1
    Selection.TypeBackspace
    Selection.TypeParagraph
    Application.Browser.Next
End Sub
		

Quelques explications

Selection.HomeKey Unit:=wdStory <=> on déplace le point d'insertion en début de document puisqu'on veut travailler sur le 1er paragraphe.

Selection.Find.ClearFormatting <=> On purge les options de recherche.

With Selection.Find <=> Cette syntaxe permet d'éviter de dupliquer les mêmes débuts d'instructions jusqu'au prochain "End With".

.Text = ".^w"
.Replacement.Text = ".^p" <=> on remplace ".^w" par ".^p" c'est-à-dire tous les points suivis de n'importe quel type d'espace par un point suivi par un nouveau paragraph (cf. tutoriel sur la recherche et le remplacement dans Word).

.Forward = True <=> Définit le sens de la recherche ici vers le bas du document.

.Wrap = wdFindContinue <=> Définit si on s'arrête en fin de document ou si on reprend à partir du début ici on continue en fin de doc.

.Format = False <=> Définit si on utilise les options de formatage (gras/italique, couleur de caractères, police, titre...).

.MatchCase = False <=> Définit si la recherche est sensible à la casse ici on recherche indifférement sur les majuscules et les minusucles.

.MatchWildcards = False <=> Définit si la recherche utilise les caractères génériques ici on recherche sans caractère générique.

.MatchSoundsLike = False <=> Définit si la recherche est phonétique ici on recherche sans cet attribut.

.MatchAllWordForms = False <=> Définit si la recherche porte sur toutes les formes du mot ici on recherche sans cet attribut.

End With <=> Marque la fin de regroupement des instructions.

Selection.Find.Execute <=> On exécute la recherche.

Selection.MoveRight Unit:=wdCharacter, Count:=1 <=> On se déplace d'un caractère vers la droite. Mais ici on sait même pas si on a réussi à trouver le texte recherché.

Selection.TypeBackspace <=> On efface le caractère précédent. Attention, si on a trouvé, il n'y a pas de problème sinon on essaie d'effacer le caractère précédent mais n'importe lequel.

Selection.TypeParagraph <=> On insère un paragraphe.

Application.Browser.Next <=> On utilise l'explorateur pour déplacer le point d'insertion vers la prochaine occurrence de la recherche. Cet outil est accessible dans le bas de la barre de défilement verticale et permet de recherche la prochaine occurrence ou la suivante.

Selection.MoveRight Unit:=wdCharacter, Count:=1
Selection.TypeParagraph
Selection.TypeParagraph
Application.Browser.Next <=> cf. plus haut.

L'un des problèmes de l'enregistreur de macro est sa verbosité : il définit tous les paramètres y compris ceux par défaut et rend le code plus lourd donc moins lisible. Vous pouvez les conserver, surtout si vous ne connaissez pas le fonctionnement d'un des paramètres, mais pour simplifier la macro, pensez à supprimer les paramètres inutiles :
Pour cela, ne supprimez pas directement les instructions mais ajoutez une apostrophe simple au début de chaque ligne que vous voulez supprimer. Cet apostrophe est la syntaxe pour mettre une ligne en commentaire (elle devrait changer de couleur dans l'éditeur). Ainsi la macro ne fonctionne plus comme vous vous y attendez, il suffit de supprimer l'apostrophe pour retrouver le comportement et en plus sans avoir à ressaisir le code ou à ré-enregistrer la macro.

Ici pour l'exemple, on va mettre en commentaires tous les attributs de recherche inutiles (Format, MatchWholeWord, MatchWildcards, MatchSoundsLike, MatchAllWordForms)

 
Sélectionnez

Sub DvpRechercherMultipleEnrModif1()
'
' DvpRechercherMultipleEnr Macro - Version modifiée
'
'
  	Selection.HomeKey Unit:=wdStory
    Selection.Find.ClearFormatting
    With Selection.Find
        .Text = ".^w"
        .Forward = True
        .Wrap = wdFindContinue
        .Format = False
'        .MatchCase = False
        .MatchWholeWord = False
'        .MatchWildcards = False
'        .MatchSoundsLike = False
'        .MatchAllWordForms = False
    End With
    Selection.Find.Execute
    Selection.MoveRight Unit:=wdCharacter, Count:=1
    Selection.TypeBackspace
    Selection.TypeParagraph
    Application.Browser.Next
    Selection.MoveRight Unit:=wdCharacter, Count:=1
    Selection.TypeBackspace
    Selection.TypeParagraph
    Application.Browser.Next
End Sub
		

L'un des problèmes de l'enregistreur de macro est son impossibilité de gérer les tests des fonctions comme la recherche
Pour cela, après des fonctions comme la recherche, vous devez tester le résultat pour savoir si votre document correspond à ce qui est prévu dans votre code.

Ici pour l'exemple, on va mettre tester le résultat de la recherche avant d'effectuer les modifications. Le code pour vérfier si une recherche a abouti ou non est "Selection.Find.Found" qui est valorisé à la valeur de la dernière recherche effectuée.
Vous avez remarquer que l'enregistreur de macros utilise l'instruction "With... End With", il décale le code : cette technique de codage, appelée "indentation", plus ancienne que VBA, est très largement utilisée en technique de programmation et est reconnue comme facilitant la lecture et la compréhension du code. On va donc continuer à l'utiliser et l'étendre à tous les blocs de programmation.
De façon identique, on va remplacer "Application.Browser.Next" qui relance la dernière recherche effectuée par une nouvelle exécution de la recherche mais comme tous les paramètres de la recherche sont déjà positionnés, il suffit d'exécuter la dernière recherche <=> "Selection.Find.Execute"

 
Sélectionnez

Sub DvpRechercherMultipleEnrModif2()
  	Selection.HomeKey Unit:=wdStory
    Selection.Find.ClearFormatting
    With Selection.Find
        .Text = ".^w"
        .Forward = True
        .Wrap = wdFindContinue
        .Format = False
        .MatchWholeWord = False
    End With
    Selection.Find.Execute
    If Selection.Find.Found Then
        Selection.MoveRight Unit:=wdCharacter, Count:=1
        Selection.TypeBackspace
        Selection.TypeParagraph
	    Selection.Find.Execute
	    If Selection.Find.Found Then
            Selection.MoveRight Unit:=wdCharacter, Count:=1
            Selection.TypeBackspace
            Selection.TypeParagraph
        End If
    End If
End Sub
		

Quelques explications

If Selection.Find.Found Then La commande "Selection.Find.Execute" positionne la varaible "Found" avec le résultat de la recheche qui vaut "True" si la recherche a abouti (i.e. si on a trouvé l'élément recherché) sinon qui vaut "False".
Remarque : en programmation, on ne teste pas les expressions booléennes sur "True" ou sur "False" mais seulement sur leur condition <=> pas de d'écriture "If Selection.Find.Found = True Then" mais bien "If Selection.Find.Found Then".

La deuxième instruction Selection.Find.Execute permet de relancer une nouvelle exécution de la recherche déjà définie.

VII. Remerciements

2Do