Populate Multiple combobox's makes VBA userform slow - excel

At the moment I'm working with making a userform with 40 combobox's all which have the same list. My problem is filling all those combobox's is making the userform.show slow. The list that gets populated in those combobox's is a very long list (46542 rows and list length can vary) the list is with 3 columns.
I have been fooling around with CONCATENATE the whole list but that doesn't make much of a change. Also because I need to have the value when selected in the combobox to be CONCATENATE with all 3 columns in the combobox etc. when selecting row no. 1 in the combobox instead of writing only column 1 in the comboxbox textfield it will return all 3 columns so that means I'm actually having 4 columns where the first is CONCATENATE and hidden in the dropdown.
So my question is, is there a way to do the process more light?
So here is the code:
Private Sub UserForm_Initialize()
Set tsheet = ThisWorkbook.Sheets("Players")
Dim v As Variant, i As Long
v = tsheet.Range("A2:l" & Worksheets("Players").Cells(Rows.Count,
1).End(xlUp).Row).Value
With Me.ComboBox1
.RowSource = ""
.ColumnCount = 4
.BoundColumn = 2
.ColumnWidths = "1;50;50;50" 'Hide first column in dropdown
For i = LBound(v) To UBound(v)
.AddItem v(i, 1) & " " & v(i, 2) & " " & v(i, 3)
.List(.ListCount - 1, 1) = v(i, 1)
.List(.ListCount - 1, 2) = v(i, 2)
.List(.ListCount - 1, 3) = v(i, 3)
Next i
End With
With Me.ComboBox2
.RowSource = ""
.ColumnCount = 4
.BoundColumn = 2
.ColumnWidths = "1;50;50;50" 'Hide first column in dropdown
For i = LBound(v) To UBound(v)
.AddItem v(i, 1) & " " & v(i, 2) & " " & v(i, 3)
.List(.ListCount - 1, 1) = v(i, 1)
.List(.ListCount - 1, 2) = v(i, 2)
.List(.ListCount - 1, 3) = v(i, 3)
Next i
End With
This code goes on until it hit combox40
My old code was working pretty fast but it didn't have the column that was concatenated
ComboBox3.ColumnWidths = "50;50;50" 'COLUMN WITH OF LISTBOX
ComboBox3.ColumnCount = 3
'COLUMN NUMBER OF LISTBOX
ComboBox3.List = tsheet.Range("A2:l" &
Worksheets("Players").Cells(Rows.Count, 1).End(xlUp).Row).Value

Instead of
ComboBox3.List = tsheet.Range("A2:l" &
Worksheets("Players").Cells(Rows.Count, 1).End(xlUp).Row).Value
use something like this (declare Arr as Variant):-
Arr = tsheet.Range("A2:l" &
Worksheets("Players").Cells(Rows.Count, 1).End(xlUp).Row).Value
' add your extra rows to the array here, followed by
ComboBox3.List = Arr
Instead of repeating the same code 40 times, create a loop.
For i = 1 to 40
Cbx = Me.Controls("ComboBox" & Cstr(i))
' then manipulate Cbx as you have done.
Next I
Finally, since your 40 comboboxes are all the same, why not make do with only 1? You can move it around from row to row, let the user make his selection and transfer that selection to a textbox that appears in the place of the Cbx on Exit. When you click on the Tbx again it is substituted by the Cbx so that you have access to the list again.

In the module:
Dim ArrPlayers() as integer
In the userform initialization:
'To Do: add code to populate listbox with players
ReDim ArrPlayers (0 To 39)
On the listbox change event:
txtPosition.text = ArrPlayers(lstPlayers.ListIndex)
On the textbox change event:
ArrPlayers(lstPlayers.ListIndex) = cInt(txtPosition.text)
You will need to then save the values.

use the RowSource property of the combobox control
Option Explicit
Private Sub UserForm_Initialize()
Dim tsheet As Worksheet
Set tsheet = ThisWorkbook.Sheets("Players")
Dim rs As String
rs = "Players!a2:d" & tsheet.Cells(tsheet.Rows.Count, 1).End(xlUp).Row
Dim aaa As Control
For Each aaa In Me.Controls
If Left(aaa.Name, 8) = "ComboBox" Then
aaa.RowSource = rs ' =mySheet!a2:d24 in properties
aaa.ControlSource = "Players!z1" ' put the chosen value into this cell (example)
aaa.ColumnCount = 4
aaa.BoundColumn = 2
aaa.ColumnWidths = "1;50;50;50" ' Hide first column in dropdown
End If
Next aaa
End Sub

Related

Listbox to display data from columns based on if cells in specified column contain values

CODE ANSWER (thanks to karma)
Private Sub UserForm_Initialize()
Call PopLB
End Sub
Sub PopLB()
With Sheets("helper")
.Cells.Clear
Master.UsedRange.Copy Destination:=.Range("B1")
addr = .UsedRange.Columns(1).Offset(0, -1).Address
.Range("A1").Value = Split(addr, ":")(0)
.Range("A1").AutoFill Destination:=.Range(addr), Type:=xlFillSeries
.Range(addr).Offset(0, 1).SpecialCells(xlBlanks).EntireRow.Delete '0, 1 is Request ID; 0, 2 is CTC File Number; 0, 3 is Work Order
End With
With listboxRequestsETR
.ColumnCount = 27
.ColumnWidths = "00,28,00,00,28,28,208,28,28,28," & _
"28,28,28,28,28,28,28,28,28,28," & _
"28,28,28,28,28,28,28"
LastRow = Range("A" & Rows.Count).End(xlUp).row
.RowSource = "helper!A2:AA" & LastRow
.ColumnHeads = True
End With
End Sub
I am hoping to create a UserForm that pulls data from certain columns in a main sheet Master (Sheet1) based on if there is any data within the specified column.
Ideally, the ListBox listboxRequestsETR would check if there is a Request ID available Column A, and display the data in the yellow and blue columns. In this case, Row 1 would be shown as the column heads for the ListBox, and the only row that would not transfer over to the ListBox would be Row 4.
On a separate note, I am hoping to apply the same logic to separate ListBoxes with Columns B and C, such that the condition of populating the ListBox would be based on whether there is any data in the specified cell (regardless of whether the data is General or Number).
Any help would be appreciated!
I am able to populate the ListBox with data, however when I attempt to filter the data I come up with errors. Below is the code I have so far that brings up the ListBox with all the data.
NEW CODE
Private Sub UserForm_Initialize()
Call AddDataToListBoxETR
End Sub
Private Sub AddDataToListBoxETR()
' Dim rg As Range
' Set rg = GetRangeETR
' With listboxRequestsETR
' .RowSource = rg.Address(external:=True)
' .ColumnCount = rg.Columns.Count
' .ColumnWidths = "75;90;100;110;75;125;125;100;100;100;100;100"
' .ColumnHeads = True
' .ListIndex = 0
' End With
Dim i As Long
For i = 2 To Master.Range("A100000").End(xlUp).Offset(1, 0).row
If Master.Cells(i, "A").Value <> 0 Then
Me.listboxRequestsETR.AddItem Master.Cells(i, 1).Value
Me.listboxRequestsETR.List(listboxRequestsETR.ListCount - 1, 2) = Master.Cells(i, "D").Value
'Me.listboxRequestsETR.List(listboxRequestsETR.ListCount - 1, 3) = Master.Cells(i, "E").Value
'Me.listboxRequestsETR.List(listboxRequestsETR.ListCount - 1, 4) = Master.Cells(i, "F").Value
'Me.listboxRequestsETR.List(listboxRequestsETR.ListCount - 1, 5) = Master.Cells(i, "G").Value
'Me.listboxRequestsETR.List(listboxRequestsETR.ListCount - 1, 6) = Master.Cells(i, "H").Value
'Me.listboxRequestsETR.List(listboxRequestsETR.ListCount - 1, 7) = Master.Cells(i, "I").Value
'Me.listboxRequestsETR.List(listboxRequestsETR.ListCount - 1, 8) = Master.Cells(i, "J").Value
'Me.listboxRequestsETR.List(listboxRequestsETR.ListCount - 1, 9) = Master.Cells(i, "K").Value
'Me.listboxRequestsETR.List(listboxRequestsETR.ListCount - 1, 10) = Master.Cells(i, "L").Value
'Me.listboxRequestsETR.List(listboxRequestsETR.ListCount - 1, 11) = Master.Cells(i, "M").Value
'Me.listboxRequestsETR.List(listboxRequestsETR.ListCount - 1, 12) = Master.Cells(i, "O").Value
End If
Next i
End Sub
DESIRED OUTCOME
I recognize there is a column limit of 10, so the ability to display columns in the ListBox filtered by the condition (if Request ID is available in Row i) is what I am looking for
OLD CODE
Private Sub UserForm_Initialize()
Call AddDataToListBoxETR
End Sub
Private Sub AddDataToListBoxETR()
Dim rg As Range
Set rg = GetRangeETR
With listboxRequestsETR
.RowSource = rg.Address(external:=True)
.ColumnCount = rg.Columns.Count
.ColumnWidths = "75;90;100;110;75;125;125;100;100;100;100;100"
.ColumnHeads = True
.ListIndex = 0
End With
End Sub
Module
Option Explicit
Public Function GetRangeETR() As Range
Set GetRangeETR = Master.Range("A1").CurrentRegion
Set GetRangeETR = GetRangeETR.Offset(1).Resize(GetRangeETR.Rows.Count - 1)
End Function
I'm still not sure if I understand you correctly. Anyway, below is just my guess about what you want .....
The LB (ListBox) in the userform will show only the row with value in column A, hide column B and C, then show column D to Z. So, in the LB, there is H01 and then H04 to H26 while the row is coming from row 2,3,5 and 6. The LB doesn't show row 4 and 7 because in column A those rows are blank/no-value.
In the Userform there are 5 textbox to update/edit the row(N) of data for H04,H05,H06,H11 and H12.
Example:
The user click one item in the LB.
Then the textbox (tb) 1 to 5 show the corresponding column value which is clicked.
Then the user update/change the value in each tb, then click UPDATE button. The DATA is updated and the LB also updated.
Private Sub UserForm_Initialize()
Call PopLB
End Sub
Sub PopLB()
With Sheets("helper")
.Cells.Clear
Sheets("DATA").UsedRange.Copy Destination:=.Range("B1")
addr = .UsedRange.Columns(1).Offset(0, -1).Address
.Range("A1").Value = Split(addr, ":")(0)
.Range("A1").AutoFill Destination:=.Range(addr), Type:=xlFillSeries
.Range(addr).Offset(0, 1).SpecialCells(xlBlanks).EntireRow.Delete
End With
With LB
.ColumnCount = 27
.ColumnWidths = "00,28,00,00,28,28,28,28,28,28," & _
"28,28,28,28,28,28,28,28,28,28," & _
"28,28,28,28,28,28,28"
.RowSource = "helper!" & Sheets("helper").UsedRange.Address
End With
End Sub
Private Sub LB_Click()
tb1.Value = LB.List(LB.ListIndex, 4)
tb2.Value = LB.List(LB.ListIndex, 5)
tb3.Value = LB.List(LB.ListIndex, 6)
tb4.Value = LB.List(LB.ListIndex, 11)
tb5.Value = LB.List(LB.ListIndex, 12)
End Sub
Private Sub bt_Click()
If LB.ListIndex = -1 Then Exit Sub
With Sheets("DATA")
r = Range(LB.List(LB.ListIndex, 0)).Row
.Cells(r, 4).Value = tb1.Value
.Cells(r, 5).Value = tb2.Value
.Cells(r, 6).Value = tb3.Value
.Cells(r, 11).Value = tb4.Value
.Cells(r, 12).Value = tb5.Value
End With
Call PopLB
End Sub
In PopLB sub, first it clear the whole cells in sheet "helper".
Then it copy the data in sheet "DATA" to sheet "helper" cell B1.
Within sheet "helper":
it get the address of the usedrange as addr variable, then put the first split value of addr in cell A1, fill series the range of addr, then finally it delete the blank row of H01
within the LB:
It make 27 columns and set each column width. Please note that there are three zero value for the column width. One is to hide the id/row in column A, the other two is to hide H02 and H03. Finally it use the sheet helper used range as the row source for the LB.
The sub LB_Click will be triggered when the user click any item in the LB. It will populate textbox (tb) 1 to 5.
The bt_Click sub will be triggered when the user click the UPDATE button. It will update the corresponding value in the sheet DATA from the tb1 to tb5 value in the userform, then it call back the PopLB sub.
so, as you said :
this UF is meant to connect with an additional UF that can edit /
delete data in selected rows.
Although maybe it's not exactly what you mean, but this UF still can update/edit the data in sheet DATA although it use a helper sheet.

VBA Partial match (Value/String) of cells consecutive

I am struggling with partial Match, the idea is to add a comment on each line if there is a match or not with the below row, result should be as on below picture
my struggle is with the part with Part Match, ex with B4 Value"87032610" and B5 "Payment 87032610", results which I want to have is txt in column C4 and C5 "Part Match"
so far my code :
Sub testRes()
Dim i As Integer
i = 2
Do While ThisWorkbook.Worksheets("test").cells(i, 1) <> ""
If ThisWorkbook.Sheets("test").cells(i, 1) Like "*" & ThisWorkbook.Sheets("test").cells(i + 1, 1) Then
ThisWorkbook.Sheets("test").cells(i, 1).Offset(0, 1).Value = "Yes"
ThisWorkbook.Sheets("test").cells(i + 1, 1).Offset(0, 1).Value = "Yes"
Else
ThisWorkbook.Sheets("test").cells(i, 1).Offset(0, 1).Value = "No"
ThisWorkbook.Sheets("test").cells(i + 1, 1).Offset(0, 1).Value = "No"
End If
i = i + 1
Loop
End Sub
Thank you :-)
Check variable payment texts against invoice number
It's less time consuming to loop through an array than cells by means of VBA. In order to check for partial findings just change the direction of Like comparisons (completed by surrounding wildcards *), as the invoice number represents the smaller part than variable payment texts (of commercial clients).
Sub testRes()
'[0]get variant 1-based 2-dim data field array
Dim rng As Range
Set rng = Sheet1.Range("B2:B9") ' << change to wanted range reference
Dim v
v = rng.Value
'[1]check invoice number against changing payment texts
Dim i As Long
Dim invoice, pmt
For i = 1 To UBound(v) - 1 Step 2
invoice = v(i, 1)
pmt = v(i + 1, 1)
If invoice = pmt Then
v(i, 1) = "Yes"
ElseIf pmt Like "*" & invoice & "*" Then
v(i, 1) = "Part Match"
Else
v(i, 1) = "No"
End If
v(i + 1, 1) = v(i, 1)
Next i
'[2]write results
rng.Offset(0, 1) = v
End Sub

Selecting individual items from multiple columns in ListBox

I am trying to create a listbox with the column headers of my data to use that as an input from the user on which columns the user wants displayed in the final result. So far I've been able to divide the listbox into two columns and populate the relevant data in it, but on selecting the items, it is selecting the complete row and not individual items.
Can you please let me know how can I select individual items from a multicolumn listbox.
Here's the code :
Private Sub ListBox1_Enter()
Dim firstrow As Range
Dim c_no As Integer
Dim Arr() As String
Dim i As Integer
Dim j As Integer
Set firstrow = ThisWorkbook.Sheets("Tag Dump").Range("A1:AR1")
With firstrow
c = .Cells.Count
End With
Application.ScreenUpdating = False
ReDim Preserve Arr(c, 2)
'If WorksheetFunction.IsEven(c) = True Then
For i = 1 To c / 2
Arr(i, 1) = Sheets("Tag Dump").Cells(1, i).Value
Arr(i, 2) = Sheets("Tag Dump").Cells(1, i + (c / 2)).Value
'ElseIf WorksheetFunction.IsEven(c) = False Then
'End If
With ListBox1
.ColumnCount = 2
.Additem
.List(i - 1, 0) = Arr(i, 1)
.List(i - 1, 1) = Arr(i, 2)
End With
Next
With ListBox1
.ListStyle = fmListStyleOption
.Font = "Arial"
.MultiSelect = fmMultiSelectExtended
End With
Application.ScreenUpdating = True
End Sub
Here's an image of the result I'm getting:
Listbox selection
Thanks in advance.

Excel Userform to search in textbox and filter in a listbox

Hello I am looking for help, I have one textbox and one listbox in an Excel Userform, it works flawlessly except for one small Detail: as soon as the results appear in the listbox they represent the search within all columns. The first column, however is hidden when I type in the textbox, how can I make sure the column remains visible during search?
Thanks in advance
Here is the code:
Private Sub UserForm_Initialize()
End Sub
Private Sub TextBox1_Change()
With Sheets("Sheet1")
lr = .Range("A" & Rows.Count).End(xlUp).Row
ReDim arr(1 To lr - 1)
ReDim sn(1 To lr - 1, 1 To 13)
For i = 1 To UBound(arr)
arr(i) = .Range("A" & i + 2) & " " & .Range("B" & i + 2) & " " & .Range("C" & i + 2) & " " & .Range("D" & i + 2) & " " & .Range("E" & i + 2) & " " & .Range("F" & i + 2)
If InStr(1, arr(i), TextBox1) > 0 Then
j = j + 1
For X = 2 To 8
sn(j, X - 1) = .Cells(i + 2, X)
Next
End If
Next
ListBox1.List = sn
End With
End Sub
Consistent Array Approach
Your original code shows a mixture of array and range loops when creating a filtered listbox list. In order to be more consistent here by looping through arrays only *) , you could refine your code as follows (e.g. using the same match check via Instr):
Userform Event procedure TextBox1_Change()
Private Sub TextBox1_Change()
Const STARTROW = 3
Dim i&, iCnt&, r&, c& ' array counters for "rows" and "columns"
Dim sn, tmp ' variant 2-dim 1-based arrays
With Sheets("Sheet1")
iCnt = .Range("A" & Rows.Count).End(xlUp).Row - STARTROW + 1 ' items counter
ReDim sn(1 To iCnt, 1 To 13) ' provide for filtered data array
For i = 1 To iCnt
'assign current data row to 2-dim 1-based temporary array
tmp = .Range("A" & (i + 2) & ":F" & (i + 2)) ' current data row (c.f. OP)
'compare search string with concatenated data string from current row
If InStr(1, concat(tmp), TextBox1.Text) > 0 Then ' check occurrence e.g. via Instr
r = r + 1 ' new rows counter
For c = 1 To UBound(tmp, 2) ' col counter
sn(r, c) = tmp(1, c) ' collect found row data
Next
End If
Next
ListBox1.List = sn ' assign array to .List property
End With
End Sub
Helper function concat() called by above event procedure
Private Function concat(ByVal arr, Optional ByVal delim$ = " ") As String
' Purpose: build string from 2-dim array row, delimited by 2nd argument
' Note: concatenation via JOIN needs a "flat" 1-dim array via double transposition
concat = Join(Application.Transpose(Application.Transpose(arr)), delim)
End Function
Notes
*) Looping through a range by VBA is always time consuming, so do this with arrays instead.
You might be also interested in the following solution demonstrating the use of the listbox Column property. By playing around this could help you to remove the superfluous blank rows in the listbox.

vba excel - Transferring MultiSelect ListBox items and SingleSelect items in one command

I have 2 adjacent listboxes that transfer items back and forth. Each listbox has a respective worksheet. Between each listbox and each worksheet, each selection transfers properly from the first item down until the last item. If the last item in either ListBox is selected to transfer to its adjacent listbox, the item transfers to its adjacent list (and sheet), however the rest of its originating list (and sheet data) disappear (UPDATE: only if one uses MultiSelect code for SingleSelect Property). I'm wondering if someone can see something in the code that I'm obviously overlooking. Below is my transfer code.
EDIT: ListBox1 was a MultiSelect and ListBox2 was a SingleSelect and the code was for purely MultiSelect ListBoxes. My working example (i.e. ListBox1 Properties) did not take that into account so I've updated the code below to reflect a transfer between MultiSelect an SingleSelect ListBoxes. I know it's a lot of code for such a simple procedure, but it's a necessity for my application, so I hope this can help someone.
Tested and works.
Private Sub MultiListToSingleList()
Set ws = Sheets(1)
With ListBox2 'SingleSelect ListBox
.ColumnCount = 7
.ColumnWidths = "0;0;150;20;0;0;0" 'contains different columns and
End with 'indexing than ListBox1
' can insert error handling and message boxes here
With ListBox1
For n = 0 To .ListCount - 1
If .Selected(n) Then
With ListBox2
.AddItem Me.ListBox1.List(n)
.List(ListBox2.ListCount - 1, 2) = ListBox1.List(n, 1)
.List(ListBox2.ListCount - 1, 3) = ListBox1.List(n, 2)
End With
End If
Next n
For n = .ListCount - 1 To 0 Step -1
If .Selected(n) Then
.RemoveItem n 'removes the item from ListBox1
ws.Rows(n + 2).EntireRow.Delete 'removes the row from the
End if 'ListBox1 source in Sheet 1
Next n
End With
SheetTransfer
End Sub
Private Sub SingleListToMultiList ()
Set ws = Sheets(2)
With ListBox1 'MultiSelect ListBox
.ColumnCount = 3
.ColumnWidths = "0;140;20"
End With
' can insert error handling and message boxes here
With ws
With ListBox2
For n = 0 To .ListCount - 1
If Me.ListBox2.Selected(n) Then 'adds ListBox2 item to
'ListBox1
Me.ListBox1.AddItem .List(n)
Me.ListBox1.List(ListBox1.ListCount - 1, 0) = .List(n, 0)
Me.ListBox1.List(ListBox1.ListCount - 1, 1) = .List(n, 2)
Me.ListBox1.List(ListBox1.ListCount - 1, 2) = .List(n, 3)
End If
Next n
For n = 0 To .ListCount - 1
If .Selected(n) Then
.RemoveItem n 'removes the ListBox2 item
ws.Rows(n + 2).EntireRow.Delete 'removes the row from the
End if 'ListBox2 source in Sheet 2
Next n
End With
End With
SheetTransfer
End Sub
Private Sub SheetTransfer() 'moves the ListBox items to respective sheet sources
Set ws = Sheets(1)
With ws
For n = 0 To ListBox1.ListCount - 1
.Cells(n + 2, 1).Value = Me.ListBox1.List(n, 0)
.Cells(n + 2, 2).Value = Me.ListBox1.List(n, 1)
.Cells(n + 2, 3).Value = Me.ListBox1.List(n, 2)
Next n
FillListBox1
For n = 0 To ListBox2.ListCount - 1
wb.Sheets(2).Cells(n + 2, 3).Value = Me.ListBox2.List(n, 2)
wb.Sheets(2).Cells(n + 2, 4).Value = Me.ListBox2.List(n, 3)
Next i
End With
CheckLists
End Sub
Private Sub CheckLists() 'addtnl routine to check if listbox is truly
'empty...otherwise header row will show up if listbox has no items
Set ws = Sheets(2)
Set rng = ws.Range("A2")
With ws
ListBox1.Clear
ListBox2.Clear
If WorksheetFunction.CountA(rng) <> 0 Then
FillListBox2 'use List Property not RowSource
If WorksheetFunction.CountA(Sheets(1).Range("A2")) <> 0 Then
FillListBox1 'use List Property not RowSource
Else
Me.ListBox1.Clear
End If
Else
Me.ListBox2.Clear
FillListBox1 'in my application, ListBox1 fills from a lookuplist
End If
End With
End Sub

Resources