【问题标题】:How to speed up filling of listbox values on userform excel如何加快在用户窗体 excel 上填充列表框值
【发布时间】:2018-04-09 13:13:35
【问题描述】:

我有这段代码,它基本上过滤列表框中的值,因为 excel 中用户窗体上文本框中的值发生变化

Private Sub TextBox1_Change()

Dim sht As Worksheet
Dim rng1 As Range
Set sht = Sheet5
Set rng1 = sht.Range("F2:F" & sht.Range("F" & sht.Rows.Count).End(xlUp).Row)

ListBox2.ColumnCount = 7

'=====
Dim i As Long
Dim arrList As Variant

Me.ListBox2.Clear
If sht.Range("F" & sht.Rows.Count).End(xlUp).Row > 1 Then
    arrList = sht.Range("F2:L" & sht.Range("F" & sht.Rows.Count).End(xlUp).Row).Value2
    For i = LBound(arrList) To UBound(arrList)
        If InStr(1, arrList(i, 1), Trim(Me.TextBox1.Value), vbTextCompare) Then
            liste = ListBox2.ListCount
            Me.ListBox2.AddItem
            Me.ListBox2.List(liste, 0) = arrList(i, 1)
            Me.ListBox2.List(liste, 1) = arrList(i, 2)
            Me.ListBox2.List(liste, 2) = arrList(i, 3)
            Me.ListBox2.List(liste, 3) = arrList(i, 4)
            Me.ListBox2.List(liste, 4) = arrList(i, 5)
            Me.ListBox2.List(liste, 5) = arrList(i, 6)
            Me.ListBox2.List(liste, 6) = arrList(i, 7)

        End If
    Next i
End If

If Me.ListBox2.ListCount = 1 Then Me.ListBox2.Selected(0) = True

End Sub

它工作得很好,除非我将值从某事更改为空,即空白需要大约 4 到 5 秒才能完成从列表框中的工作表中填充大约 8k 行 * 7 列数据,这是不可取的。有什么办法可以加快速度吗?

【问题讨论】:

  • 嗨,如何将值更改为空?我在您的代码中找不到该行。
  • @DavidG.manually。这是一个文本框更改事件子。我输入一些值,然后使用退格键将其删除。
  • 通过将Trim(Me.TextBox1.Value) 放入变量字符串中,您不必每次循环都计算它
  • @Rohan,我添加了一种高速方法,通过一个代码行用您的完整数据填充您的列表框,并添加了进一步改进的想法。

标签: vba excel listbox userform


【解决方案1】:

将数据放入新数组后,通过新数组设置列表框。

Private Sub TextBox1_Change()

Dim sht As Worksheet
Dim rng1 As Range
Dim vR() As Variant

Set sht = Sheet5
Set rng1 = sht.Range("F2:F" & sht.Range("F" & sht.Rows.Count).End(xlUp).Row)

ListBox2.ColumnCount = 7

'=====
Dim i As Long
Dim arrList As Variant

Me.ListBox2.Clear
If sht.Range("F" & sht.Rows.Count).End(xlUp).Row > 1 Then
    arrList = sht.Range("F2:L" & sht.Range("F" & sht.Rows.Count).End(xlUp).Row).Value2
    For i = LBound(arrList) To UBound(arrList)
        If InStr(1, arrList(i, 1), Trim(Me.TextBox1.Value), vbTextCompare) Then
            n = n + 1
            ReDim Preserve vR(1 To 7, 1 To n)
            For j = 1 To 7
                vR(j, n) = arrList(i, j)
            next j
        End If
    Next
     Me.ListBox2.List = WorksheetFunction.Transpose(vR)
End If

If Me.ListBox2.ListCount = 1 Then Me.ListBox2.Selected(0) = True

End Sub

【讨论】:

  • next j,不是nex j
  • 而不是redim preserve在每个循环中,您可以在循环开始时重新调整它,大小与arrList相同(最大大小),在Next i之后,您再次重新调整它,@987654326 @
  • @PatrickLepelletier,感谢您的评论。我纠正了错别字。
  • @PatrickLepelletier,arrList 大小和 vR() 大小不同。所以我使用了 redim preserve。
  • @T.M.,我有点不准确。如果 n = 1 那么 Me.ListBox2.column = vR
【解决方案2】:

如何将所需时间减少到几乎为零

技巧加快从列表框中的工作表中填充大约 8k 行 * 7 列数据不是每次都使用AddItem,而是设置整个数组到列表框:

    Me.ListBox2.List = a

检查搜索字符串s是否为空后

    If Len(s) = 0 Then                                      

代码

Option Explicit

Private Sub TextBox1_Change()

Dim t       As Double     ' Timer
Dim oSht    As Worksheet
'=====
Dim liste   As Long
Dim i       As Long
Dim j       As Long
Dim n       As Long
Dim s       As String
Dim a       ' data field array, variant! (shorter for arrList)

t = Timer
Set oSht = ThisWorkbook.Worksheets("Test")          ' set worksheet fully qualified reference to memory

ListBox2.ColumnCount = 7                            ' dimension listbox columns

s = Me.TextBox1.Value                               ' get search string
Me.ListBox2.Clear                                   ' clear listbox
n = oSht.Range("F" & oSht.Rows.Count).End(xlUp).Row ' get last row number
If n > 1 Then                                       ' at least 1 line needed
  ' write range to one based 2dim data field array
    a = oSht.Range("F2:L" & n).Value2

    If Len(s) = 0 Then                              ' check if EMPTY string
    '   ====================================
    '   Trick: add complete items all in one
    '   ====================================
        Me.ListBox2.List = a                        ' avoids loop
        Debug.Print "Time needed: " & Format(Timer - t, "0.00 ") & " seconds." & vbNewLine & _
                    "Empty string """": all " & UBound(a) & " items refreshed."
    Else
    ' loop through ONE based 2dim array
      For i = LBound(a) To UBound(a)

        If InStr(1, a(i, 1), Trim(s), vbTextCompare) Then
           Me.ListBox2.AddItem                      ' add new listbox item
         ' enter 7 column values
           For j = 1 To 7                           ' ListBox2.List is ZERO based!!
               Me.ListBox2.List(Me.ListBox2.ListCount - 1, j - 1) = a(i, j)
           Next j
        End If

      Next i
      Debug.Print "Time needed: " & Format(Timer - t, "0.00 ") & " seconds." & vbNewLine & _
                  "Search string """ & s & """:" & Me.ListBox2.ListCount & " items found."

    End If
End If
If Me.ListBox2.ListCount = 1 Then Me.ListBox2.Selected(0) = True
End Sub

注意

我关心的是在空字符串进入后提高速度。所以我专注于这部分,并几乎保留了您的进一步代码,但确实对其进行了一些润色以使其更具可读性并使用更短的名称(例如a而不是arrList)。为了控制它,我添加了一个Timer。顺便说一句,我想你忘记了一些变量声明。

进一步提高速度的想法

如果你想加快正常的字符串搜索,我建议使用以下步骤:

  • 临时工作表中使用高级过滤
  • 将内容读入新的数据域数组,
  • 通过所描述的方法将其写回列表框并
  • (之后删除临时工作表)。

当然,您会找到正确的代码:-)

附加提示

我建议阅读 C.Pearson 的“VBA 中的数组和范围”http://www.cpearson.com/excel/ArraysAndRanges.aspx。有关如何操作列表框的示例,另请参阅 Excel VBA - avoid Error 1004 writing UF ListBox Array to Sheet

祝你好运!

================================================ ====

后续编辑(参见 11/4-5 之前的 cmets)

此重新编辑不仅结合了加快 (A) 空字符串搜索的优点(参见上面我自己的答案) 使用 (B) Dy Lee 非常快速且高度赞赏的方法(搜索字符串不为空), 但也通过考虑一个衬垫和“零”衬垫来完成他的解决方案。

最近提出的解决方案区分了一个衬垫和其他衬垫

     '' ===========================
      '' B1 get one liners correctly
      '' ===========================
      '  If ii = 1 Then
      '     Me.ListBox2.Column = vR
      '' ===============================================
      '' B2 get others with exception of 'zero' findings
      '' ===============================================
      '  ElseIf ii > 1 Then
      '     Me.ListBox2.List = WorksheetFunction.Transpose(vR) ' not necessary, see below
      '  End If

但只能由一个代码行替换,因为ListBox.Column 属性重新转置了已经 在任何情况下都正确地将 vR 数组转置为 2dim 数组

         Me.ListBox2.Column = vR

ListBox.List 属性在这种情况下会起到双重作用。

附加提示:

值得一提的是,通过数据字段数组填充列表框有助于克服内置的**10列列表框限制” 当使用AddItem 方法时。

摘要代码

以下 - 稍作修改 - 代码应总结所有要点并帮助其他用户理解所做的所有改进(感谢@Dy.Lee):

Dy Lee 的解决方案提炼和评论

Option Explicit
Private Sub TextBox1_Change()
' Note:    based on Dy.Lee's approach including zero and one liners
' Changes: a) allows empty string search by one high speed code line
'          b) writes back one liners correctly via .Column property instead of .List property (cf. comment)
'          c) excludes zero findings to avoid error msg
' declare vars
  Dim t       As Double                          ' Timer
  Dim s       As String                          ' search string
  Dim oSht    As Worksheet                       ' work sheet
  Dim r       As Range
  '=====
  Dim a       As Variant                         ' one based 2-dim data field array
  Dim vR()    As Variant                         ' transposed array
  Dim i       As Long                            ' rows
  Dim j       As Long                            ' columns
  Dim ii      As Long                            ' count findings
  Dim jj      As Long                            ' count listbox columns (.ColumnCount)
  Dim n       As Long                            ' last row
  Dim nn      As Long                            ' findings via filter function
  t = Timer                                      ' stop watch
  s = Me.TextBox3                                ' get search string
  Set oSht = ThisWorkbook.Worksheets("Test")
' get last row number
  n = oSht.Range("F" & oSht.Rows.count).End(xlUp).Row
  if n = 1 then exit sub                 ' avoids later condition

  ListBox2.ColumnCount = 7                       ' (just for information)
  jj = ListBox2.ColumnCount
  ListBox2.Clear                                 ' clear listbox elements

' write range to one based 2dim data field array
  a = oSht.Range("F2:L" & n).Value2

' ========================
' A) EMPTY string findings                ' show all items
' ========================
If Len(s) = 0 Then                               ' check if EMPTY string
  ' ====================================
  ' Trick: add complete items all in one
  ' ====================================
    Me.ListBox2.List = a                         ' avoid loops, double speed
' ========================
' B) other actual findings
' ========================
Else                         ' 

   ' write results to redimmed and transposed array
     For i = LBound(a) To UBound(a)
         If InStr(1, a(i, 1), Trim(s), vbTextCompare) Then
                ii = ii + 1
                ReDim Preserve vR(1 To jj, 1 To ii)
                For j = 1 To jj
                    vR(j, ii) = a(i, j)
                Next j
         End If
      Next
    ' ==============================
    ' B1-B2) get any actual findings (retransposes both cases correctly to 2dim!)
    ' ==============================
      If ii >=1 then ListBox2.Column = vR ' exclude "zero" lines
End If

If Me.ListBox2.ListCount = 1 Then Me.ListBox2.Selected(0) = True

' time needed
  Debug.Print "Time needed: " & Format(Timer - t, "0.00 ") & " seconds." & _
                " - Search string """ & s & """: " & Me.ListBox2.ListCount & " items found."
End Sub

【讨论】:

  • 如果 n = 1 那么 Me.ListBox2.Column = vR
  • @Dy.Lee,我重新编辑以总结所有改进。一段时间后重新阅读帮助,我发现使用列表框的.Column 属性甚至可以区分一个衬里(.Column)和更高的发现(.List),因为 vR 数组已经被 ReDim 和这两种方法转置正确转回。所以我也学习了。附加说明:我使用了另一个变量名称 ii 而不是您的 n
【解决方案3】:

使用行源属性

Option Explicit

Private Sub TextBox1_Change()

    Dim sht As Worksheet
    Set sht = Sheet1

    Dim dataEnd as long
    dataEnd = sht.Range("F" & sht.Rows.Count).End(xlUp).Row

    Dim rng1 As Range
    Set rng1 = sht.Range("F2:F" & dataEnd)

    ListBox2.ColumnCount = 7
    ListBox2.ColumnWidths = "30 pt;30 pt;30 pt;30 pt;30 pt;30 pt;30 pt"
    '=====
    Dim i As Long
    Dim listData As Range

    ' Me.ListBox2.Clear
    If dataEnd > 1 Then
        Set listData = sht.Range("F2:L" & dataEnd)

        Me.ListBox2.RowSource = Sheet2.Name & "!" & listData.Address  ' this fills the listbox

    End If

    If Me.ListBox2.ListCount = 1 Then Me.ListBox2.Selected(0) = True

End Sub

【讨论】:

  • 它在代码的什么地方检查文本框中输入的字符串是否部分匹配?
  • 如果您将其用作范围,则将其重命名为 rangeList 或其他名称。当其他人必须阅读您的代码时,ArrList 太混乱了。
  • 我很抱歉。我不知道我在想什么。完全错过了部分比赛部分。 Dy.Lee 发的那张看起来很不错。
猜你喜欢
  • 1970-01-01
  • 2021-12-07
  • 2017-04-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-03
  • 2019-03-31
  • 2021-08-27
相关资源
最近更新 更多