【问题标题】:Userform loops in seemingly reverse order用户表单以看似相反的顺序循环
【发布时间】:2019-07-09 18:08:04
【问题描述】:

我有一个包含 7 个复选框和一些描述它们的标签的用户表单。对于每个相应的复选框,都有一个数组,如果复选框被选中,则将从该数组中创建一个报告。但是,它没有正确循环。

我希望它以A, B, C, D, E, F, G 的形式循环遍历每个具有TabIndex 编号的复选框,分别为0, 1, 2, 3, 4, 5, 6。但是它按0,6,5,4,3,2,1 的顺序循环。

我有一个定义和声明变量的主子。我的用户表单打印代码如下:

Sub Get_PDF_Click()
' Creating PDF

Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual

PDFUserForm.Hide
i = 0
j = 0
For Each ctl In Me.Controls
    If TypeName(ctl) = "CheckBox" Then
        If ctl.Value = True Then
            j = j + 1
            Name_of_File = Array(i + 1, 1) & " report" & YYMM & ".xlsx"
            Workbooks.Open Filename:=OutputPath & Name_of_File
            Set Wkb = Workbooks(Name_of_File)
                For Each ws In Wkb.Worksheets
                    PDF_Name = Array(i + 1, 1) & " " & ws.Name & " " & YYMM
                    ws.ExportAsFixedFormat Type:=xlTypePDF, Filename:= _
                    OutputPath & PDF_Name, Quality _
                    :=xlQualityStandard, IncludeDocProperties:=True, IgnorePrintAreas _
                    :=False, OpenAfterPublish:=False
                Next ws
            Wkb.Close SaveChanges:=False
        End If ' See if checked
    i = i + 1
    Debug.Print ctl.Name
    End If ' See if checkbox
Next ctl

If j > 0 Then
    ' Notification on process time
    SecondsElapsed = Round(Timer - StartTime, 0)
    MsgBox "PDF succesfully published after " & SecondsElapsed & " seconds." & Chr(10) & "Location: " & OutputPath, vbInformation
Else
    MsgBox "No file was selected.", vbInformation
End If

Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True

End Sub

顺便说一句,我在另一段代码中遇到了类似的问题,我在工作表上的图表中循环,该图表也以错误的顺序循环,所以也许可以应用相同的解决方案概念。

【问题讨论】:

  • PDF_Name = Array(i + 1, 1) & ... 让我觉得是 type mismatch 错误的原因:为什么要创建一个包含两个元素(i + 11)的数组,然后尝试连接那个带有字符串的数组(??)?
  • 我的数组其实叫别的东西,但为了不混淆,我把它翻译了。我意识到我没有做到这一点。该数组包含名称客户。

标签: excel vba loops userform


【解决方案1】:

这是另一种方式;)这不需要您硬编码复选框的名称。

逻辑:创建一个二维数组。将 TabindexCheckBox 名称存储在数组中。在Tabindex 上对其进行排序,然后根据需要使用它:)

代码

Option Explicit

Private Sub Sample()
    Dim CbArray() As String
    Dim n As Long: n = 1
    Dim cbCount As Long
    Dim tindex As String, ctlname As String
    Dim ctl As Control
    Dim i As Long, j As Long

    For Each ctl In Me.Controls
        If TypeName(ctl) = "CheckBox" Then
           n = n + 1
        End If
    Next

    n = n - 1: cbCount = n

    ReDim CbArray(1 To n, 1 To 2)

    n = 1

    '~~> Sort the Tabindex and checkbox name in the array
    For Each ctl In Me.Controls
        If TypeName(ctl) = "CheckBox" Then
           CbArray(n, 1) = ctl.TabIndex
           CbArray(n, 2) = ctl.Name
           n = n + 1
        End If
    Next

    '~~> Sort the array
    For i = 1 To cbCount
        For j = i + 1 To cbCount
            If CbArray(i, 1) < CbArray(j, 1) Then
                tindex = CbArray(j, 1)
                ctlname = CbArray(j, 2)

                CbArray(j, 1) = CbArray(i, 1)
                CbArray(j, 2) = CbArray(i, 2)

                CbArray(i, 1) = tindex
                CbArray(i, 2) = ctlname
            End If
        Next j
    Next i

    '~~> Loop through the checkboxes
    For i = cbCount To 1 Step -1
        With Controls(CbArray(i, 2))
            Debug.Print .Name
            '
            '~~> Do what you want
            '
        End With
    Next i
End Sub

【讨论】:

  • 非常感谢。我期待下次有机会实施此功能,因为它的方法更具动态性,并且将来需要更少的手动更新。
【解决方案2】:

For Each 未指定以保证枚举顺序。很可能这些控件是按添加到Me.Controls 集合的顺序进行枚举的。

如果您需要特定订单,请使用For 循环:

Dim checkboxNames As Variant
checkboxNames = Array("chkA", "chkB", "chkC", "chkD", "chkE", ...)

Dim current As Long, checkboxName As String, currentBox As MSForms.CheckBox
For current = LBound(checkboxNames) To UBound(checkboxNames)
    checkboxName = checkboxNames(current)
    Set currentBox = Me.Controls(checkboxName)
    'work with the currentBox here
Next

请注意,这也消除了迭代您不感兴趣的控件的需要

【讨论】:

  • 谢谢你的工作就像一个魅力。另外,感谢您指定枚举机制。
  • @BlackBear 请注意,如果您使用的是Rubberduck,则可以通过使用实际复选框控件的名称而不对它们进行硬编码来获得重命名重构友好版本,例如checkboxNames = Array(chkA.Name, chkB.Name, chkC.Name, ...) - 这样,如果您重构/重命名控件,代码将相应更新(而如果名称只是字符串文字,重命名控件将破坏代码)。可能是介于此和 Sid 更彻底的基于TabIndex 的解决方案之间的一个很好的中间选择。
猜你喜欢
  • 1970-01-01
  • 2021-12-30
  • 2016-05-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-25
  • 1970-01-01
相关资源
最近更新 更多