Excel 模块相对于无模式用户窗体的VBA最佳实践

Excel 模块相对于无模式用户窗体的VBA最佳实践,excel,vba,user-input,modeless,program-flow,Excel,Vba,User Input,Modeless,Program Flow,我遇到了类似的问题,并阅读了回复: 我一直试图在我自己的情况下应用由提供的建议。在我的情况下,我似乎找不到一种既采纳泽门先生的建议又不利用后藤的方法 我想知道是否有更好或更优雅的解决方案 以下是我所做工作的概要: 我有一个带有命令按钮的UserForm,它开始执行代码,在多个Excel工作簿上执行多个操作。因此,存在多个代码块,一个代码块的成功完成允许执行后续代码块 在某一点上,根据情况,代码可能需要用户输入;在其他情况下,可以从Excel中获取所需的数据。如果需要用户输入,将显示另一个User

我遇到了类似的问题,并阅读了回复:

我一直试图在我自己的情况下应用由提供的建议。在我的情况下,我似乎找不到一种既采纳泽门先生的建议又不利用后藤的方法

我想知道是否有更好或更优雅的解决方案

以下是我所做工作的概要:

我有一个带有命令按钮的UserForm,它开始执行代码,在多个Excel工作簿上执行多个操作。因此,存在多个代码块,一个代码块的成功完成允许执行后续代码块

在某一点上,根据情况,代码可能需要用户输入;在其他情况下,可以从Excel中获取所需的数据。如果需要用户输入,将显示另一个UserForm

在输入之前,用户可能需要查看多个不同的Excel工作表,因此UserForm是无模式的。因此,代码停止,直到用户输入所需的输入并单击另一个命令按钮


正是在这一点上,我遇到了麻烦:如何恢复程序流。使用GoTo语句是“从中断的地方开始”的唯一方法吗?或者,是否有某种方式来组织模块,从而形成一个统一的程序流,在一个点上定义,并且不从可能需要用户输入的点复制?

以下是我对这个问题的看法希望我正确理解了这个问题

假设:

  • 有两个用户表单
  • UserForm1,带有启动处理的按钮
  • UserForm2,带有提供中间输入的按钮
  • 模块内用于启动/启动UserForm1的子模块
  • VBA代码(用于子程序)

    VBA代码(用于UserForm1)


    好的,这是我的想法

    您有一个userform
    frmSelectUpdateSheet
    ,当无法通过编程确定工作表时,您希望使用它来允许用户选择工作表。问题是,如果执行
    .Show vbModeless
    (允许用户浏览工作表),则代码将继续执行,从而导致错误或其他不希望的输出

    我认为有可能采用我所采用的方法。然而,这是不可能的,除非你付钱让我对你所有的代码进行反向工程:P

    假设此时需要分配一个
    工作表
    对象变量(或表示工作表名称的字符串等)(并且该变量在范围内为
    公共
    ),只需使用表单上的CommandButton根据
    frmSelectUpdateSheet
    列表框中的选定项分配该变量即可

    这可能是一种优越的方法,原因有很多(并非最不重要的原因是试图避免针对这种边缘情况重新设计应用程序),例如:

    • 这将保留表单
      vbModal
      ,并防止用户在处理过程中无意中篡改工作表等

    • 使用这种方法,线程将保持显示的
      vbModal
      frmSelectUpdateSheet
      ,并且您依赖表单的事件过程来控制流程/代码执行

    • 实施起来应该更容易(因此也更便宜);不管你是自己做还是外包

    • 它应该更容易(因此也更便宜)维护

    现在,仔细检查,您似乎已经在使用
    cmdbtselect\u Click
    事件处理程序执行这种方法了,这让我相信存在一个相关的/后续问题:


    工作表名称(在列表框中)不足以让用户识别正确的工作表。因此,如果用户需要能够“滚动”工作表(例如,查看不适合窗口的数据等),则添加一些微调器按钮或其他表单控件,以允许他们浏览工作表

    在没有先看到模块/代码的情况下,很难提出更好的方法来组织模块/代码:)当然,另一种可能性是(在第二个用户窗体上)为程序本身提供一种方式来“选择”并查看必要的工作表(例如,列出所有工作簿/工作表的组合框控件等).我还建议,理想情况下,您应该在执行任何代码之前进行所需的验证--哪些条件需要手动干预?在代码执行任何操作之前检查这些条件,并在终止运行时时提示用户输入。然后,当用户重新运行程序时,它不应该要求添加用户输入(用户之前已经纠正了丢失的信息)。好的,已经编写了很多代码,并且每个代码块所做的似乎与整个程序流程没有关系。基本上,工作表中有一个可用的命令按钮。单击事件调用模块中的主函数。该主函数包含对其他函数的调用。如果被调用函数成功执行,则焦点将返回到主函数,并执行下一行代码,这是对下一个函数的调用。提供一个最小的、自包含的示例,展示您遇到的问题。我知道这是在要求你做更多的“工作”,但这比要求我们重新发明你的轮子要好(从我和其他人的观点来看:),你也可以省略那些不相关的代码块。至少,需要了解您的模块/表单的总体结构,以及第二个表单的调用方式,它是命令按钮/事件过程等。现在,我完全不知道您实际上在做什么,您实际上在做什么非常重要……让我测试一下我的理解:kee
    Sub LaunchUserForm1()
        Dim frm As New UserForm1
    
        '/ Launch the main userform.
        frm.Show vbModeless
    End Sub
    
    Private Sub cmdStart_Click()
        Dim i       As Long
        Dim linc    As Long
        Dim bCancel As Boolean
        Dim frm     As UserForm2
    
        '/ Prints 1 to 5 plus the value returned from UserForm2.
    
        For i = 1 To 5
    
            If i = 2 Then
                Set frm = New UserForm2
                '/ Launch supplementary form.
                frm.Show vbModeless
    
    '<< This is just a PoC. If you have large number of inputs, better way will be
    ' to create another prop such as Waiting(Boolean Type) and then manipulate it as and when User
    ' supplies valid input. Then validate the same in While loop>>
    
                '/ Wait till we get the value from UserForm2.
                '/ Or the User Cancels the Form with out any input.               
                Do While linc < 1 And (linc < 1 And bCancel = False)
                    linc = frm.Prop1
                    bCancel = frm.Cancel
                    DoEvents
                Loop
    
                Set frm = Nothing
            End If
    
            Debug.Print i + linc
        Next
    
        MsgBox "User Form1's ops finished."
    
    End Sub
    
    Dim m_Cancel        As Boolean
    Dim m_prop1         As Long
    
    Public Property Let Prop1(lVal As Long)
        m_prop1 = lVal
    End Property
    
    Public Property Get Prop1() As Long
       Prop1 = m_prop1
    End Property
    
    Public Property Let Cancel(bVal As Boolean)
        m_Cancel = bVal
    End Property
    
    Public Property Get Cancel() As Boolean
        Cancel = m_Cancel
    End Property
    
    Private Sub cmdlinc_Click()
        '/Set the Property Value to 10
        Me.Prop1 = 10
        Me.Hide
    End Sub
    
    Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
        '/ Diasble X button
    
        Me.Cancel = True
        Me.Hide
        Cancel = True
    
    End Sub