【问题标题】:Passing variable from Form to Module in VBA在VBA中将变量从表单传递到模块
【发布时间】:2013-12-11 10:02:55
【问题描述】:

我在表单上有以下按钮:

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 的登录子。

我想在我卸载它之前在我的表单中添加类似Module1.Login (pass) 的东西会起作用,但这似乎没有通过任何东西。任何帮助将非常感激。谢谢。

【问题讨论】:

    标签: excel vba module userform


    【解决方案1】:

    Siddharth 的回答很好,但依赖于全局范围的变量。有一种更好、对 OOP 更友好的方式。

    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
    

    现在调用代码可以做到这一点(假设用户窗体被命名为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 的反馈总是值得赞赏的;我总是乐于接受改进答案的新方法。现在我有点困惑,避免全局状态和通过简单的抽象来增强代码的表现力和健壮性有什么问题。干杯。
    • 我喜欢这种将表单与数据和方法分离的方法。我将如何以用户窗体可以保持可见的方式实现这一点。使用上面的示例,假设返回的 ID 或密码不正确,表单是否可以保持打开状态并给出警告?
    • @RobertTodar 在这种情况下,您将在视图/表单中声明一个事件,并在演示者中处理它(然后必须是一个类模块,并为视图/表单声明 WithEvents )。有点像'Apply' logic for UserForm dialog
    • 惊人的解决方案。不过,“QueryClose”有一个错误。我在下面发布了修复的链接。因为我花了 30 分钟试图解决它,所以我在这里添加以供将来参考,因为这是谷歌搜索将值从表单传递到模块 stackoverflow.com/questions/51179449/… 时的第一个结果
    • @Hend 感谢您的提醒!请注意,这段代码确实调用了Me.Hide,我从来没有遇到过任何问题(尽管这有点简化,我实际上将状态标志切换+表单隐藏在我调用的私有OnCancel方法中来自QueryClose 和一些CancelButton_Click 处理程序;链接问题中的代码似乎缺少表单的隐藏,缺少Cancel = True 是导致错误的原因,因为不取消这会使表单对象自我破坏。我正在解决这个问题,谢谢!
    【解决方案2】:

    不要在用户表单中声明变量。在模块中声明为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
    

    这将确保不会传递空字符串。

    【讨论】:

    • +1 Sid 这是一个很好的方法。或者,您可以隐藏 Userform1 而不是卸载它并从模块中访问 UserForm1.TextBox1 然后 Unload userform1 但方法的决定取决于整个实现
    • 啊哈,我从没想过将变量声明为 Public。这是一个非常棒的提示,我将不得不开始做更多的事情,谢谢!
    • @user2140173 最好在完成后卸载表单。更好的内存和安全性。
    • @TylerH 只是因为您正在使用表单的默认实例。将表单视为对象而不是全局状态 UI,“内存”和“安全”参数都不再成立。
    猜你喜欢
    • 2021-11-23
    • 1970-01-01
    • 2015-03-01
    • 1970-01-01
    • 2015-12-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-12
    相关资源
    最近更新 更多