在VBA中从表单(Form)传递变量到模块(Module)

27

我在表单上有以下按钮:

Private Sub CommandButton1_Click()
 Dim pass As String
 pass = UserForm1.TextBox1
 Unload UserForm1
End Sub

然后我有一个名为Module1的模块:

 Public Sub Login()

 ...

 UserForm1.Show
 driver.findElementByName("PASSWORD").SendKeys pass

 ...

End Sub

这个想法是,用户在输入框中输入的任何密码都将分配给变量pass。然而,我遇到的问题是如何将pass从UserForm1传递到Module1的Login子程序。

我原以为在卸载表单之前在我的表单中添加类似于Module1.Login (pass)的内容会起作用,但似乎没有传递任何东西。非常感谢您的任何帮助。谢谢。

2个回答

42

不要在用户窗体中声明变量。请在模块中声明为Public

Public pass As String

在用户窗体中

Private Sub CommandButton1_Click()
    pass = UserForm1.TextBox1
    Unload UserForm1
End Sub

在模块中

Public pass As String

Public Sub Login()
    '
    '~~> Rest of the code
    '
    UserForm1.Show
    driver.findElementByName("PASSWORD").SendKeys pass
    '
    '~~> Rest of the code
    '
End Sub

在调用 driver.find... 行之前,你可能还想添加一个附加检查吗?

If Len(Trim(pass)) <> 0 Then
这将确保不传递空字符串。

7
+1 Sid这是一个好的做法。另外,您可以隐藏Userform1而不是卸载它,并从模块访问UserForm1.TextBox1,然后卸载userform1,但是对于使用哪种方法的决定取决于整个实现。 - user2140173
啊哈,我从来没有想过将变量声明为公共的。这是一个非常好的提示,我以后会更多地这样做的,谢谢! - JimmyK
最好在使用完表单后卸载它。这样更有利于内存和安全性。 - TylerH
4
只因为你正在使用窗体的默认实例,所以@TylerH所说的。把窗体视为对象,而不是全局状态的UI,这样就不存在"内存"和"安全性"的争议了。 - Mathieu Guindon
在 UserForm1.Show 之后,我尝试了 MsgBox pass 但是什么也没有弹出,UserForm 输入的值没有传递到模块! - KiDo
显示剩余2条评论

30

Siddharth的答案不错,但依赖全局作用域变量。有更好、更面向对象友好的方法。

UserForm是一个类模块,和其他任何类一样——唯一的区别在于它具有隐藏的VB_PredeclaredId属性设置为True,这使得VB创建一个名为该类的全局范围对象变量——这就是你可以编写UserForm1.Show而不创建该类新实例的方式。

远离这个做法,把你的表单当作一个对象处理——公开Property Get成员并将表单控件抽象化——调用代码无论如何都不关心控件

Option Explicit
Private cancelling As Boolean

Public Property Get UserId() As String
    UserId = txtUserId.Text
End Property

Public Property Get Password() As String
    Password = txtPassword.Text
End Property

Public Property Get IsCancelled() As Boolean
    IsCancelled = cancelling
End Property

Private Sub OkButton_Click()
    Me.Hide
End Sub

Private Sub CancelButton_Click()
    cancelling = True
    Me.Hide
End Sub

Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
    If CloseMode = VbQueryClose.vbFormControlMenu Then
        cancelling = True
        Cancel = True
        Me.Hide
    End If
End Sub

现在,调用代码可以这样做(假设UserForm被命名为 LoginPrompt):

With New LoginPrompt
    .Show vbModal
    If .IsCancelled Then Exit Sub
    DoSomething .UserId, .Password
End With

其中DoSomething将是需求这两个字符串参数的某个过程:

Private Sub DoSomething(ByVal uid As String, ByVal pwd As String)
    'work with the parameter values, regardless of where they came from
End Sub

@RandomDownvoter,非常感谢您的反馈;我一直乐于接受新的方法来改善我的答案。目前我有点困惑,不知道避免全局状态并通过简单的抽象提高代码的表现力和健壮性有什么问题。干杯。 - Mathieu Guindon
1
我喜欢这种将表单与数据和方法分离的方法。我该如何以一种用户界面仍可见的方式实现它?以上面的例子为例,如果ID或密码不正确,表单是否可以保持打开并给出一些警告? - Robert Todar
非常感谢提供的信息,我正在阅读该网站并练习运用到我的代码中。顺便提一下,该网站上的下载链接已经失效了。再次感谢! - Robert Todar
1
了不起的解决方案。但在“QueryClose”上有一个错误。我会在下面发布修复链接。 由于我花了30分钟来尝试解决它,所以我将它添加到这里以备将来参考,因为这是从表单传递值到模块的谷歌搜索结果中的第一个。 https://dev59.com/r6zka4cB1Zd3GeqP_6ah - hend
1
@Hend 谢谢你提醒我!请注意,这段代码确实调用了 Me.Hide,但我从未遇到过任何问题(虽然这有点简化了,实际上我将状态标志切换和表单隐藏放在了一个私有的 OnCancel 方法中,我从 QueryClose 和一些 CancelButton_Click 处理程序中调用该方法;链接问题中的代码似乎缺少了隐藏表单的部分,并且缺少了 Cancel = True,这就是导致错误的原因,因为不取消会使表单对象自毁。我正在修复这个问题,谢谢!) - Mathieu Guindon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接