multiselect vba excel-vba listbox userform

multiselect - userform show vba excel



Cómo acelerar el llenado de los valores de listbox en userform excel (3)

Tengo este código que básicamente filtra los valores en el cuadro de lista a medida que el valor cambia en el cuadro de texto en forma de usuario en 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

Funciona perfectamente, excepto cuando cambio el valor de algo a nada, es decir, en blanco, demora entre 4 y 5 segundos para completar aproximadamente 8k filas * 7 columnas de datos de la hoja en el cuadro de lista, lo que no es deseable. ¿Hay alguna manera de que podamos acelerar las cosas aquí?


Después de poner datos en una nueva matriz, configure listbox por nueva matriz.

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


use la propiedad rowsource

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


Cómo reducir el tiempo necesario a casi cero

El truco para acelerar el llenado de aproximadamente 8k filas * 7 columnas de datos de la hoja en el cuadro de lista no es usar AddItem cada vez, sino establecer una matriz completa en el cuadro de lista:

Me.ListBox2.List = a

después de verificar si la cadena de búsqueda s está vacía por

If Len(s) = 0 Then

Código

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

Nota

Mi preocupación era mejorar la velocidad después de que entra la cuerda vacía . Así que me concentré en esta parte y dejé su código adicional casi como estaba, pero lo pulí un poco para hacerlo más legible y usé nombres más cortos (por ejemplo, a lugar de arrList ). Para controlar eso, agregué un Timer . Por cierto, creo que olvidó algunas declaraciones variables.

Idea para una mayor mejora de la velocidad

Si desea acelerar la búsqueda de cadenas normal, le sugiero usar los siguientes pasos:

  • usar el filtrado avanzado en una hoja de trabajo temporal ,
  • lea el contenido en una nueva matriz de campo de datos,
  • escríbalo de nuevo a la lista mediante el método descrito y
  • (elimine la hoja de trabajo temporal después).

Seguro que encontrarás el código correcto para hacerlo :-)

Sugerencia adicional

Recomiendo leer "Arrays and Ranges in VBA" de C.Pearson en http://www.cpearson.com/excel/ArraysAndRanges.aspx . Para ver un ejemplo sobre cómo manipular listboxes, vea también Excel VBA - evite el error 1004 escribiendo UF ListBox Array to Sheet

¡Buena suerte!

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

Edición subsiguiente (cf comentarios anteriores al 11 / 4-5)

Esta reedición combina no solo las ventajas de acelerar (A) la búsqueda de cadenas vacías (ver mi propia respuesta anterior) con (B) el enfoque muy rápido y apreciado de Dy Lee (cadena de búsqueda no vacía), sino que completa su solución considerando una revestimientos y revestimientos "cero", también.

La solución recientemente sugerida distingue entre un revestimiento y otros

'''' =========================== '''' 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

pero puede ser reemplazado por UNA línea de código solamente, ya que la propiedad ListBox.Column retranspone la matriz vR ya transpuesta en CUALQUIER caso correctamente a una matriz de 2dim

Me.ListBox2.Column = vR

mientras que la propiedad ListBox.List haría un doble trabajo en este caso.

Sugerencia adicional:

Vale la pena mencionar que AddItem lista mediante matrices de campos de datos ayuda a superar la limitación incorporada de la lista de 10 columnas ** cuando se utiliza el método AddItem .

Código resumido

El siguiente código, ligeramente modificado, debe resumir todos los puntos y ayudar a otros usuarios a comprender todas las mejoras realizadas (thx @ Dy.Lee):

La solución de Dy Lee refinado y comentado

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