IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Macros et VBA Excel Discussion :

Événement dans Module de classe


Sujet :

Macros et VBA Excel

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2018
    Messages
    4
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2018
    Messages : 4
    Par défaut Événement dans Module de classe
    Bonjour à tous,

    Afin de remplir un userform, J'ai créé un modules de classe, qu'on appellera A.
    Aussi, j'ai défini un événement "Change" sur cette classe A.
    J'ai donc déclaré mon événement dans le module de classe A en écrivant en haut de la page :

    Puis :

    aux bons endroits, toujours dans mon module de classe A.
    Ensuite j'ai créé une instance de classe A dans mon userform :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Private WithEvents Avariable As A
    Finalement, j'ai initialisé ma variable dans mon userform en écrivant :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    Private Sub UserForm_Initialize()
        Set Avariable = New A
    End Sub
    Jusque là, aucun souci, tout fonctionne parfaitement.

    Maintenant, j'introduis une second classe, qu'on appellera B, ayant une propriété de classe A.
    J'aimerais que l’événement "Change" décrit plus haut soit détectable d'une façon ou d'une autre depuis une instance de classe B de mon Userform.
    Et là ça se complique cas je ne sais plus où écrire les instructions précédentes.

    J'ai essayé en introduisant dans ma classe B un nouvel événement Change2 propre à ma propriété de classe A se déclenchant dès que l’événement "Change" se déclenche.
    J'ai donc écrit cette ligne en haut de mon module de classe B :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Public WithEvents Aproriete As A
    Puis j'ai explicité mon nouvel événement, toujours dans mon module de classe B.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    Private Sub Aproriete_Change()
     
           RaiseEvent Change2
     
    End Sub
    Mais du coup je ne sais pas où initialiser ma propriété. Si je ne l'initialise pas avec une instruction New, j'ai un message d'erreur.
    Vous auriez une idée ?

    J'espère que je ne vous ai pas perdu

    Merci d'avance !

  2. #2
    Rédacteur/Modérateur


    Homme Profil pro
    Formateur et développeur chez EXCELLEZ.net
    Inscrit en
    Novembre 2003
    Messages
    19 125
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 58
    Localisation : Belgique

    Informations professionnelles :
    Activité : Formateur et développeur chez EXCELLEZ.net
    Secteur : Enseignement

    Informations forums :
    Inscription : Novembre 2003
    Messages : 19 125
    Billets dans le blog
    131
    Par défaut
    Salut.

    Pas certain d'avoir bien compris ta question. Si tu souhaites qu'un événement de A soit levé dans B, tu peux procéder ainsi dans le code de B
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    Option Explicit
     
    Private WithEvents mA As A
     
    Public Function SetValueForA(Value As Long)
      mA.MyValue = Value
    End Function
     
    Private Sub Class_Initialize()
      Set mA = New A
    End Sub
     
    Private Sub mA_Change()
      MsgBox "La valeur de A a été modifiée"
    End Sub
    Avec une classe A écrite ainsi
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    Option Explicit
     
    Public Event Change()
    Private mMyValue As Long
     
    Property Let MyValue(Value As Long)
      mMyValue = Value
      RaiseEvent Change
    End Property
     
    Property Get MyValue() As Long
      MyValue = mMyValue
    End Property
    Après, tu utilises B dans un code quelconque
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    Sub test()
      Dim myB As New B
     
      myB.SetValueForA 19
    End Sub
    C'est juste un exemple car normalement une classe ne peut pas envoyer de message à l'utilisateur (règle d'encapsulation). C'est d'ailleurs une des raisons qui amène à créer un événement

    Si maintenant tu souhaites que l'événement de A passe au travers de B pour être capturé par le userform et que tu sais dans B que l'événement sur A sera levé lors d'une modification en B, une solution simple serait de créer une événement dans B. Tu pourrais donc, dans le userform, déclarer B avec WithEvents. Voici une classe B qui pourrait être utilisée pour cela
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    Public Event Change()
    Private mA As A
     
    Public Function SetValueForA(Value As Long)
      mA.MyValue = Value
      RaiseEvent Change
    End Function
     
    Private Sub Class_Initialize()
      Set mA = New A
    End Sub
    Troisième possibilité: Dans B, tu ne sais pas quand la valeur de A est modifiée (par exemple, elle l'est à cause d'un élément extérieur à B ou à la suite de conditions que tu ne peux/veux pas répéter dans B). Tu peux alors créer un événement dans B qui est levé lors de la levée de l'événement dans A pour créer des événements en cascade.

    Voici une classe A qui lève un événement sur une valeur modifiée en fonction d'une condition
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    Option Explicit
     
    Public Event Change()
    Private mMyValue As Long
     
    Property Let MyValue(Value As Long)
      mMyValue = Value
      If Value > 10 Then RaiseEvent Change
    End Property
     
    Property Get MyValue() As Long
      MyValue = mMyValue
    End Property
    Le code de B pourrait alors être le suivant puisque, sans répéter les conditions dans B, c'est A qui décidera si l'événement est levé ou pas.
    Code : 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
    Option Explicit
     
    Public Event Change()
    Private WithEvents mA As A
     
    Public Function SetValueForA(Value As Long)
      mA.MyValue = Value
    End Function
     
    Private Sub Class_Initialize()
      Set mA = New A
    End Sub
     
    Private Sub mA_Change()
      RaiseEvent Change
    End Sub
    Un userform pourrait alors utiliser ceci:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    Option Explicit
     
    Dim WithEvents myB As B
     
    Private Sub CommandButton1_Click()
     myB.SetValueForA 19
    End Sub
     
    Private Sub myB_Change()
      MsgBox "Modification de la valeur de A"
    End Sub
     
    Private Sub UserForm_Initialize()
      Set myB = New B
    End Sub
    "Plus les hommes seront éclairés, plus ils seront libres" (Voltaire)
    ---------------
    Mes billets de blog sur DVP
    Mes remarques et critiques sont purement techniques. Ne les prenez jamais pour des attaques personnelles...
    Pensez à utiliser les tableaux structurés. Ils vous simplifieront la vie, tant en Excel qu'en VBA ==> mon tuto
    Le VBA ne palliera jamais une mauvaise conception de classeur ou un manque de connaissances des outils natifs d'Excel...
    Ce ne sont pas des bonnes pratiques parce que ce sont les miennes, ce sont les miennes parce que ce sont des bonnes pratiques
    VBA pour Excel? Pensez D'ABORD en EXCEL avant de penser en VBA...
    ---------------

  3. #3
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2018
    Messages
    4
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2018
    Messages : 4
    Par défaut
    Bonjour Pierre,

    Merci d'avoir répondu.
    Bon, avec ce que tu m'as écrit et ce que j'ai pu trouver sur internet (en anglais surtout ), j'ai compris des choses mais une grosse partie reste encore obscure.
    Avec ta première solution, on a :

    • Une classe A avec des propriétés de classe simple (long) et un événement (Change)
    • Une classe B avec une propriété (mA instance de A) et une fonction (pourquoi pas une procédure ?) qui affecte une valeur à la propriété (long) de l'instance de A, c'est SetValueForA
    • Un UserForm avec une instance de B (myB).


    Ai-je bien compris ?

    Du coup, tu dis que dans le code du UserForm (par exemple), en appelant la fonction SetValueForA de ma variable myB, j'affecte une valeur long à la propriété de l'instance de A appelée par la classe B et en même temps je déclenche l'événement Change du module A.
    Plusieurs remarques :

    • Je pourrais affecter directement une valeur à cette propriété en écrivant quelques chose comme : myB.mA.Value = 19 , non ?
    • Comme tu dis par la suite, l'événement est déclenché au niveau de la classe A, et non B.
    • Comme tu le dis également par la suite, la classe A communique directement avec l'utilisateur (msgbox) sans passer par ses propriétés, fonctions ou ses événements (violation du principe d'encapsulation).


    tu propose donc une deuxième solution , où l'on déclare un événement directement dans B, qui prend le relais à Change en quelques sorte, c'est à ça que j'avais pensé.
    J'ai réussi à faire tourner cette solution.

    Dans ta troisième solution, tu suppose que l’événement Change n'est pas nécessairement déclenché dès qu'on modifie la propriété mA.Value.
    Est-ce bien cela ?

    Bon, pour l'instant je crois avoir saisi la syntaxe, l'utilisation du mot-clés WithEvents, la déclaration des événements avec Public Event, l’initialisation avec New et l'événement qu'il déclenche.
    Mais l'architecture en elle même reste un peu floue, il faut que je digère ça.

    Autre question, quand on créé une instance objet de classe B, on utilise New B ce qui nous amène à l'initialisation de B (événement B_initialize) ou l'on aura écrit New A . On arrive donc à l'initialisation de A (événement A_initilize) et donc aux données de classe simple qui ne nécessitent pas de New. Il ne faut pas détruire les pointeurs en fin de procédure avec un événement terminate ?

    Voila, désolé d'être un peu confus, je débute dans ce domaine.

    Merci.

  4. #4
    Rédacteur/Modérateur


    Homme Profil pro
    Formateur et développeur chez EXCELLEZ.net
    Inscrit en
    Novembre 2003
    Messages
    19 125
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 58
    Localisation : Belgique

    Informations professionnelles :
    Activité : Formateur et développeur chez EXCELLEZ.net
    Secteur : Enseignement

    Informations forums :
    Inscription : Novembre 2003
    Messages : 19 125
    Billets dans le blog
    131
    Par défaut
    Citation Envoyé par mc222 Voir le message
    [...]

    • Une classe A avec des propriétés de classe simple (long) et un événement (Change)
    • Une classe B avec une propriété (mA instance de A) et une fonction (pourquoi pas une procédure ?) qui affecte une valeur à la propriété (long) de l'instance de A, c'est SetValueForA
    • Un UserForm avec une instance de B (myB).


    Ai-je bien compris ?

    Du coup, tu dis que dans le code du UserForm (par exemple), en appelant la fonction SetValueForA de ma variable myB, j'affecte une valeur long à la propriété de l'instance de A appelée par la classe B et en même temps je déclenche l'événement Change du module A.
    Plusieurs remarques :

    [LIST][*]Je pourrais affecter directement une valeur à cette propriété en écrivant quelques chose comme : myB.mA.Value = 19 , non ?[...]
    Ca aurait pu être une procédure. Mais ce n'est pas forcément la meilleure des choses à écrire et tu n'aurais pas pu écrire directement myB.mA.Value = 19 car mMyA n'est pas publique. Il serait plus intéressant de créer une propriété publique de B qui expose une propriété qui pointe vers l'instance privée de A

    Code de la classe A
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    Option Explicit
     
    Public Event Change()
    Private mMyValue As Long
     
    Property Get MyValue() As Long
      MyValue = mMyValue
    End Property
     
    Property Let MyValue(Value As Long)
      mMyValue = Value
      If Value > 10 Then RaiseEvent Change
    End Property
    Code de la classe B
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    Option Explicit
     
    Private WithEvents mMyA As A
     
    Property Get MyA() As A
      Set MyA = mMyA
    End Property
     
    Private Sub Class_Initialize()
      Set mMyA = New A
    End Sub
    Tu pourrais alors avoir la syntaxe que tu proposes
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    Sub TEst()
      Dim MyB As New B
     
      MyB.MyA.MyValue = 25
    End Sub
    Citation Envoyé par mc222 Voir le message
    [...]
    • Comme tu dis par la suite, l'événement est déclenché au niveau de la classe A, et non B.
    • Comme tu le dis également par la suite, la classe A communique directement avec l'utilisateur (msgbox) sans passer par ses propriétés, fonctions ou ses événements (violation du principe d'encapsulation).
    [...]
    Ce n'est pas A qui communique avec l'utilisateur, c'est B . L'événement de A ne peut être intercepté que par le module de classe qui a créé l'instance de A avec WithEvents. C'est pourquoi ton formulaire ne saurait pas détecter directement l'événement de A et que tu dois passer par B


    Citation Envoyé par mc222 Voir le message
    [...]
    tu propose donc une deuxième solution , où l'on déclare un événement directement dans B, qui prend le relais à Change en quelques sorte, c'est à ça que j'avais pensé.
    J'ai réussi à faire tourner cette solution.[...]
    Oui, mais tu ne peux faire cela que si B peut déterminer qu'en modifiant A, il lèvera à coup sûr l'événement de A.


    Citation Envoyé par mc222 Voir le message
    [...]
    Dans ta troisième solution, tu suppose que l’événement Change n'est pas nécessairement déclenché dès qu'on modifie la propriété mA.Value.
    Est-ce bien cela ?[...]
    Oui, c'est bien cela. C'est pourquoi B intercepte l'événement de A et lève son propre événement utilisable par le userform qui a déclaré B avec WithEvents.


    Citation Envoyé par mc222 Voir le message
    [...]
    Autre question, quand on créé une instance objet de classe B, on utilise New B ce qui nous amène à l'initialisation de B (événement B_initialize) ou l'on aura écrit New A . On arrive donc à l'initialisation de A (événement A_initilize) et donc aux données de classe simple qui ne nécessitent pas de New. Il ne faut pas détruire les pointeurs en fin de procédure avec un événement terminate ?

    Voila, désolé d'être un peu confus, je débute dans ce domaine.

    Merci.
    Pas de soucis

    Normalement, il faudrait détruire mMyA dans l'événement Terminate de B. Tu as raison.

    Si cela t'intéresse, j'ai écrit il y a longtemps un truc sur les classes perso, mais je n'y parle pas de propriétés d'une classe perso qui serait elle-même une classe perso, ni de la cascade d'événements. Par contre, j'y parle du débugueur de VBA et si tu veux voir le cheminement du code, je te propose de l'utiliser. Tu mets un point d'arrêt sur la procédure du userform qui amènera à la modif de la valeur de A puis lorsque le code s'arrête sur cette ligne, tu avances avec F8 pour suivre l'exécution en pas-à-pas.
    "Plus les hommes seront éclairés, plus ils seront libres" (Voltaire)
    ---------------
    Mes billets de blog sur DVP
    Mes remarques et critiques sont purement techniques. Ne les prenez jamais pour des attaques personnelles...
    Pensez à utiliser les tableaux structurés. Ils vous simplifieront la vie, tant en Excel qu'en VBA ==> mon tuto
    Le VBA ne palliera jamais une mauvaise conception de classeur ou un manque de connaissances des outils natifs d'Excel...
    Ce ne sont pas des bonnes pratiques parce que ce sont les miennes, ce sont les miennes parce que ce sont des bonnes pratiques
    VBA pour Excel? Pensez D'ABORD en EXCEL avant de penser en VBA...
    ---------------

  5. #5
    Membre à l'essai
    Homme Profil pro
    Étudiant
    Inscrit en
    Mars 2018
    Messages
    4
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mars 2018
    Messages : 4
    Par défaut
    Bonjour Pierre,

    Ca aurait pu être une procédure. Mais ce n'est pas forcément la meilleure des choses à écrire et tu n'aurais pas pu écrire directement myB.mA.Value = 19 car mMyA n'est pas publique. Il serait plus intéressant de créer une propriété publique de B qui expose une propriété qui pointe vers l'instance privée de A
    Oui, en y réfléchissant bien, c'est plus pratique.
    En fait, ma classe A a une propriété string que j'ai appelé Value et plusieurs méthode pour construire ce Value proprement.
    Donc, comme tu le proposes, j'ai recréé ces méthodes publiques sur B pour qu'elles manipulent une instance de classe A appelée dans B en tant que propriété privée.
    Finalement, j'ai un property Get qui vient récupérer la propriété Value de mon instance pour l'injecter dans une propriété publique de B de classe string. C'est bien plus commode ainsi (j'espère que c'est compréhensible).

    Tu pourrais alors avoir la syntaxe que tu proposes
    Du coup j'ai abandonné ça.

    De même pour les événements en cascades, je me suis rendu compte que je pouvais arriver à la même chose plus simplement

    Si cela t'intéresse, j'ai écrit il y a longtemps un truc sur les classes perso
    Oui, je l'ai déjà lu mais il faudrait peu être que j'y remette le nez. En tout cas bravo pour ta contribution, en dehors de tes travaux, il n'y a pas grand chose en français traitant le sujet sur internet.

    Merci beaucoup.

Discussions similaires

  1. Find Range dans module de classe
    Par ylemasson dans le forum Macros et VBA Excel
    Réponses: 1
    Dernier message: 21/10/2011, 11h45
  2. Pb ajout objet dans module de classe
    Par steph6499 dans le forum VB 6 et antérieur
    Réponses: 0
    Dernier message: 06/02/2011, 17h55
  3. Pb Menu OnAction dans Module de Classe
    Par Laurent GAUDILLIER dans le forum Macros et VBA Excel
    Réponses: 10
    Dernier message: 23/03/2009, 18h37
  4. Gestion erreur dans module de class E97
    Par Eric_03 dans le forum Macros et VBA Excel
    Réponses: 3
    Dernier message: 15/12/2008, 16h45
  5. Utilisation composant timer dans module de classe
    Par MaximeM dans le forum VB 6 et antérieur
    Réponses: 3
    Dernier message: 26/06/2007, 13h35

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo