VBA et développement Web

Image non disponible


précédentsommairesuivant

IV. Librairie HTML

Jusqu'ici nous avons navigué sur une page sans regarder son contenu.
Une page internet est codée en langage HTML.
Si vous ne connaissez pas ce langage, vous pouvez consulter ce tutoriel : Les bases du HTML.

Ce langage étant structuré, nous allons lire sa structure à l'aide de la librairie Microsoft HTML Object Library.
Commençons donc par référencer cette librairie dans le menu : Outils => Références ....

IV-A. Parcourir un document HTML

Afin de parcourir un document HTML en provenance d'internet par exemple, il faut d'abord le charger.
Chargeons la page de recherche Google dans un navigateur.
Utilisons pour cela le code vu dans le chapitre précédent :

 
Sélectionnez

Public Sub CreerNavigateur()
Dim oNav As SHDocVw.InternetExplorer
Set oNav = New SHDocVw.InternetExplorer
oNav.Visible = True
oNav.navigate "http://google.fr"
End Sub


Cette fonction ouvre Internet Explorer et navigue vers la page de recherche de Google.
Il est également possible d'ouvrir la page dans un contrôle WebBrowser, la suite du code serait identique.

Vous pouvez visualiser la source en cliquant sur la page avec le bouton droit et en choisissant "Afficher la source".
Le code affiché est le code HTML ; il est difficilement exploitable en l'état par code VBA.

En fonction de votre version d'Internet Explorer, vous avez peut-être accès aux outils de développements (dans le menu Outils ou F12).
Cet outil s'avère très pratique pour analyser une page HTML.

Les contrôles navigateurs ont une propriété Document de type Object.
Cette propriété est une passerelle entre la librairie Microsoft Internet Controls et la librairie Microsoft HTML Object Library.
C'est en fait un objet de type HTMLDocument référencé dans la librairie HTML.
Pour profiter de l'autocomplétion de VBA, nous allons déclarer un objet de ce type afin d'y affecter la valeur de cette propriété.

 
Sélectionnez

Public Sub CreerNavigateur()
Dim oNav As SHDocVw.InternetExplorer
Dim oDoc As MSHTML.HTMLDocument
Set oNav = New SHDocVw.InternetExplorer
oNav.Visible = True
oNav.navigate "http://google.fr"
Set oDoc = oNav.document
End Sub


Si on exécute le code, on obtient une erreur :

Erreur d'exécution '-2147467259(80004005)':

Erreur Automation
Erreur non spécifiée

Cette erreur est due au fait que l'on essaye de lire la propriété Document avant que celui ne soit chargé.
Il faut donc ajouter une temporisation afin d'attendre le chargement de la page.

Pour vérifier l'état du navigateur, il y a deux propriétés :
- ReadyState : indique l'état de chargement de la page (en cours, chargée...)
- Busy : indique si le navigateur est occupé.

Il est possible que le chargement soit terminé, mais que le navigateur soit toujours occupé à faire diverses tâches.
Nous allons utiliser ces deux propriétés pour nous assurer que tout est bien chargé et que les objets HTML sont tous présents dans l'objet Document.
Une simple boucle suffirait :

 
Sélectionnez

While oNav.readyState <> READYSTATE_COMPLETE Or oNav.Busy = True
    DoEvents
Wend


Ce code attend que le chargement de la page soit terminé et que le navigateur ne soit plus occupé.
DoEvents peut être nécessaire pour que certains éléments se chargent.
Sans DoEvents, il est possible qu'on reste bloqué dans une boucle sans fin.
Mais comme nous aurons probablement besoin de faire cette même temporisation plusieurs fois, nous allons créer une petite fonction.
Nous en profiterons pour ajouter un timeout qui évitera une boucle sans fin en cas de blocage du navigateur.

 
Sélectionnez

' Attend que la page internet soit chargée
' pTimeOut est un time out en secondes (WaitIE vaut True si Timeout)
Public Function WaitIE(oIE As InternetExplorer, Optional pTimeOut As Long = 0) As Boolean
Dim lTimer As Double
lTimer = Timer
Do
    DoEvents
   If oIE.readyState = READYSTATE_COMPLETE And Not oIE.Busy Then Exit Do
   If pTimeOut > 0 And Timer - lTimer > pTimeOut Then
       WaitIE = True
       Exit Do
   End If
Loop
End Function


Cette fonction peut être placée dans n'importe quel module VBA de l'application.
On lui donne en paramètre l'objet Navigateur que l'on veut attendre, et un timeout en secondes.
Si le navigateur n'a pas rendu la main à l'issue du timeout, la fonction renvoie True.

 
Sélectionnez

Public Sub CreerNavigateur()
Dim oNav As SHDocVw.InternetExplorer
Dim oDoc As MSHTML.HTMLDocument
Set oNav = New SHDocVw.InternetExplorer
oNav.Visible = True
oNav.navigate "http://google.fr"
' Attente avec timeout de 10 s
If WaitIE(oNav, 10) Then
   ' 10 s écoulées et page non chargée
   MsgBox "Time out!"
Else
   ' Page chargée, on continue
   Set oDoc = oNav.document
End If
End Sub


Cette fonction WaitIE pourra être appelée à chaque navigation.
Si on a précisé un timeout et qu'elle renvoie True, on ne continue pas le traitement.

Sinon, on charge le document dans l'objet oDoc.
Pour visualiser cet objet, placez un point d'arrêt (F9 sur la ligne End If), ou ajoutez une instruction Stop.
Exécutez ensuite la fonction, l'exécution s'arrête.
Affichez la fenêtre Variables Locales : Affichage => Fenêtre Variables Locales.

Image non disponible

Vous pouvez naviguer dans l'arborescence de l'objet oDoc.
Chaque élément possède une collection ChildNodes qui permet de visualiser hiérarchiquement tous les objets HTML du document.
Dans la collection All, tous les éléments de l'arborescence sont mis à plat.
Il existe d'autres collections regroupant des objets de même type, par exemple :
forms (formulaires), links (hyperliens), images (images)...

Le nombre d'éléments dans une collection est donné par la propriété Length :
- oDoc.Images.Length => 4
Le premier élément est l'élément d'indice 0 :
- oDoc.Images(0).src => http://www.google.fr/images/close_sm.gif

Pour chercher un élément par son Identifiant, on utilisera la fonction getElementById.
Pour chercher un élément par son Nom, on utilisera la fonction getElementsByName.

IV-B. Piloter une page HTML

Nous souhaitons maintenant remplir la zone de texte à rechercher, puis cliquer sur le bouton de recherche par le code VBA.
Après le chargement de la page, il suffit de chercher la zone de texte dans l'arborescence d'objets et de lui affecter une valeur.
Après analyse du code source de la page, on trouve la zone de recherche :

 
Sélectionnez

<input autocomplete=off maxlength=2048 name=q class="lst" title="Recherche Google" 
        value="" size=57 style="background:#fff;border:1px solid #ccc;border-bottom-color:#999;
        border-right-color:#999;color:#000;font:18px 
        arial,sans-serif bold;margin:0;padding:5px 8px 0 6px;vertical-align:top">

La zone de texte est nommée (name=q), nous pouvons donc faire une recherche par nom.

 
Sélectionnez

[...]
   ' Page chargée, on continue
   Set oDoc = oNav.document
   ' Valeur recherchée
   oDoc.getElementsByName("q")(0).Value = "Arkham46"
End If
End Sub


La fonction getElementsByName renvoie un tableau car il pourrait y avoir plusieurs éléments de même nom.
Sur notre page il n'y en a qu'un ; on récupère donc le premier trouvé (indice 0) et on modifie sa valeur.
Pour l'exemple je mets "Arkham46" dans cette zone de recherche.

Ensuite on simule le clic sur le bouton de recherche.
L'élément qui soumet un formulaire est de type submit ; le voici :

 
Sélectionnez

<input name=btnG type=submit value="Recherche Google" class=lsb>


Notez qu'il existe un autre bouton de type submit, celui qui correspond à la recherche nommée "J'ai de la chance".

Pour cliquer sur un bouton, il faut exécuter sa méthode Click :

Pour rechercher ce bouton, on peut utiliser la fonction getElementsByName.
Problème ici : avec la version 8 d'Internet Explorer, on se retrouve avec deux boutons de même nom sur la page.

Le code suivant fonctionne avec Internet Explorer 6, mais pas avec Internet Explorer 8 :

 
Sélectionnez

   ' Clic sur bouton
   oDoc.getElementsByName("btnG")(0).click


Pour Internet Explorer 8, le bouton recherché est le deuxième (donc d'indice 1) :

 
Sélectionnez

   ' Clic sur bouton
   oDoc.getElementsByName("btnG")(1).click


Pour pouvoir facilement retrouver un bouton, on se crée une petite fonction :

 
Sélectionnez

' Recherche d'un bouton par son nom dans le document
Private Function GetButton(Document As HTMLDocument, ButtonName As String) As MSHTML.HTMLInputElement
If Document.getElementsByName(ButtonName).length > 1 Then
   Set GetButton = Document.getElementsByName(ButtonName)(1)
Else
   Set GetButton = Document.getElementsByName(ButtonName)(0)
End If
End Function


Cette fonction renvoie le premier bouton s'il n'y en a qu'un, ou le deuxième s'il y en a plusieurs.

On note ici les problèmes rencontrés lors de l'automatisation d'une page qui n'a pas été prévue pour.
La page peut évoluer et le code est alors à revoir...

Maintenant qu'on sait retrouver le bouton, on clique dessus :

 
Sélectionnez

[...]
   ' Page chargée, on continue
   Set oDoc = oNav.document
   ' Valeur recherchée
   oDoc.getElementsByName("q")(0).Value = "Arkham46"
   ' Clic sur bouton
   GetButton(oDoc, "btnG").click
End If
End Sub


Une autre possibilité est de soumettre le formulaire directement avec sa méthode submit sans passer par un bouton.

 
Sélectionnez

   ' Soumettre le formulaire
   oDoc.Forms("f").submit


Attention cependant car le clic sur un bouton peut effectuer certaines actions complémentaires qui ne seront pas accomplies par un submit direct.
Dans le doute, simulez le clic sur le bouton voulu.

IV-C. Événements des éléments d'une page HTML

On a vu précédemment que nous avions à notre disposition des événements liés au document.
Il existe aussi des événements pour chacun des éléments de la page.
À titre d'exemple, capturons le clic sur le bouton de recherche de Google.

Reprenons la fonction précédemment écrite, mais en retirant le clic sur le bouton que nous ferons manuellement.
Afin de bénéficier des événements, il faut écrire le code dans un module de classe.
Créons donc un formulaire avec un bouton nommé OpenExplorer.
Sur clic sur ce bouton, on ouvre Internet Explorer sur la page de recherche Google.

 
Sélectionnez

Private Sub OpenExplorer_Click()
Dim oNav As SHDocVw.InternetExplorer
Dim oDoc As MSHTML.HTMLDocument
Set oNav = New SHDocVw.InternetExplorer
oNav.Visible = True
oNav.navigate "http://google.fr"
' Attente avec timeout de 10s
If WaitIE(oNav, 10) Then
   ' 10&#160;s écoulées et page non chargée
   MsgBox "Time out!"
Else
   ' Page chargée, on continue
   Set oDoc = oNav.document
End If
End Sub       


Nous allons également avoir besoin de la fonction GetButton créée précédemment, collez-la en fin de module par exemple.

Il faut en premier lieu connaître le type de l'élément.
Utilisons TypeName.

 
Sélectionnez

[...]
   ' Page chargée, on continue
   Set oDoc = oNav.document
   ' Bouton Google
   MsgBox TypeName(GetButton(oDoc,"btnG"))
End If
End Sub       


La boîte de message nous donne le type d'élément nommé btnG : nous avons affaire à un HTMLInputElement.
Ajoutons en en-tête du module une déclaration d'objet de ce type.

 
Sélectionnez

Private WithEvents BoutonGoogle As HTMLInputElement


Grâce à l'instruction WithEvents, les événements de cet objet sont disponibles dans les listes déroulantes en haut de la page de code VBA.
Choisissez l'objet BoutonGoogle à gauche et l'événement onclick à droite.
La procédure de traitement du clic sur le bouton est générée :

 
Sélectionnez

Private Function BoutonGoogle_onclick() As Boolean
            
End Function


Vous pouvez alors écrire le code désiré à l'intérieur.
Par exemple l'affichage d'un texte dans la fenêtre Exécution (affichée par CTRL + G) :

 
Sélectionnez

Private Function BoutonGoogle_onclick() As Boolean
Debug.Print "Le bouton de recherche est désactivé !"
End Function


L'objet BoutonGoogle est prêt, mais pour l'instant il est vide.
Pour terminer, n'oublions pas d'y affecter le bouton du formulaire après chargement de la page :

 
Sélectionnez

[...]
   ' Page chargée, on continue
   Set oDoc = oNav.document
   ' Bouton Google
   Set BoutonGoogle = GetButton(oDoc,"btnG")
End If
End Sub


Nous pouvons tester l'événement en affichant le formulaire.

Notez que par défaut l'événement remplace l'événement standard.
La recherche Google ne s'effectue pas.
Pour que l'événement de clic standard se déclenche, donnez la valeur True au retour de la fonction.

 
Sélectionnez

Private Function BoutonGoogle_onclick() As Boolean
Debug.Print "Le bouton de recherche est activé !"
BoutonGoogle_onclick = True
End Function

IV-D. Modifier dynamiquement une page

Nous allons ajouter un bouton de recherche personnalisée, en plus des deux boutons de recherche de Google.
Pour démarrer ce chapitre, on reprend le formulaire avec un bouton OpenExplorer et sa procédure sur clic.

 
Sélectionnez

Private Sub OpenExplorer_Click()
Dim oNav As SHDocVw.InternetExplorer
Dim oDoc As MSHTML.HTMLDocument
Set oNav = New SHDocVw.InternetExplorer
oNav.Visible = True
oNav.navigate "http://google.fr"
' Attente avec timeout de 10 s
If WaitIE(oNav, 10) Then
   ' 10 s écoulées et page non chargée
   MsgBox "Time out!"
Else
   ' Page chargée, on continue
   Set oDoc = oNav.document
End If
End Sub


Jetons un coup d'œil à la source ; voici ce qu'on y trouve (légèrement remis en forme) :

 
Sélectionnez

<span class=ds >
    <span class=lsbb>
        <input name=btnG type=submit value="Recherche Google" class=lsb>
    </span>
</span>
<span class=ds>
    <span class=lsbb>
        <input name=btnI type=submit class=lsb value="J'ai de la chance">
    </span>
</span>


Dans un premier temps, notre objectif est de rajouter un troisième bouton en suivant le même modèle que les deux existants.
C'est-à-dire : un élément input dans un span lui-même dans un span.
En conservant cette même structure, on aura visuellement un bouton identique aux deux autres.

Voici le code HTML de ce que nous souhaitons ajouter :

 
Sélectionnez

<span class=ds >
    <span class=lsbb>
        <input name=btnA type=submit value="Recherche Arkham46" class=lsb>
    </span>
</span>


Le nouveau bouton aura pour nom btnA et pour texte "Recherche Arkham46".
C'est un élément de type submit car il sert à soumettre le formulaire de recherche.

On commence par déclarer trois objets, dont deux au début de la procédure OpenExplorer_Click :

 
Sélectionnez

Dim oSpan_ds As MSHTML.HTMLSpanElement
Dim oSpan_lsbb As MSHTML.HTMLSpanElement


Et le troisième (le bouton) au début du module, avec WithEvents, pour avoir accès à ses événements :

 
Sélectionnez

Private WithEvents oButton As MSHTML.HTMLInputElement


On a ainsi à disposition un objet pour notre bouton et un objet pour chaque span.
Créons alors nos éléments après chargement du document.

 
Sélectionnez

[...]
   ' Page chargée, on continue
   Set oDoc = oNav.document    ' Span
   Set oSpan_ds = oDoc.createElement("span")
   oSpan_ds.className = "ds"
   ' Span
   Set oSpan_lsbb = oDoc.createElement("span")
   oSpan_ds.className = "lsbb"
   ' Bouton
   Set oButton = oDoc.createElement("input")
   oButton.Name = "btnA"
   oButton.Type = "submit"
   oButton.Value = "Recherche Arkham46"
   oButton.className = "lsb"
End If
End Sub


On crée d'abord chacun des objets avec CreateElement puis on définit les attributs.
Les éléments sont créés sur l'objet Document.
À leur création ils sont orphelins.
Il faut maintenant les ajouter au bon endroit.

 
Sélectionnez

   ' Ajout du bouton dans le span lsbb
   oSpan_lsbb.appendChild oButton
   ' Ajout du span lsbb dans le span ds
   oSpan_ds.appendChild oSpan_lsbb


Afin d'obtenir la hiérarchie définie précédemment, on ajoute le bouton dans le deuxième span, et le deuxième span dans le premier.
Reste à ajouter l'ensemble au bon emplacement, c'est-à-dire après le deuxième bouton existant.

Pour cela on va d'abord rechercher un bouton existant.
Puis on remonte à l'élément parent (avec la méthode ParentElement) : cela nous amène au deuxième span.
On cherche alors le parent une nouvelle fois pour nous mener au premier span.
Le parent de ce dernier est alors l'élément dans lequel on doit ajouter notre élément span contenant le bouton.

 
Sélectionnez

   ' Ajout de l'ensemble
   GetButton(oDoc,"btnG").parentElement.parentElement.parentElement.appendChild oSpan_ds


On obtient alors notre troisième bouton :

Image non disponible

L'utilisation de appendChild ajoute l'élément à la fin.
Pour insérez un élément à un emplacement défini, il faut utiliser insertBefore ; par exemple :

 
Sélectionnez

   ' Insertion de l'ensemble
   GetButton(oDoc,"btnG").parentElement.parentElement.parentElement.insertBefore _
                        oSpan_ds, GetButton(oDoc,"btnG").parentElement.parentElement



Pour réagir au clic sur ce bouton, choisissez dans les listes déroulantes en haut du module VBA : oButton et onclick.
Dans la procédure, écrivons le code qui remplit automatiquement la zone de recherche.

 
Sélectionnez

Private Function oButton_onclick() As Boolean
oButton.document.forms("f").elements("q").Value = "Arkham46"
oButton_onclick = True
End Function


Pour changer, ici on a utilisé les collections forms et elements pour trouver la zone de recherche.
Cela fonctionne correctement car il n'y a qu'un seul formulaire nommé "f", et un seul élément nommé "q".

On n'oublie pas de mettre le retour de la fonction à True pour que l'événement continue.

C'est maintenant terminé : un clic sur le nouveau bouton lance une recherche sur le mot "Arkham46".
Voici le code complet de ce formulaire :

 
Sélectionnez

Option Explicit
            
Private WithEvents oButton As MSHTML.HTMLInputElement
            
Private Function oButton_onclick() As Boolean
oButton.document.forms("f").elements("q").Value = "Arkham46"
oButton_onclick = True
End Function
            
Private Sub OpenExplorer_Click()
Dim oNav As SHDocVw.InternetExplorer
Dim oDoc As MSHTML.HTMLDocument
Dim oSpan_ds As MSHTML.HTMLSpanElement
Dim oSpan_lsbb As MSHTML.HTMLSpanElement
Set oNav = New SHDocVw.InternetExplorer
oNav.Visible = True
oNav.navigate "http://google.fr"
' Attente avec timeout de 10 s
If WaitIE(oNav, 10) Then
   ' 10 s écoulées et page non chargée
   MsgBox "Time out!"
Else
   ' Page chargée, on continue
   Set oDoc = oNav.document
   ' Span
   Set oSpan_ds = oDoc.createElement("span")
   oSpan_ds.className = "ds"
   ' Span
   Set oSpan_lsbb = oDoc.createElement("span")
   oSpan_lsbb.className = "lsbb"
   ' Bouton
   Set oButton = oDoc.createElement("input")
   oButton.Name = "btnA"
   oButton.Type = "submit"
   oButton.Value = "Recherche Arkham46"
   oButton.className = "lsb"
   ' Ajout du bouton dans le span lsbb
   oSpan_lsbb.appendChild oButton
   ' Ajout du span lsbb dans le span ds
   oSpan_ds.appendChild oSpan_lsbb
   ' Ajout de l'ensemble
   GetButton(oDoc,"btnG").parentElement.parentElement.parentElement.appendChild oSpan_ds
End If
End Sub
            
' Recherche d'un bouton par son nom dans le document
Private Function GetButton(Document As HTMLDocument, ButtonName As String) As MSHTML.HTMLInputElement
If Document.getElementsByName(ButtonName).length > 1 Then
   Set GetButton = Document.getElementsByName(ButtonName)(1)
Else
   Set GetButton = Document.getElementsByName(ButtonName)(0)
End If
End Function

IV-E. Créer un gestionnaire d'événements commun

Maintenant, nous souhaitons réagir au clic sur chacun des trois boutons.
On pourrait déclarer trois objets au début du module, avec WithEvents, et gérer chaque événement indépendamment.
C'est une solution qui fonctionne, mais qui devient vite lourde à gérer lorsqu'on souhaite gérer de nombreux éléments.
Nous allons donc dans ce chapitre voir comment créer un gestionnaire d'événements en VBA.

Voici le code de départ de ce chapitre, dans un formulaire avec un bouton nommé OpenExplorer :

 
Sélectionnez

Private Sub OpenExplorer_Click()
Dim oNav As SHDocVw.InternetExplorer
Dim oDoc As MSHTML.HTMLDocument
Dim oSpan_ds As MSHTML.HTMLSpanElement
Dim oSpan_lsbb As MSHTML.HTMLSpanElement
Dim oButton As MSHTML.HTMLInputElement
Set oNav = New SHDocVw.InternetExplorer
oNav.Visible = True
oNav.navigate "http://google.fr"
' Attente avec timeout de 10 s
If WaitIE(oNav, 10) Then 
   ' 10 s écoulées et page non chargée
   MsgBox "Time out!"
Else
   ' Page chargée, on continue
   Set oDoc = oNav.Document
   ' Span
   Set oSpan_ds = oDoc.createElement("span")
   oSpan_ds.className = "ds"
   ' Span
   Set oSpan_lsbb = oDoc.createElement("span")
   oSpan_lsbb.className = "lsbb"
   ' Bouton
   Set oButton = oDoc.createElement("input")
   oButton.Name = "btnA"
   oButton.Type = "submit"
   oButton.Value = "Recherche Arkham46"
   oButton.className = "lsb"
   ' Ajout du bouton dans le span lsbb
   oSpan_lsbb.appendChild oButton
   ' Ajout du span lsbb dans le span ds
   oSpan_ds.appendChild oSpan_lsbb
   ' Ajout de l'ensemble
   GetButton(oDoc, "btnG").parentElement.parentElement.parentElement.insertBefore _
                        oSpan_ds, GetButton(oDoc, "btnG").parentElement.parentElement
End If
End Sub
            
' Recherche d'un bouton par son nom dans le document
Private Function GetButton(Document As HTMLDocument, ButtonName As String) As MSHTML.HTMLInputElement
If Document.getElementsByName(ButtonName).length > 1 Then
   Set GetButton = Document.getElementsByName(ButtonName)(1)
Else
   Set GetButton = Document.getElementsByName(ButtonName)(0)
End If
End Function


Ce code ouvre Internet Explorer, navigue sur la page de recherche Google, et ajoute le bouton "Recherche Arkham46".
Par rapport au code précédent, on a supprimé la déclaration de oButton de l'en-tête pour la mettre en local dans la procédure.
Tout l'enjeu est maintenant de pouvoir gérer des événements sans déclarer chacun des éléments en en-tête de module.

Chacun des éléments possède des propriétés définissant le gestionnaire affecté à chaque élément :
Par exemple : onclick, onchange...

S'il est classique de créer un gestionnaire d'événements en JavaScript par exemple, il est moins facile de le faire en VBA.
Mais c'est tout de même possible.
Le gestionnaire est en fait un objet, qui possède une méthode par défaut.
Pour créer un tel objet, il faut ajouter un module de classe : menu Insertion => Module de classe.
Le code de ce module est le suivant :

 
Sélectionnez

Option Explicit
            
Public Event WebEvent()
            
Public Sub EventFired()
'Attribute EventFired.VB_UserMemId = 0
   RaiseEvent WebEvent
End Sub


Il contient :
- un événement WebEvent : c'est cet événement que nous allons récupérer dans notre code.
- une procédure EventFired (son nom est arbitraire) qui est la procédure exécutée lorsqu'un événement est levé.

Sauvegardez ce module, avec le nom WebEvents.

Pour une utilisation dans une application Internet Explorer, il faut que ce module soit public.
Définissez la propriété Instancing du module à 2 - PublicNotCreatable.
Le module peut rester privé si la page est affichée dans un contrôle WebBrowser.

Ensuite il faut définir la procédure EventFired comme procédure par défaut de l'objet.
Pour cela il faut exporter, modifier, puis réimporter le module.

- Exportez le module : menu Fichier => Exporter un fichier....
- Supprimez le module : menu Fichier => Supprimer WebEvents....
- Modifiez le fichier exporté dans un éditeur texte : décommentez la première ligne de la procédure.
- Importez le fichier modifié : menu Fichier => Importer un fichier....

La ligne Attribute EventFired.VB_UserMemId = 0 est prise en compte à l'import, mais n'est pas affichée dans l'éditeur VBA.

Le module WebEvents est prêt.
Pour l'utiliser, il faut déclarer dans le code du formulaire (en en-tête de module) un objet de type WebEvents.
N'oubliez pas l'instruction WithEvents qui permet d'utiliser l'événement WebEvent de l'objet.

 
Sélectionnez

Private WithEvents oEvents As WebEvents


Ensuite il faut instancier cet objet, dans la procédure OpenExplorer_Click.
En début de procédure, après les déclarations, ajoutez :

 
Sélectionnez

Set oEvents = New WebEvents


L'objet est prêt à servir de gestionnaire d'événements.
Il suffit de l'affecter à la propriété correspondant à l'événement à gérer.
On peut réutiliser le même gestionnaire autant de fois que l'on veut.

En fin de procédure, on affecte l'objet oEvents à chacun des boutons :

 
Sélectionnez

[...]
   ' Affecte le gestionnaire d'événements aux boutons
   GetButton(oDoc, "btnG").onclick = oEvents
   GetButton(oDoc, "btnI").onclick = oEvents
   oButton.onclick = oEvents
End If
End Sub


Notez que pour le bouton personnalisé, on utilise directement la variable oButton car ce bouton ajouté dynamiquement n'est pas retrouvé par la méthode getElementsByName.

Par ce code, on demande à ce qu'un clic sur un bouton déclenche la procédure par défaut de l'objet oEvents.
Cette procédure déclenche alors l'événement WebEvent que nous allons gérer.

Cliquez en haut de la page de code sur oEvents à gauche et sur WebEvent à droite.
La procédure événementielle est générée :

 
Sélectionnez

Private Sub oEvents_WebEvent()
            
End Sub


Écrivons-y une instruction Stop pour tester.
Affichez le formulaire et cliquez sur son bouton OpenExplorer pour ouvrir notre explorateur.
Un clic sur un des boutons nous amène à la procédure oEvents_WebEvent.
Intéressant mais comment savoir quel bouton a été cliqué ?

Pendant la durée d'un événement, l'objet Window du document HTML possède une propriété Event qui contient les informations de l'événement.
Pour pouvoir lire cette propriété il nous faut l'objet document.
Cet objet est l'objet oNav qui est déclaré dans la procédure OpenExplorer_Click et n'est donc pas disponible dans la procédure oEvents_WebEvent.
Déplacez donc sa déclaration en en-tête de module, après la déclaration de oEvents :

 
Sélectionnez

Private WithEvents oEvents As WebEvents
Private oDoc As MSHTML.HTMLDocument


oDoc est maintenant accessible dans toutes les procédures du module.

Jetons un œil sur les informations de l'événement.
Toujours avec le point d'arrêt dans oEvents_WebEvent, lancez le navigateur et cliquez sur un bouton.
Revenez à l'éditeur VBA et affichez la fenêtre Variables locales : menu Affichage => Fenêtre Variables Locales.

Naviguez dans l'arborescence : Me => oDoc => parentWindow => event.

Image non disponible

Nous constatons que cet objet est de type IHTMLEventObj.

Il contient de nombreuses informations dont l'objet source de l'événement : srcElement.
On voit également le type d'événement ("click") qui pourrait permettre de gérer plusieurs types d'événements dans la procédure.

Dans notre procédure événementielle, on peut alors récupérer l'objet event.

 
Sélectionnez

Private Sub oEvents_WebEvent()
Dim oEvent As IHTMLEventObj
Set oEvent = oDoc.parentWindow.event
End Sub


Puis on ajoute du code de traitement de l'événement :

 
Sélectionnez

Private Sub oEvents_WebEvent()
Dim oEvent As IHTMLEventObj
Set oEvent = oDoc.parentWindow.event
' Traitement fonction du nom de l'élément
Select Case oEvent.srcElement.getAttribute("name")
   Case "btnA" ' Bouton "Recherche Arkham46"
       ' Inscrit la valeur recherchée
       oEvent.srcElement.Document.getElementsByName("q")(0).Value = "Arkham46"
   Case "btnG" ' Bouton "Recherche Google"
       '
   Case "btnI" ' Bouton "J'ai de la chance"
       ' Désactive ce bouton
       oEvent.returnValue = False
End Select
' Ajout du texte du bouton dans la fenêtre exécution
Debug.Print "Click sur " & oEvent.srcElement.getAttribute("value")
End Sub


On récupère les attributs HTML de l'élément avec la méthode getAttribute :
- name est le nom du bouton ;
- value est le texte du bouton.

Si le nom du bouton est "btnA", on écrit le texte de recherche prédéfini "Arkham46".
Pour le bouton "btnI" (bouton 'J'ai de la chance'), on désactive l'événement en donnant la valeur False à returnValue.
Pour le bouton "btnG" (bouton de recherche classique) on ne fait rien de particulier.

On écrit également le texte du bouton cliqué dans la fenêtre Exécution (CTRL+G).

C'est maintenant terminé : le clic sur chacun des boutons est géré dans une seule procédure.
Voici le code complet de ce formulaire :

 
Sélectionnez

Option Explicit
            
Private WithEvents oEvents As WebEvents
Private oDoc As MSHTML.HTMLDocument
            
Private Sub oEvents_WebEvent()
Dim oEvent As IHTMLEventObj
Set oEvent = oDoc.parentWindow.event
' Traitement fonction du nom de l'élément
Select Case oEvent.srcElement.getAttribute("name")
   Case "btnA" ' Bouton "Recherche Arkham46"
       ' Inscrit la valeur recherchée
       oEvent.srcElement.Document.getElementsByName("q")(0).Value = "Arkham46"
   Case "btnG" ' Bouton "Recherche Google"
       '
   Case "btnI" ' Bouton "J'ai de la chance"
       ' Désactive ce bouton
       oEvent.returnValue = False
End Select
' Ajout du texte du bouton dans la fenêtre exécution
Debug.Print "Click sur " & oEvent.srcElement.getAttribute("value")
End Sub
            
Private Sub OpenExplorer_Click()
Dim oNav As SHDocVw.InternetExplorer
Dim oSpan_ds As MSHTML.HTMLSpanElement
Dim oSpan_lsbb As MSHTML.HTMLSpanElement
Dim oButton As MSHTML.HTMLInputElement
Set oEvents = New WebEvents
Set oNav = New SHDocVw.InternetExplorer
oNav.Visible = True
oNav.navigate "http://google.fr"
' Attente avec timeout de 10 s
If WaitIE(oNav, 10) Then
   ' 10 s écoulées et page non chargée
   MsgBox "Time out!"
Else
   ' Page chargée, on continue
   Set oDoc = oNav.Document
   ' Span
   Set oSpan_ds = oDoc.createElement("span")
   oSpan_ds.className = "ds"
   ' Span
   Set oSpan_lsbb = oDoc.createElement("span")
   oSpan_lsbb.className = "lsbb"
   ' Bouton
   Set oButton = oDoc.createElement("input")
   oButton.Name = "btnA"
   oButton.Type = "submit"
   oButton.Value = "Recherche Arkham46"
   oButton.className = "lsb"
   ' Ajout du bouton dans le span lsbb
   oSpan_lsbb.appendChild oButton
   ' Ajout du span lsbb dans le span ds
   oSpan_ds.appendChild oSpan_lsbb
   ' Ajout de l'ensemble
   GetButton(oDoc, "btnG").parentElement.parentElement.parentElement.insertBefore _
                            oSpan_ds, GetButton(oDoc, "btnG").parentElement.parentElement
   ' Affecte le gestionnaire d'événements aux boutons
   GetButton(oDoc, "btnG").onclick = oEvents
   GetButton(oDoc, "btnI").onclick = oEvents
   oButton.onclick = oEvents
End If
End Sub
            
' Recherche d'un bouton par son nom dans le document
Private Function GetButton(Document As HTMLDocument, ButtonName As String) As MSHTML.HTMLInputElement
If Document.getElementsByName(ButtonName).length > 1 Then
   Set GetButton = Document.getElementsByName(ButtonName)(1)
Else
   Set GetButton = Document.getElementsByName(ButtonName)(0)
End If
End Function

précédentsommairesuivant

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2011 Thierry GASPERMENT. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts. Droits de diffusion permanents accordés à Developpez LLC.