如何在 VBA 表单上列出/打印控件名称和/或属性?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/25248034/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-12 04:11:46  来源:igfitidea点击:

How do I list/print control names and/or properties on a VBA form?

excel-vbauserformvbaexcel

提问by JSM

I am updating a userform, and have added many more controls on separate tabs. I am getting ready to update my Initialize sub, and was wondering if there is a feature that will allow me to list and/or print all the control-objects on the form?

我正在更新用户表单,并在单独的选项卡上添加了更多控件。我准备更新我的 Initialize 子程序,想知道是否有一项功能可以让我列出和/或打印表单上的所有控件对象?

Having their other properties would be swell as well, since it would give me a map of what I need to set up, as well as use it as a checklist to make sure I complete everything that's needed. It would be more efficient to do that than run through them all, hope I have the right names and cell-references, wash/rinse/repeat.

拥有他们的其他属性也会膨胀,因为它会给我一张我需要设置的地图,并将其用作清单以确保我完成所需的一切。这样做比遍历它们更有效,希望我有正确的名称和细胞参考,洗涤/冲洗/重复。

Thanks

谢谢

回答by JSM

Sub ListControls() 
    Dim lCntr As Long 
    Dim aCtrls() As Variant 
    Dim ctlLoop As MSForms.Control 

     'Change UserForm Name In The Next Line
    For Each ctlLoop In MyUserForm.Controls 
        lCntr = lCntr + 1: Redim Preserve aCtrls(1 To lCntr) 
        'Gets Type and name of Control  
        aCtrls(lCntr) = TypeName(ctlLoop)&":"&ctlLoop.Name 
    Next ctlLoop 
     'Change Worksheet Name In The Next Line
    Worksheets("YrSheetName").Range("A1").Resize(UBound(aCtrls)).Value = Application.Transpose(aCtrls) 
End Sub 

This worked perfectly, adding all controls to a manually created sheet. Make sure to read comments and make changes required for individual projects.

这非常有效,将所有控件添加到手动创建的工作表中。确保阅读评论并针对各个项目进行所需的更改。

Thanks to the folks at OzGridwho answered this question many moons ago. Lesson: keep trying different words in Google as long as you have options.

感谢OzGrid的人们在很多个月前回答了这个问题。教训:只要你有选择,就继续在谷歌中尝试不同的词。

回答by mrSteveW

I recently had similar requirements and started with JSM's code above. With 350 controls nested within Frames and Multipages, I was having a difficult time tracing "where" each control sat within the UserForm.

我最近有类似的需求,并从上面的 JSM 代码开始。由于嵌套在框架和多页中的 350 个控件,我很难跟踪每个控件在用户窗体中的“位置”。

The solution below stores the Control Object as a key in a dictionary and it's path as an Array of Control Objects for each ancestor. Dimming the dictionary as Public to be used in other parts of the module have helped for looping through the dictionary objects (and/or any parent objects) to find or change attributes of those objects (font, color, etc).

下面的解决方案将控制对象存储为字典中的键,并将其路径存储为每个祖先的控制对象数组。将字典调暗为公共以用于模块的其他部分有助于循环遍历字典对象(和/或任何父对象)以查找或更改这些对象的属性(字体、颜色等)。

Creating or overwriting an existing worksheet is optional in case it is just necessary to update the dictionary. Sorting is based on Tab Index within Frames (and Index for Pages in a Multipage) and I opted to filter out Labels for the initial view.

如果只需要更新字典,则创建或覆盖现有工作表是可选的。排序基于框架内的标签索引(和多页中的页面索引),我选择过滤掉初始视图的标签。

Dimmed the following in another module so dictionary could be used elsewhere:

在另一个模块中将以下内容变暗,以便字典可以在其他地方使用:

Public usrFm As Object
Public dPath As New Scripting.Dictionary

ex: Call DictUserFormControls("EditInvForm",True)

例如:调用 DictUserFormControls("EditInvForm",True)

Public Sub DictUserFormControls(userFormName As String, Optional replaceSh As Boolean = False, Optional shName As String = "x_Controls")

    Dim i As Long, a As Long, c As Long, pArrLen As Long

    Dim cCont As Object, nCont As Object, pArr() As Object

    Dim arrLen As Long, h As Long, pgs As Long
    Dim pathName As String, tIndex As String, conType As String
    Dim extArr As Variant

    Set usrFm = VBA.UserForms.Add(userFormName)

    If replaceSh = True Then
        Dim wb As Workbook, sh As Worksheet, y As Long
        Set wb = ActiveWorkbook

        'Delete existing sheet if it exists
        Application.DisplayAlerts = False
        On Error Resume Next
            wb.Sheets(shName).Delete
        On Error GoTo 0
        Application.DisplayAlerts = True

        'Add a new worksheet
        Set sh = wb.Worksheets.Add
        sh.Name = shName

        'Create headers and starting row
        sh.Cells(1, 1).value = "Control"
        sh.Cells(1, 2).value = "Type"
        sh.Cells(1, 3).value = "Path"
        y = 2
    End If

    'loop through all controls associated with UserForm. Find all parents and parents of parents until you reach an error (parent of UserForm)
    'add each ancestor's Object to an array, and add the array to a dictionary with the Control Object as the key.
    For Each cCont In usrFm.Controls
        Set nCont = cCont.Parent
        c = 1
        a = a + 1
        Do Until c = 0
            i = i + 1: ReDim Preserve pArr(1 To i)
            Set pArr(i) = nCont
            dPath(cCont) = pArr
            On Error GoTo ErrHandler
            Set nCont = nCont.Parent
            On Error GoTo 0
        Loop

        extArr = dPath(cCont)
        arrLen = UBound(extArr) - LBound(extArr) + 1

        'loop through dict item array backwards for each key to build path names from parent objects stored in array
        For h = arrLen To 1 Step -1
            'the last item in each array will be the root (with no index or tab index number)
            If h = arrLen Then
                pathName = extArr(h).Name
            Else
                'find tab index to help in sorting -- page numbers of multipages are stored as Index not TabIndex
                If typeName(extArr(h)) = "Page" Then
                    tIndex = extArr(h).Index
                Else
                    tIndex = extArr(h).TabIndex
                End If
                'concatenate 0 to help with sorting (otherwise 10, 11, 12 comes between 1 & 2)
                If Len(tIndex) = 1 Then tIndex = "0" & tIndex
                pathName = pathName & " | " & "{" & tIndex & "} " & extArr(h).Name
            End If
        Next h

        'position of the control itself
        tIndex = cCont.TabIndex
        If Len(tIndex) = 1 Then tIndex = "0" & tIndex
        pathName = pathName & " | {" & tIndex & "}"

        If replaceSh = True Then
            'populate rows
            sh.Cells(y, 1).value = cCont.Name
            'added special condition based on how I name my Labels that are used to display data: determine if "_LblData" is in cCont.Name, if so use LblData for typeName instead of actual typeName
            If typeName(cCont) = "Label" And InStr(cCont.Name, "_LblData") <> 0 Then
                sh.Cells(y, 2).value = "LabelData"
            Else
                sh.Cells(y, 2).value = typeName(cCont)
            End If
            sh.Cells(y, 3).value = pathName
            y = y + 1

        End If

        i = 0
    Next cCont

    If replaceSh = True Then

        Dim fullRng As Range, hdrRng As Range
        Set fullRng = sh.Range(Cells(1, 1), Cells(y, 3))
        Set hdrRng = sh.Range(Cells(1, 1), Cells(1, 3))

        sh.Activate

        'format sheet and sort
        sh.Sort.SortFields.Clear
        sh.Sort.SortFields.Add key:=Range( _
            Cells(2, 3), Cells(y, 3)), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:= _
            xlSortNormal
        sh.Sort.SortFields.Add key:=Range( _
            Cells(2, 2), Cells(y, 2)), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:= _
            xlSortNormal
        sh.Sort.SortFields.Add key:=Range( _
            Cells(2, 1), Cells(y, 1)), SortOn:=xlSortOnValues, Order:=xlAscending, DataOption:= _
            xlSortNormal
        With sh.Sort
            .SetRange Range(Cells(1, 1), Cells(y, 3))
            .Header = xlYes
            .MatchCase = False
            .Orientation = xlTopToBottom
            .SortMethod = xlPinYin
            .Apply
        End With

        'autofit columns and show filters for header
        fullRng.Columns.AutoFit
        hdrRng.AutoFilter

        'set initial view to display items that require coding
        fullRng.AutoFilter Field:=2, Criteria1:=Array( _
        "CheckBox", "ComboBox", "CommandButton", "LabelData", "OptionButton", "TextBox"), Operator:= _
        xlFilterValues

    End If

    Exit Sub

ErrHandler:
    'root reached
    c = c - 1
    Resume Next

End Sub

An example of the output is here: output

输出示例如下: 输出

col1: v1_Cmb_Name
col2: ComboBox
col3: EditInvForm | {07} tabs | {00} vndPg | {00} vend_Frm | {00} v1_Frm | {01}

col1: v1_Cmb_Name
col2: ComboBox
col3: EditInvForm | {07} 个标签 | {00} vndPg | {00} vend_Frm | {00} v1_Frm | {01}

Considering 0 based index:

考虑基于 0 的索引:

"v1_Cmb_Name" is a ComboBoxthat can be found in the UserForm > MultiPage (8th Tabbed element) > 1st Page within MultiPage > 1st Frame (vend_Frm) > 1st sub-frame (v1_Frm) > 2nd Control

v1_Cmb_Name”是一个组合框,可以在 UserForm > MultiPage (8th Tabbed element) > 1st Page within MultiPage > 1st Frame (vend_Frm) > 1st sub-frame (v1_Frm) > 2nd Control中找到