web-dev-qa-db-fra.com

Les variables publiques ne sont pas vraiment publiques dans VBA dans les formulaires

Vous trouverez ci-dessous une question à laquelle je répondrai moi-même. Toutefois, cela m'a causé beaucoup de frustration et j'ai eu beaucoup de difficulté à la rechercher sur le Web. Je poste ici dans l'espoir de gagner du temps et de faire des efforts pour les autres, et peut-être pour moi si j'oublie cela à l'avenir:

Pour VBA (dans mon cas, MS Excel), la déclarationPublic EST SUPPOSÉE RENDRE LA VARIABLE (OU LA FONCTION) GLOBALEMENT ACCESSIBLE PAR D’AUTRES FONCTIONS OU SOUS-ROUTINES DE CE MODULE, AINSI QUE DE TOUT AUTRE MODULE.

IL S'AVÈRE QUE CE N'EST PAS VRAI DANS LE CAS DE Forms
, et je soupçonne également dans Sheets, mais je n'ai pas vérifié le dernier.

En bref, les suivants NE créeront PAS de variable publique/accessible lorsqu’ils sont créés dans un Form, et planteront donc, indiquant que les variables bYesNo et dRate ne sont pas définies dans mModule1:

(inside fMyForm)
Public bYesNo As Boolean`
Public dRate As Double

Private Sub SetVals()
    bYesNo = Me.cbShouldIHaveADrink.value
    dRate = CDec(Me.tbHowManyPerHour.value)
End Sub
(Presume the textbox & checkbox are defined in the form)

(inside mModule1)
Private Sub PrintVals()
    Debug.Print CStr(bYesNo)
    Debug.Print CStr(dRate)
End Sub


Cependant, si vous faites la légère modification ci-dessous, tout fonctionnera correctement:

(inside fMyForm)

Private Sub SetVals()
    bYesNo = Me.cbShouldIHaveADrink.value
    dRate = CDec(Me.tbHowManyPerHour.value)
End Sub
(Presume the textbox & checkbox are defined in the form)

(inside mModule1)
Public bYesNo As Boolean`
Public dRate As Double
Private Sub PrintVals()
    Debug.Print CStr(bYesNo)
    Debug.Print CStr(dRate)
End Sub


mModule1 fonctionnera parfaitement et, en supposant que le fMyForm soit toujours appelé en premier, puis, au moment où la routine PrintVals est exécutée, les valeurs de la zone de texte et de la case à cocher du formulaire seront correctement capturées.

Honnêtement, je ne peux vraiment pas comprendre ce que MS pensait de ce changement, mais le manque d'uniformité nuit à l'efficacité, car il permet d'apprendre des idiosyncracies comme celles-ci, qui sont si peu documentées qu'une recherche Google en 2013 est probablement si difficile à chercher.

10
Mike Williamson

Premier commentaire:

Les modules Userform et Sheet sont des modules Object: ils ne se comportent pas de la même manière qu'un module standard. Vous pouvez toutefois faire référence à une variable dans un formulaire utilisateur de la même manière que vous feriez référence à une propriété de classe. Dans votre exemple, faire référence à fMyForm.bYesNo fonctionnerait correctement. Si vous n'aviez pas déclaré bYesNo en tant que public, le code ne serait pas visible en dehors du formulaire. Ainsi, lorsque vous le rendez public, il est vraiment différent de non public. - Tim Williams 11 avr. 13 à 21:39 

est en fait une réponse correcte ...

4
Ota Milink

En guise de réponse rapide à la réponse de la communauté, juste pour vous prévenir:

Lorsque vous instanciez vos formulaires, vous pouvez utiliser l'objet de formulaire lui-même ou vous pouvez créer une nouvelle instance de l'objet de formulaire en utilisant Nouveau et en le plaçant dans une variable. La dernière méthode est la méthode IMO la plus propre, car elle rend l’utilisation moins singleton-ish.

Toutefois, lorsque vous appelez Décharger (Me) dans votre formulaire utilisateur, les membres all public seront effacés. Donc, si votre code va comme ceci:

  Dim oForm as frmWhatever
  Set oForm = New frmWhatever
  Call oForm.Show(vbModal)
  If Not oForm.bCancelled Then  ' <- poof - bCancelled is wiped clean at this point

La solution que j’utilise pour éviter cela, et c’est une solution alternative intéressante pour le PO également, consiste à capturer tous les IO avec le formulaire (c’est-à-dire tous les membres publics) dans une classe séparée et à utiliser une instance. de cette classe pour communiquer avec le formulaire. Donc, par exemple.

  Dim oFormResult As CWhateverResult
  Set oFormResult = New CWhateverResult
  Dim oForm as frmWhatever
  Set oForm = New frmWhatever
  Call oForm.Initialize(oFormResult)
  Call oForm.Show(vbModal)
  If Not oFormResult.bCancelled Then  ' <- safe
1
Carl Colijn