Class Module pour associer une macro à un bouton dynamiquement crée

Bonjour tout le monde,

J'ai besoin de votre aide. Je bloque sur l’exécution de mon "class module" et je n'arrive pas à trouver la solution. Je vous explique la macro :

  • Un Userform demande à l'utilisateur de renseigner les données d'entrées concernant le nombre de document par catégorie (4 catégories : GT, ST, Gen, HRSG) image
  • En fonction des inputs du Userform2, un autre UserForm (UserForm3) s'ouvre pour demander à l'utilisateur de charger des documents référence (dans l'exemple ci dessous les inputs sont GT = 4, ST = 2, Gen = 2, HRSG = 2) image
  • Lorsque qu'on click sur les boutons "Load Source", je veux qu'un explorateur de fichier s'ouvre afin de sélectionner le fichier souhaité

Le problème que je rencontre est le suivant : J'ai réussi à ouvrir un explorateur de fichier quand on click sur le bouton mais ceci fonctionne seulement pour le dernier bouton de chaque catégorie. Pour chaque bouton créer je veux que la macro du class module 2 s'éxécute.

Voici le code dans mon UserForm2

' Déclarez la collection globalement pour gérer les instances des handlers de boutons
Public ButtonHandlers As Collection
Public ButtonHandlers2 As Collection

Sub InitializeButtonHandlers()
    Set ButtonHandlers = New Collection
End Sub

Sub InitializeButtonHandlers2()
    Set ButtonHandlers2 = New Collection
End Sub

Public Sub BtnUF2_Click()
    Dim uf3 As UserForm3
    Set uf3 = New UserForm3

    ' Retrieve values from UserForm2 textboxes
    Dim GTcount As Integer
    Dim STcount As Integer
    Dim Gencount As Integer
    Dim HRSGcount As Integer

    GTcount = Val(Me.GTinput.Value)
    STcount = Val(Me.STinput.Value)
    Gencount = Val(Me.Geninput.Value)
    HRSGcount = Val(Me.HRSGinput.Value)

    ' Initialize the collection if needed
    If ButtonHandlers Is Nothing Then
        Set ButtonHandlers = New Collection
    End If

    If ButtonHandlers2 Is Nothing Then
        Set ButtonHandlers2 = New Collection
    End If

'--------------------------------------------------------------------------------------------------

    ' Create textboxes and buttons for GT
    Dim i As Integer
    Dim topPosGT As Integer
    topPosGT = 30 ' Initial vertical position of controls

    For i = 1 To GTcount
        Dim tbGT As MSForms.TextBox
        Set tbGT = uf3.Controls.Add("Forms.TextBox.1")
        tbGT.Name = "GTfile" & i
        tbGT.Top = topPosGT
        tbGT.Left = 25
        tbGT.Width = 100

        Dim btnGT As MSForms.CommandButton
        Set btnGT = uf3.Controls.Add("Forms.CommandButton.1")
        btnGT.Caption = "Load GT Source " & i
        btnGT.Name = "GTbutton" & i
        btnGT.Top = topPosGT + 20
        btnGT.Left = 25
        btnGT.Width = 100

          ' Create an event handler for the button
        Dim btnGTHandler As clsButtonHandler2
        Set btnGTHandler = New clsButtonHandler2
        Set btnGTHandler.btn = btnGT
        btnGTHandler.SetTextBox tbGT

        ' Add the handler to the collection to keep it alive
        ButtonHandlers2.Add btnGTHandler

        topPosGT = topPosGT + 50

    Next i

'-------------Repeat the process for ST, Gen, and HRSG-------------

    ' Create textboxes and buttons for ST
    Dim j As Integer
    Dim topPosST As Integer
    topPosST = 30

    For j = 1 To STcount
        Dim tbST As MSForms.TextBox
        Set tbST = uf3.Controls.Add("Forms.TextBox.1")
        tbST.Name = "STfile" & j
        tbST.Top = topPosST
        tbST.Left = 175
        tbST.Width = 100

        Dim btnST As MSForms.CommandButton
        Set btnST = uf3.Controls.Add("Forms.CommandButton.1")
        btnST.Caption = "Load ST Source " & j
        btnST.Name = "STbutton" & j
        btnST.Top = topPosST + 20
        btnST.Left = 175
        btnST.Width = 100

        ' Create an event handler for the button
        Dim btnSTHandler As clsButtonHandler2
        Set btnSTHandler = New clsButtonHandler2
        Set btnSTHandler.btn = btnST
        btnSTHandler.SetTextBox tbST

        ' Add the handler to the collection to keep it alive
        ButtonHandlers2.Add btnSTHandler

        topPosST = topPosST + 50
    Next j

'----------------------------------------------------------

    ' Create textboxes and buttons for Gen
    Dim k As Integer
    Dim topPosGen As Integer
    topPosGen = 30

    For k = 1 To Gencount
        Dim tbGen As MSForms.TextBox
        Set tbGen = uf3.Controls.Add("Forms.TextBox.1")
        tbGen.Name = "Genfile" & k
        tbGen.Top = topPosGen
        tbGen.Left = 325
        tbGen.Width = 100

        Dim btnGen As MSForms.CommandButton
        Set btnGen = uf3.Controls.Add("Forms.CommandButton.1")
        btnGen.Caption = "Load Gen Source " & k
        btnGen.Name = "Genbutton" & k
        btnGen.Top = topPosGen + 20
        btnGen.Left = 325
        btnGen.Width = 100

        ' Create an event handler for the button
        Dim btnGenHandler As clsButtonHandler2
        Set btnGenHandler = New clsButtonHandler2
        Set btnGenHandler.btn = btnGen
        btnGenHandler.SetTextBox tbGen

        ' Add the handler to the collection to keep it alive
        ButtonHandlers2.Add btnGenHandler

        topPosGen = topPosGen + 50
    Next k

'----------------------------------------------------------

    ' Create textboxes and buttons for HRSG
    Dim l As Integer
    Dim topPosHRSG As Integer
    topPosHRSG = 30

    For l = 1 To HRSGcount
        Dim tbHRSG As MSForms.TextBox
        Set tbHRSG = uf3.Controls.Add("Forms.TextBox.1")
        tbHRSG.Name = "HRSGfile" & l
        tbHRSG.Top = topPosHRSG
        tbHRSG.Left = 475
        tbHRSG.Width = 100

        Dim btnHRSG As MSForms.CommandButton
        Set btnHRSG = uf3.Controls.Add("Forms.CommandButton.1")
        btnHRSG.Caption = "Load HRSG Source " & l
        btnHRSG.Name = "HRSGbutton" & l
        btnHRSG.Top = topPosHRSG + 20
        btnHRSG.Left = 475
        btnHRSG.Width = 100

        ' Create an event handler for the button
        Dim btnHRSGHandler As clsButtonHandler2
        Set btnHRSGHandler = New clsButtonHandler2
        Set btnHRSGHandler.btn = btnHRSG
        btnHRSGHandler.SetTextBox tbHRSG

        ' Add the handler to the collection to keep it alive
        ButtonHandlers2.Add btnHRSGHandler

        topPosHRSG = topPosHRSG + 50
    Next l

'--------------------------------------------------------------------------------------------------

    ' Calculate the height needed for UserForm3
    Dim maxHeight As Integer
    maxHeight = Application.WorksheetFunction.Max(topPosGT, topPosST, topPosGen, topPosHRSG) + 50

    ' Adjust the size of UserForm3
    uf3.Width = 600
    uf3.Height = maxHeight

    ' Add the UF3Btn button
    Dim btnUF3 As MSForms.CommandButton
    Set btnUF3 = uf3.Controls.Add("Forms.CommandButton.1")
    btnUF3.Caption = "Go To Work"
    btnUF3.Name = "UF3Btn"
    btnUF3.Top = maxHeight - 50
    btnUF3.Left = 25
    btnUF3.Width = 550
    btnUF3.Height = 20

    ' Create an event handler for the button
    Dim btnUF3Handler As clsButtonHandler
    Set btnUF3Handler = New clsButtonHandler
    Set btnUF3Handler.btn = btnUF3

    ' Add the handler to the collection to keep it alive
    ButtonHandlers.Add btnUF3Handler

    ' Show UserForm3 and hide UserForm2
    Unload Me
    uf3.Show
End Sub

Voici le code dans mon class module 2 :

' Dans la classe clsButtonHandler2
Public WithEvents btn As MSForms.CommandButton
Public associatedTextBox As MSForms.TextBox

' Setter pour associer une TextBox
Public Sub SetTextBox(tb As MSForms.TextBox)
    Set associatedTextBox = tb
End Sub

Private Sub btn_Click()
    Dim fd As FileDialog
    Set fd = Application.FileDialog(msoFileDialogFilePicker)

    With fd
        .Title = "Select a file"
        .AllowMultiSelect = False
        .Filters.Clear
        .Filters.Add "All Files", "*.*"

        If .Show = -1 Then
            ' File selected, update the associated TextBox
            associatedTextBox.Text = .SelectedItems(1)
        End If
    End With
End Sub

Je vous remercie de votre aide, et si des informations supplémentaire sont nécessaire dite le moi.

Bonne Journée

Bonjour stepniak,

Sans fichier pour tester ...

on click sur le bouton mais ceci fonctionne seulement pour le dernier bouton de chaque catégorie.

C'est normal actuellement, car une bouche "For" recommence le traitement pour tous les boutons de la section.

Au lieu du "For", teste i = x , x représentant le numéro du bouton de cette section. C'est pareil pour j et k.
Le tout suivi de l'action à faire.

Bizz

Merci beaucoup pour ton retour. Cependant le problème est toujours présent.

Trouvez en pièce jointe fichier excel afin de tester.

14trackeritpl-v2.xlsm (63.18 Ko)

Bonjour,

Le fichier joint n'aide en rien car sans vos documents liés on ne peut atteindre l'UF en question...

Vous devriez ajouter l'initialisation de vos collections dans le sub Userform_Initialize(), activé automatiquement lors de la création de l'instance. Ca éviterai vos "tests if needed".

Ensuite je pense que vous avez un(/ de nombreux ?) problèmes de scopes/portée de vos variables. Si les collections/leurs boutons se trouvent dans l'UF3, que font-elles dans l'UF2 ?

Si vous souhaitez approcher les userforms d'une manière orientée objet (ce que je salue), vous devriez vraiment revoir votre code :

Les UF sont des interfaces, définir vos objets représentant vos données à l'intérieur meme de leurs définitions/fonctions est vraiment une mauvaise idée : si un UF est fermé/mis a jour ou autre vos datas disparaissent de manière mystérieuse... comme c'est le cas pour vos listes de boutons en l'occurence.

Typiquement tout le processus de construction de UF3 devrait se trouver dans le module de UF3 / une procédure, au lieu de BtnUF2_Click.

La démarche a suivre, robuste, consiste à définir d'un coté :

- votre Modèle avec ses données et opérations

- de l'autre votre interface faisant appel auxdites opérations. Aucun lien direct avec les données.

Cela demande une certaine abstraction et du recul, pas facile quand on débute...

Essayez donc de modifier l'emplacement de définition de vos listes de boutons et je pense que vous pouvez résoudre votre problème.

Enfin de manière générale, avec l'utilisation de classes votre code devrait etre structuré ainsi :

Modules de classes : objets et opérations les concernant

Modules UFs : interface utilisateur pour la manipulation de ces classes, via les opérations prédéfinies dans vos classes

Modules classiques : Structure du programme, par exemple :

Sub clickInitial()
  Dim obj As myClass
  Set obj = New myClass

  Dim interface1 As UserForm1
  SetAttr interface1 = New UserForm1
  interface1.Show
  ' appel de doSecondStep via les clics dans l'interface
End Sub

Sub doSecondStep(myDatas As myClass)
  myDatas.doOperation
End Sub

Sans un fichier plus fonctionnel, difficile de vous aider davantage.

Re, effectivement en placant les collections de boutons dans le module de l'UF3 votre problème est résolu. Ci-joint votre fichier modifié et ci-après le code revu des classes UserForm2 et UserForm.
20trackeritpl-v2.xlsm (70.44 Ko)

Classe UserForm2 :

Public Sub BtnUF2_Click()
    Dim uf3 As UserForm3
    Set uf3 = New UserForm3

    ' Show UserForm3 and hide UserForm2
    uf3.DefineInterface Val(Me.GTinput.Value), Val(Me.STinput.Value), Val(Me.Geninput.Value), Val(Me.HRSGinput.Value)
    Unload Me
    uf3.Show
End Sub

Classe UserForm3 :

' Déclarez la collection globalement pour gérer les instances des handlers de boutons
Private ButtonHandlers As Collection
Private ButtonHandlers2 As Collection

Private Sub InitializeButtonHandlers()
    Set ButtonHandlers = New Collection
End Sub

Private Sub InitializeButtonHandlers2()
    Set ButtonHandlers2 = New Collection
End Sub

Sub UF3Btn_Click()

    Unload UserForm3

End Sub
Private Sub UserForm_Initialize()

    Me.Caption = "UserForm3"

    InitializeButtonHandlers
    InitializeButtonHandlers2
End Sub

Public Sub DefineInterface(GTcount As Long, STcount As Long, Gencount As Long, HRSGcount As Long)

    '------------ 1/4 Create textboxes and buttons for GT ------------
    Dim i As Long
    Dim topPosGT As Long
    topPosGT = 30 ' Initiai verticai position of controls

    For i = 1 To GTcount
        Dim tbGT As MSForms.TextBox
        Set tbGT = Me.Controls.Add("Forms.TextBox.1")
        tbGT.Name = "GTfile" & i
        tbGT.Top = topPosGT
        tbGT.Left = 25
        tbGT.Width = 100

        Dim btnGT As MSForms.CommandButton
        Set btnGT = Me.Controls.Add("Forms.CommandButton.1")
        btnGT.Caption = "Load GT Source " & i
        btnGT.Name = "GTbutton" & i
        btnGT.Top = topPosGT + 20
        btnGT.Left = 25
        btnGT.Width = 100

          ' Create an event handler for the button
        Dim btnGTHandler As clsButtonHandler2
        Set btnGTHandler = New clsButtonHandler2
        Set btnGTHandler.btn = btnGT
        btnGTHandler.SetTextBox tbGT

        ' Add the handler to the collection to ieep it alive
        ButtonHandlers2.Add btnGTHandler

        topPosGT = topPosGT + 50

    Next i

'-------------2/4 Repeat the process for ST-------------
    Dim topPosST As Long
    topPosST = 30

    For i = 1 To STcount
        Dim tbST As MSForms.TextBox
        Set tbST = Me.Controls.Add("Forms.TextBox.1")
        tbST.Name = "STfile" & i
        tbST.Top = topPosST
        tbST.Left = 175
        tbST.Width = 100

        Dim btnST As MSForms.CommandButton
        Set btnST = Me.Controls.Add("Forms.CommandButton.1")
        btnST.Caption = "Load ST Source " & i
        btnST.Name = "STbutton" & i
        btnST.Top = topPosST + 20
        btnST.Left = 175
        btnST.Width = 100

        ' Create an event handler for the button
        Dim btnSTHandler As clsButtonHandler2
        Set btnSTHandler = New clsButtonHandler2
        Set btnSTHandler.btn = btnST
        btnSTHandler.SetTextBox tbST

        ' Add the handler to the collection to ieep it alive
        ButtonHandlers2.Add btnSTHandler

        topPosST = topPosST + 50
    Next i

' ------------ 3/4 Create textboxes and buttons for Gen ------------
    Dim topPosGen As Long
    topPosGen = 30

    For i = 1 To Gencount
        Dim tbGen As MSForms.TextBox
        Set tbGen = Me.Controls.Add("Forms.TextBox.1")
        tbGen.Name = "Genfile" & i
        tbGen.Top = topPosGen
        tbGen.Left = 325
        tbGen.Width = 100

        Dim btnGen As MSForms.CommandButton
        Set btnGen = Me.Controls.Add("Forms.CommandButton.1")
        btnGen.Caption = "Load Gen Source " & i
        btnGen.Name = "Genbutton" & i
        btnGen.Top = topPosGen + 20
        btnGen.Left = 325
        btnGen.Width = 100

        ' Create an event handler for the button
        Dim btnGenHandler As clsButtonHandler2
        Set btnGenHandler = New clsButtonHandler2
        Set btnGenHandler.btn = btnGen
        btnGenHandler.SetTextBox tbGen

        ' Add the handler to the collection to ieep it alive
        ButtonHandlers2.Add btnGenHandler

        topPosGen = topPosGen + 50
    Next i

'----------------------------------------------------------

    ' Create textboxes and buttons for HRSG
    Dim topPosHRSG As Long
    topPosHRSG = 30

    For i = 1 To HRSGcount
        Dim tbHRSG As MSForms.TextBox
        Set tbHRSG = Me.Controls.Add("Forms.TextBox.1")
        tbHRSG.Name = "HRSGfile" & i
        tbHRSG.Top = topPosHRSG
        tbHRSG.Left = 475
        tbHRSG.Width = 100

        Dim btnHRSG As MSForms.CommandButton
        Set btnHRSG = Me.Controls.Add("Forms.CommandButton.1")
        btnHRSG.Caption = "Load HRSG Source " & i
        btnHRSG.Name = "HRSGbutton" & i
        btnHRSG.Top = topPosHRSG + 20
        btnHRSG.Left = 475
        btnHRSG.Width = 100

        ' Create an event handler for the button
        Dim btnHRSGHandler As clsButtonHandler2
        Set btnHRSGHandler = New clsButtonHandler2
        Set btnHRSGHandler.btn = btnHRSG
        btnHRSGHandler.SetTextBox tbHRSG

        ' Add the handler to the collection to ieep it alive
        ButtonHandlers2.Add btnHRSGHandler

        topPosHRSG = topPosHRSG + 50
    Next i

'--------------------------------------------------------------------------------------------------

    ' Calculate the height needed for UserForm3
    Dim maxHeight As Long
    maxHeight = Application.WorksheetFunction.Max(topPosGT, topPosST, topPosGen, topPosHRSG) + 50

    ' Adiust the size of UserForm3
    Me.Width = 600
    Me.Height = maxHeight

    ' Add the UF3Btn button
    Dim btnUF3 As MSForms.CommandButton
    Set btnUF3 = Me.Controls.Add("Forms.CommandButton.1")
    btnUF3.Caption = "Go To Work"
    btnUF3.Name = "UF3Btn"
    btnUF3.Top = maxHeight - 50
    btnUF3.Left = 25
    btnUF3.Width = 550
    btnUF3.Height = 20

    ' Create an event handler for the button
    Dim btnUF3Handler As clsButtonHandler
    Set btnUF3Handler = New clsButtonHandler
    Set btnUF3Handler.btn = btnUF3

    ' Add the handler to the collection to ieep it alive
    ButtonHandlers.Add btnUF3Handler
End Sub

Merci beaucoup pour tous vos retour et commentaire, et merci pour la résolution de mon problème !

Bonne journée à vous !

Rechercher des sujets similaires à "class module associer macro bouton dynamiquement cree"