如何使用函数填充集合,然后调用集合以在用户表单中填充组合框

时间:2019-06-18 13:59:53

标签: excel vba

我对VBA还是很陌生,我想用一个工作表中的一系列单元格来填充一个集合。稍后我将要添加和减去订单项,因此我需要它动态地包含第1、2和3列中所需的所有行。然后,我需要调用填充集合的函数来填充一些不同的组合框,但是我只想用集合的前两列填充组合框。我希望第一列成为集合中每个订单项的键。

我已经在线阅读了很多文章,但我反复遇到运行时错误91:对象变量或未设置块变量。此外,我似乎在实际在我的userform子菜单中调用collection函数时遇到麻烦。这可能与我的代码结构有关,但我不知道是什么。这可能是基本的,但我已经尝试了好一阵子了,但未能做到。

Dim cCodes As Collection
Function getCodes() As Collection

Set cCodes = New Collection
Dim rRange As Range
Dim rRow As Range
Set getCodes = New Collection
Dim ws As Worksheet
Set ws = Worksheets("Sheet1")
ws.Activate

Let rRange = Range("A4:C4")
Let rRow = Range(rRange, rRange.End(xlDown))
For Each rRange In rRow
 cCodes.Add rRange.Cells(0, 0), rRange.Cells(0, 1), rRange.Cells(0, 2), 
  Key:=rRange.Cells(0, 1)
 Let rRange = rRange.Offset(1, 0)

 Next rRange

Set getCodes = cCodes


End Function







Private Sub UserForm_Initialize()
 dateIn.Value = Now                                         
 dateIn = Format(dateIn.Value, "mm/dd/yyyy")
    sundayDate.Value = Worksheets("Sheet1").Cells(2, 24) 

Dim cCodes As Collection
Set cCodes = getCodes


With UserForm1
  CostCode1.List = cCodes                            
  CostCode2.List = cCodes
  CostCode3.List = cCodes
  CostCode4.List = cCodes
  CostCode5.List = cCodes
  CostCode6.List = cCodes
    End With
   ......more userform code

End Sub

我希望它运行平稳,以使集合具有全局性,并始终使用指定列中的所有订单项进行更新(在第一个空行处停止)。我还将希望在其他地方使用此集合,因此需要能够对其进行调用。请让我知道我在做什么错

3 个答案:

答案 0 :(得分:1)

我不会使用全局变量。这是一种不好的做法,容易出错。取而代之的是,我叫 thread t1(&Acquisition::recordIQ, &acq); t1.detach(); 来构建集合并稍后像这样使用它:

Sub

因此,您仅一次在主子变量上声明变量,我认为在您的示例中,Option Explicit Sub getCodes(cCodes As Collection) Set cCodes = New Collection Dim rRange As Range Dim rRow As Range Set getCodes = New Collection Dim ws As Worksheet Set ws = Worksheets("Sheet1") ws.Activate Let rRange = Range("A4:C4") Let rRow = Range(rRange, rRange.End(xlDown)) For Each rRange In rRow cCodes.Add rRange.Cells(0, 0), rRange.Cells(0, 1), rRange.Cells(0, 2), Key:=rRange.Cells(0, 1) Let rRange = rRange.Offset(1, 0) Next rRange End Sub Private Sub UserForm_Initialize() Dim cCodes As Collection dateIn.Value = Now dateIn = Format(dateIn.Value, "mm/dd/yyyy") sundayDate.Value = Worksheets("Sheet1").Cells(2, 24) getCodes cCodes With UserForm1 CostCode1.List = cCodes CostCode2.List = cCodes CostCode3.List = cCodes CostCode4.List = cCodes CostCode5.List = cCodes CostCode6.List = cCodes End With ......more userform code End Sub 在其中声明后,可以将UserForm_Initalize传递给{{ 1}},如下所示:cCodes,该过程将构建您的集合,准备在主过程中使用,或者如果使用相同的方法,则将要使用它。

另一个技巧是使用getCodes,这将迫使您声明所有变量,并且代码将得到更好的构建。

答案 1 :(得分:0)

与收藏相比,我更喜欢字典。它们在服务器上的功能均相同,但我发现Dictionary在性能和易用性方面均具有优势。话虽这么说,我认为您正在寻找类似的东西。诚然,这是相当先进的,因此我对代码进行了注释,以帮助遵循它的作用:

Private Sub UserForm_Initialize()

    Dim ws As Worksheet
    Dim rData As Range
    Dim hCodes As Object
    Dim vKey As Variant
    Dim aCols As Variant

    'This is the sheet that contains the data you wanted to get the codes from
    Set ws = ThisWorkbook.Worksheets("Sheet1")

    'This is the range containing the codes on that sheet
    Set rData = ws.Range("A4:C" & ws.Cells(ws.Rows.Count, "A").End(xlUp).Row)

    'Create the dictionary object
    Set hCodes = CreateObject("Scripting.Dictionary")

    'Specify the columns you want to use for the comboboxes (1 is the first column, 2 is the second column, etc.)
    'It doesn't have to be consecutive, if you want 1st and 3rd columns for example you could specify Array(1, 3)
    aCols = Array(1, 2)

    'Populate the dictionary using the GetCodes function (see below)
    Set hCodes = GetCodes(rData, 2) 'First argument is the range to pull the codes from, the second argument is the column that contains the keys

    'Loop through each key in the populated dictionary
    For Each vKey In hCodes.Keys
        'Populate the correct combobox based on the key (these are examples, change to what your actual keys and comboboxes will be)
        'See below for the PopulateList function;
        '    first argument is the listbox that should be populated
        '    second argument is the full array of values that the list will be populated from
        '    third argument is the list of column numbers that will be used to pull from the provided array values
        Select Case vKey
            Case "a":   PopulateList Me.ComboBox1, hCodes(vKey), aCols
            Case "b":   PopulateList Me.ComboBox2, hCodes(vKey), aCols
            Case "c":   PopulateList Me.ComboBox3, hCodes(vKey), aCols
            Case "d":   PopulateList Me.ComboBox4, hCodes(vKey), aCols
            Case "e":   PopulateList Me.ComboBox5, hCodes(vKey), aCols
            Case "f":   PopulateList Me.ComboBox6, hCodes(vKey), aCols
        End Select
    Next vKey

End Sub

Private Function GetCodes(ByVal arg_rData As Range, Optional ByVal arg_lKeyCol As Long = 1) As Object

    'Verify the range provided and key column provided are valid
    If arg_rData.Areas.Count > 1 Then
        MsgBox "Invalid range provided: " & arg_rData.Address & Chr(10) & "Must be a contiguous range"
        Exit Function
    ElseIf arg_rData.Columns.Count < arg_lKeyCol Or arg_lKeyCol < 1 Then
        MsgBox "Key Column must be >= 1 and <= Provided range's column count"
        Exit Function
    End If

    Dim hResult As Object
    Dim hIndices As Object
    Dim aData() As Variant
    Dim aTemp() As Variant
    Dim ixNew As Long
    Dim ixData As Long
    Dim ixCol As Long

    'Prepare the data array
    If arg_rData.Cells.Count = 1 Then
        ReDim aData(1 To 1, 1 To 1)
        aData(1, 1) = arg_rData.Value
    Else
        aData = arg_rData.Value
    End If

    'Prepare the result dictionary, and use an Indices dictionary to keep track of where data should be loaded in it
    Set hResult = CreateObject("Scripting.Dictionary")
    Set hIndices = CreateObject("Scripting.Dictionary")

    'Loop through each row of the provided data range (we loaded it into the data array earlier)
    For ixData = 1 To UBound(aData, 1)
        'Check if the key already exists
        If hResult.Exists(aData(ixData, arg_lKeyCol)) Then
            'Key already exists, update the index so we know which row to populate to in the results
            hIndices(aData(ixData, arg_lKeyCol)) = hIndices(aData(ixData, arg_lKeyCol)) + 1
        Else
            'Key does not exist, prepare a result array for it in the Results dictionary and set the Index to 1
            ReDim aTemp(1 To WorksheetFunction.CountIf(arg_rData.Columns(arg_lKeyCol), aData(ixData, arg_lKeyCol)), 1 To UBound(aData, 2))
            hResult(aData(ixData, arg_lKeyCol)) = aTemp
            hIndices(aData(ixData, arg_lKeyCol)) = 1
        End If

        'Clear the temp array and assign it to the current key's array
        Erase aTemp
        aTemp = hResult(aData(ixData, arg_lKeyCol))

        'Loop through each column in the data array
        For ixCol = 1 To UBound(aData, 2)
            'Populate the temp array with the current value from the data array
            aTemp(hIndices(aData(ixData, arg_lKeyCol)), ixCol) = aData(ixData, ixCol)
        Next ixCol

        'Set the appropriate Key of the Results dictionary to the temp array
        hResult(aData(ixData, arg_lKeyCol)) = aTemp
    Next ixData

    'Set the function's output the Results dictionary
    Set GetCodes = hResult

End Function

Private Sub PopulateList(ByVal arg_cComboBox As Control, ByVal arg_aData As Variant, ByVal arg_aColNums As Variant)

    Dim aList As Variant
    Dim vCol As Variant
    Dim i As Long, j As Long

    'Prepare the list array
    ReDim aList(LBound(arg_aData, 1) To UBound(arg_aData, 1), 1 To UBound(arg_aColNums) - LBound(arg_aColNums) + 1)

    'Loop through each row of the provided data array
    For i = LBound(arg_aData, 1) To UBound(arg_aData, 1)
        j = 0
        'Loop through only the column numbers provided
        For Each vCol In arg_aColNums
            'Populate the list array with the correct item from the data array
            j = j + 1
            aList(i, j) = arg_aData(i, vCol)
        Next vCol
    Next i

    'Clear previous list, set the column count, and set the list to the now populated list array
    With arg_cComboBox
        .Clear
        .ColumnCount = UBound(aList, 2)
        .List = aList
    End With

End Sub

答案 2 :(得分:0)

这未经测试,但是您可以用数组填充组合框:

Option Explicit
Function getCodes() as Variant ' intent is to return an array.
Dim rRange As Range
    Let rRange = ThisWorkbook.Worksheets("Sheet1").Range("A4:C4") ' fully qualified.
    Let rRange = Range(rRange, rRange.End(xlDown))
    getCodes = rRange.Value ' return a 2D array that is three columns wide. 
End Function

Private Sub UserForm_Initialize()
    dateIn.Value = Now
    dateIn = Format(dateIn.Value, "mm/dd/yyyy")
    sundayDate.Value = Worksheets("Sheet1").Cells(2, 24)

    With UserForm1
        CostCode1.List = getCodes
        CostCode2.List = getCodes
        CostCode3.List = getCodes
        CostCode4.List = getCodes
        CostCode5.List = getCodes
        CostCode6.List = getCodes
    End With
       ......more userform code
End Sub

使用函数而不是将那几行代码滚动到主代码中将有助于将来的扩展(例如,在函数中添加参数以更改代码的存储范围)。