VBA Insert Multiple Row data based on a listbox - excel

For each line of the selected listbox I want to insert data in the next blank row for every data selected in the listbox, one new copied row, but the text that comes from the Textbox must be copied equal in all row, the below code copied the text only in one single row
Private sub button1_click()
Dim rw as integer
Dim ws as worksheets
Set worksheets(“Sheet1”)
Rw = ws.cells.find(what:=“*”, searchorder:=xlrows, searchdirection:=xlprevious, lookin:xlvalues).row + 1
Ws.cells(rw, 3).value = me.textbox1.value
Ws.cells(rw, 5).value = me.listbox1.value
End sub
Please help me 😊

I've created a new Workbook and a new UserForm with a ListBox, TextBox and CommandButton all with default names for this example.
Option Explicit
____________________________________________________________________________________
Private Sub CommandButton1_Click()
Dim NextBlankRow As Long
Dim TargetRange As Range
Dim ListBoxItem As Long
Dim SelectedItemsArray As Variant
Dim ArrayElementCounter As Long
ArrayElementCounter = 0
With Me.ListBox1
ReDim SelectedItemsArray(0 To .ListCount - 1)
For ListBoxItem = 0 To .ListCount - 1
If .Selected(ListBoxItem) Then
SelectedItemsArray(ArrayElementCounter) = .List(ListBoxItem)
ArrayElementCounter = ArrayElementCounter + 1
End If
Next ListBoxItem
End With
ReDim Preserve SelectedItemsArray(0 To ArrayElementCounter - 1)
With ThisWorkbook.Sheets("Sheet1")
NextBlankRow = .Cells(Rows.Count, 3).End(xlUp).Row + 1
Set TargetRange = .Range("C" & NextBlankRow & ":C" & NextBlankRow + UBound(SelectedItemsArray))
End With
Dim TargetCell As Range
ArrayElementCounter = 0
For Each TargetCell In TargetRange
TargetCell.Value = Me.TextBox1.Value
TargetCell.Offset(0, 2).Value = SelectedItemsArray(ArrayElementCounter)
ArrayElementCounter = ArrayElementCounter + 1
Next TargetCell
End Sub
____________________________________________________________________________________
Private Sub UserForm_Initialize()
Dim ListBoxItemArray As Variant
ListBoxItemArray = Array("Listbox Item 1", "Listbox Item 2", "Listbox Item 3")
Dim ItemToAdd As Long
For ItemToAdd = LBound(ListBoxItemArray) To UBound(ListBoxItemArray)
Me.ListBox1.AddItem (ListBoxItemArray(ItemToAdd))
Next ItemToAdd
End Sub
Here are screenshots of the inputs and outputs:
Data selected/entered to the UserForm
[
Output to the worksheet
Explanation:
The Private Sub UserForm_Initialize() event populates the ListBox for the purposes of my example - You can ignore this when it comes to your code but I felt it necessary to include how I populated the ListBox Items and what values with.
The Private Sub CommandButton1_Click() event code breaks down into 4 main sections (starting after the variables are declared):
With Me.LisBox1...End With
The very first thing we do here is set the Array size with the ReDim statement. The UpperBound or limit of the Array is set to the ListCount property minus 1 which returns 1 less the number of items in the ListBox. This ensures our Array is big enough to hold all of the list box items values, if they were all selected for example, but does so dynamically so you don't waste memory using an ambiguous number to future proof your code like 100 when you might only have 30 items. If Items are added or removed, the Array is always the right size.
The reason we minus 1 is I declared the Option Base to 0 for the Array which means it starts at (or the Lower Bound is) 0 not at 1. See the Option Base Statement for more info.
Next we loop through each ListBox item and evaluate if it is Selected or not. If it is, we use the List() property to assign the value of that item to the Array. See For...Next loops for more info on how they work and see Using Arrays for how they work.
Now we have SelectedItemsArray() full of the values for each selected item in the listbox.
ReDim Preserve SelectedItemsArray(0 To ArrayElementCounter - 1)
Much like earlier, we are setting the size of our array but this time including the Preserve part of the statement. This means we can resize the array but keep all the current values in it - If we didn't use Preserve the Array would be resized but lose all values. We again set the Upper Bound dynamically to resize the array to the number of selected items. (See the Using Arrays info again regarding array sizes etc.)
With ThisWorkbook.Sheets("Sheet1")...End With
In this With block we are finding the last used row of Column C and assigning that row + 1 (so it references the next blank row) to LastUsedRow. We are also defining our range we want to write our data to dynamically using the LastUsedRow variable and the Upper Bound of our Array. This is done so the right amount of cells are written to in the next section of code.
For Each TargetCell In TargetRange...Next TargetCell
Another loop but this time For Each...Next is used. Very similar to the For...Next loop but this one loops through elements of an array or collection - in this case we will be looping through each cell in our TargetRange.
For each cell in the range, remembering our range is set to Column C from the next blank row, The TextBox value is written to Column C and each ListBox item value from our array is written to Column E. As the loop iterates, the ArrayItemCounter increments which ensures the next array element is written to Column E with each loop.

Related

How do I reference cells from different sheets with single combo box element?

Let's say I have 2 sheets: companies and persons. I take companies' and persons' names and put them into a single combo box so the items in the combobox look like this:
*CompanyName1
*CompanyName2
*CompanyName3
...
*CompanyNameN
*PersonName1
*PersonName2
*PersonName3
...
*PersonNameN
I want the ComboBox to reference the corresponding cell when chosing it, but how do I do it if the entries are from 2 different cells? I only see it this way:
Fill ComboBox with items from Companies
Make a variable to keep the starting index of the Persons
dim PersonsIndexStart as Integer
PersonsIndexStart = ComboBox.ListCount + 1
Fill ComboBox with items from Persons
Upon an Item being selected calculate the "true index" of an item.
dim TrueIndex as integer
If ComboBox.ListIndex >= PersonsIndexStart Then
TrueIndex = ComobBox.ListIndex - PersonsIndexStart
Else TrueIndex = ComboBox.ListIndex
End If
This method seem clumsy, is there another way? For example can I attach some extra data to a ComboBox item other than it's name?
Can I attach some extra data to a ComboBox?
(example code assumes a UserForm)
Of course it's possible to fill a ComboBox with a 2-dimensional data block; multiple columns are defined by its .ColumnCount and .ColumnWidths properties. You can even hide columns by defining zero widths - c.f. UserForm_Layout. Furthermore I demonstrate a fast method to assign data to the ComboBox'es .List property via the so called array method. In order to meet your additional information needs, the 2nd and 3rd column (both hidden by zero widths) are prefilled with a reference to sheet no 1 or 2 as well as its "true" row index within the referenced worksheet.
Private Sub UserForm_Initialize()
' Purpose: populate ComboBox with data from 2 sheets
doFillCombo Me.ComboBox1, Sheet1, Sheet2 ' sheets reference via their CodeName here !
End Sub
Private Sub UserForm_Layout()
' Purpose: layout combobox including hidden columns
With Me.ComboBox1
.ColumnCount = 3 ' << provide for 3 columns assigned via .List
.ColumnWidths = .Width & ";0;0" ' << hide last columns by ZERO widths
End With
End Sub
Sub call doFillCombo() (called from UserForm_Initialize)
Private Sub doFillCombo(cbo As MSForms.ComboBox, _
ws1 As Worksheet, ws2 As Worksheet, _
Optional ByVal ColWs1 = "A", Optional ByVal ColWs2 = "A")
' assign data from both sheets to temporary arrays
Dim tmp1, tmp2
tmp1 = getData(ws1, ColWs1)
tmp2 = getData(ws2, ColWs2)
' provide for a container array
ReDim arr(1 To UBound(tmp1) + UBound(tmp2), 1 To 3)
' read 1st data block to container
Dim i&
For i = 1 To UBound(tmp1)
arr(i, 1) = tmp1(i, 1)
arr(i, 2) = 1 ' refers to 1st worksheet
arr(i, 3) = i ' item count in the sheet's data column
Next i
' read 2nd data block to container
Dim StartRow&: StartRow = UBound(arr) - UBound(tmp2) + 1
For i = StartRow To UBound(arr)
arr(i, 1) = tmp2(i - UBound(tmp1), 1)
arr(i, 2) = 2 ' refers to 2nd worksheet
arr(i, 3) = i - UBound(tmp1) ' item count in the sheet's data column
Next i
' Assign data to combobox'es list property by one code line
cbo.List = arr
End Sub
*Helper function called by procedure doFillCombo
Private Function getData(ws As Worksheet, ByVal col, Optional ByVal StartRow& = 2) As Variant()
' Purpose: assign column data to variant array
If IsNumeric(col) Then col = Split(ws.Cells(1, col).Address, "$")(1)
Dim LastRow&
LastRow = ws.Range(col & Rows.Count).End(xlUp).Row
getData = ws.Range(col & StartRow & ":" & col & LastRow).Value2
End Function
Possible test display to get infos
Assumes a label control (e.g. Label1) displaying the referenced sheet no as well as the row index as prefilled in the hidden combobox columns:
Private Sub ComboBox1_Click()
' Purpose: display sheet related counters
' Note: index reference to .List is zero-based (1st column = 0, 2nd = 1, ...)
With Me.ComboBox1
If .ListIndex < 0 Then Exit Sub
Me.Label1 = "Sheet" & .List(.ListIndex, 1) & " " & _
"Item " & .List(.ListIndex, 2) ' optional test display via e.g. Label1
End With
End Sub
BTW You won't avoid some calculations anyway; in your case I'd even suggest to stick to your chosen method, but to get indices via a user defined function - needn't to regard this as clumsy :-).
If your second data is for example in range A20:A50 then you can use
Range("A20").Offset(ComboBox.ListIndex,0)
to get directly to the right cell. So you just need to know where your second list begins.

VBA using a ListBox to multi select entire column in excel

I have an excel file where I load the column headers dynamically from Row 2 across until I hit a null and put all those values into a list box transposed. This part is working as I expect it to.
My question is, how do I use the list box items to select the entire column that the named header exists in?
So in A2 B2 C2 I have the headers called Widget 1, 2, 3 respectively loaded into the listbox. Those load in order in the list box when the userform loads. In the list box, I would like to be able to click Widget 2 and 3 and have those entire columns selected. I don't want it hard coded, as I want it to be a selection as I could select widget 1 and 3 or any random selection as needed.
I could have as many as 50 widgets....so those will all load in the listbox on startup, I need to be able to select any of those values and have them select their corresponding column....
That is where I'm having issues, how to make the multi select happen.
Thanks in advance for any help.
EDITS:
This is the code used on the Private Sub UserForm_Initialize()
'Figure out how many actual columns headers there are and then search for signal names
'Dim Lastcol As String
Dim FoundColumnRangeCalculated As Variant
Dim Lastcol As Variant
Dim FoundColumnRange As Variant
With ActiveSheet
Lastcol = .Cells(2, .Columns.Count).End(xlToLeft).Column
''MsgBox Lastcol
End With
'Convert numerical column location to letter value to use as dynamic range header lookup
Dim NumberToColumn As Variant
Dim SearchColumn
SearchColumn = Lastcol
NumberToColumn = Left(Cells(1, SearchColumn).Address(1, 0), InStr(1, Cells(1, SearchColumn).Address(1, 0), "$") - 1)
'MsgBox NumberToColumn
'Build the actual range from found column headers
FoundColumnRangeCalculated = "A2:" & (NumberToColumn & Lastcol)
'Transfer headers vertically to the list box for user to see
ListBox1.List = Application.WorksheetFunction.Transpose(Range(FoundColumnRangeCalculated))
Imported List Box Transposed on UserForm Load
So now when I click or multi click (not shown in this image, but multi with choose options to be enabled) the item(s), I would like the corresponding column it represents to be selected when each item is clicked.
Columns that the items are drawn from
Since they are built in "order" from left to right, I assume it is a 1:1 match and search and select, but I'm having trouble trying to sort that piece of it out..lots of examples about getting it's data, parsing, etc.....I just simply need a "When listbox items selected, use selection to enable its column".
The columns can't be hard coded for range as the headers could be A:F, A:AA, or A:ZZ.......so it has to be a dynamic matching.
Thanks to those that responded, hopefully this edited post and images satisfy the on hold status.
To select multiple columns may try something like
Option Explicit
Private Sub ListBox1_Change()
Dim Ws As Worksheet, Rng As Range, c As Range, Sel As Range
Dim i As Long, Xval As Variant
Set Ws = ThisWorkbook.Sheets("Sheet1")
Set Rng = Ws.Range(Ws.Cells(2, 1), Ws.Cells(2, Me.ListBox1.ListCount))
For i = 0 To Me.ListBox1.ListCount - 1
If Me.ListBox1.Selected(i) Then
Xval = Me.ListBox1.List(i)
Set c = Rng.Find(Xval, LookIn:=xlValues)
If Not c Is Nothing Then
If Sel Is Nothing Then
Set Sel = Ws.Columns(c.Column)
Else
Set Sel = Union(Sel, Ws.Columns(c.Column))
End If
End If
End If
Next
If Not Sel Is Nothing Then
Sel.Select
End If
End Sub
In a case if you want to iterate through header's cells. It selects multiple columns for one widget if it's name matched more than once.
Works with ActiveSheet.
Dim result As Range
Dim criteria As String
Dim colcount As Integer
criteria = "widget1" ' Matching value
colcount = 10 ' Header columns count
For i = 1 To colcount
' Loop on 2nd row
If Cells(2, i).Value = criteria Then
' If string matched with a cell's value
If result Is Nothing Then
' if it's first match, set it as result column selection
Set result = Columns(i)
Else
' if it's not first match, add it to result selection
Set result = Union(result, Columns(i))
End If
End If
Next
' Select result seletion
If Not result Is Nothing Then
result.Select
End If

Non-contiguous For Each loop per row instead of column

I have a non-contiguous selection spanning rows and columns, and I want to do a For Each loop on it. Excel VBA does this by looping firstly down column 1, then 2,3 etc.; but I want it to loop along the row first instead.
(My sheet looks something like the picture below, I need to loop down the selection (version) each column in turn, and retrieve the Doc. No. and other information. The number of rows and version columns in the sheet is not fixed).
Short of writing a fairly large Sort function and creating an array of references, I was wondering if there was a 'built-in' way to do this?
I don't need code, just an explanation.
The order in which a For Each iterates an object collection is implementation-dependent (IOW blame Excel, not VBA) and, while likely deterministic & predictable, there is nothing in its specification that guarantees a specific iteration order. So VBA code written to iterate an object collection, should not be written with the assumption of a specific iteration order, since that's something that can very well change between versions of the type library involved (here Excel's).
It's very unclear what the shape of your Range / Selection is, but if you need to iterate the selected cells in a specific order, then a For Each loop should not be used, at least not for iterating the cells per se.
Since the ranges are not contiguous, the Range will have multiple Areas; you'll want to iterate the Selection.Areas, and for each selected area, iterate the cells in a particular order. For Each is, by far, the most efficient way to iterate an object collection, which Range.Areas is.
Debug.Assert TypeOf Selection Is Excel.Range
Dim currentArea As Range
For Each currentArea In Selection.Areas
'todo
Next
Instead of nesting the loops, make a separate procedure that takes the currentArea as a parameter - that procedure is where you'll be iterating the individual cells:
Private Sub ProcessContiguousArea(ByVal area As Range)
Dim currentRow As Long
For currentRow = 1 To area.Rows.Count
Debug.Print area.Cells(currentRow, 1).Address
Next
End Sub
Now the outer loop looks like this:
Debug.Assert TypeOf Selection Is Excel.Range
Dim currentArea As Range
For Each currentArea In Selection.Areas
ProcessContiguousArea currentArea
Next
The ProcessContiguousArea procedure is free to do whatever it needs to do with a given contiguous area, using a For loop to iterate the range by rows, without needing to care for the actual address of the selected area: using Range.Cells(RowIndex, ColumnIndex), row 1 / column 1 represents the top-left cell of that range, regardless of where that range is located in the worksheet.
Non-selected cells can be accessed with Range.Offset:
Debug.Print area.Cells(currentRow, 1).Offset(ColumnOffset:=10).Address
The top-left cell's row of the area on the worksheet is returned by area.Row, and the top-left cell's column of the area on the worksheet is retrieved with area.Column.
Non-Contiguous
By looping through the rows first (i), you will get the 'By Row sequence' e.g. A1,B1,C1, ...
The Code
Sub NonContiguous()
Dim i As Long
Dim j As Long
Dim k As Long
With Selection
For k = 1 To .Areas.Count
With .Areas(k)
For i = .Row To .Rows.Count + .Row - 1
For j = .Column To .Columns.Count + .Column - 1
Debug.Print .Parent.Cells(i, j).Address & " = " _
& .Parent.Cells(i, j)
Next
Next
End With
Next
End With
End Sub
This is based on urdearboy's suggestion:
1. loop over columns
2. within a column, loop over cells
Sub disjoint()
Dim r As Range, rInt As Range
Dim nLastColumn As Long
Dim nFirstColumn As Long, msg As String
Dim N As Long
Set r = Range("C3,C9,E6,E13,E15,G1,G2,G3,G4")
nFirstColumn = Columns.Count
nLastColumn = 0
msg = ""
For Each rr In r
N = rr.Column
If N < nFirstColumn Then nFirstColumn = N
If N > nLastColumn Then nLastColumn = N
Next rr
For N = nFirstColumn To nLastColumn
Set rInt = Intersect(Columns(N), r)
If rInt Is Nothing Then
Else
For Each rr In rInt
msg = msg & vbCrLf & rr.Address(0, 0)
Next rr
End If
Next N
MsgBox msg
End Sub

How to collaborate excel listbox as data-grid using search button and shown in the textbox?

I have this code to show all my data in the sheets and load them into the listbox. How can I display and at the same time modify the data into my textbox when I clicking the column inside the listbox?
Private Sub Dloadbtn_Click()
'Load Diret Colo data into Direct Colo Listbox data grid.
Dim ws As Worksheet
Dim rng As Range
Dim hd As Range
Dim i As Long, j As Long, rw As Long
Dim Myarray() As String
'~~> Change your sheetname here
Set ws = Sheets("Colodbs")
'~~> Set you relevant range here
Set rng = ws.Range("A1:N10000")
With Me.Dlistbox
.Clear
.ColumnHeads = False
.ColumnCount = rng.Columns.count
ReDim Myarray(rng.Rows.count, rng.Columns.count)
rw = 0
For i = 1 To rng.Rows.count
For j = 0 To rng.Columns.count
Myarray(rw, j) = rng.Cells(i, j + 1)
Next
rw = rw + 1
Next
.List = Myarray
'~~> Set the widths of the column here. Ex: For 5 Columns
'~~> Change as Applicable
.ColumnWidths = "50;70;30;50;30;120;120;30;150;30;50;50;70;200"
.TopIndex = 0
End With
End Sub
Private Sub DSearchbtn_Click()
Dim i As Long
Dim rno As Integer
i = 0
Do While Colodbs.Cells(i + 1, 1).Value <> ""
If Colodbs.Cells(i + 1, 1).Value = FbSNtxt.Text Then
rno = Colodbs.Cells(i + 1, 1).Row
GoTo Condition
Else
rno = 0
End If
i = i + 1
Loop
Condition:
If rno <> 0 Then
Colodbs.Cells(rno, 2).Value = FbSNtxt.Text
Else
MsgBox ("No Such number is found")
End If
End Sub
Get clicked value in listbox and write to textbox
Your OP states: "How can I display and at the same time modify the data into my TextBox when I click the column inside the listbox?"
Actually your question is asking how to get the clicked column index in a listbox. Using the helper function getColIndex() you receive this essential information and get easily the clicked data value via the list property for the current listindex.
In the code example below I use the listbox MouseUp event to [1] get the clicked column index via the x argument showing the position in points within the listbox based on a cumulated column widths array created when loading data to the listbox and [2] to modify the textbox FbSNtxt with the clicked data value dListBox.List(Me.dListBox.ListIndex, ColIndex).
Amplifying remark to a tricky method
In order to get the correct column index via the listbox x position, it's necessary to display the ►entire listbox width and to wrap it into a scrolling Frame (cf. listbox and frame settings in the loading procedure Dloadbtn_Click())
Declaration head of UserForm
Option Explicit
Dim cumulWidths() As Double ' cumulated column widths
Dim ColIndex As Integer ' current column index
A. Solution via Listbox MouseUp event
IMO it's the best to use the ► MouseUp event to get the horizontal position (in points) within the listbox via ist x argument as you get the actual listindex only there (and not already in the MouseDown event). Furthermore it gets triggered in any case, whereas the click event only once within the same row.
Private Sub dListBox_MouseUp(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
' Purpose: find search value in first column and write it to corresponding row in range (column B)
' Escape if no data
If dListBox.ListIndex < 0 Then Exit Sub
' [1] get the column index via helper function getColIndex
ColIndex = getColIndex(X)
' [2] get clicked data in column ColIndex and write it into textbox (i.e. modify latest value)
FbSNtxt.Text = Me.dListBox.List(Me.dListBox.ListIndex, ColIndex)
End Sub
►Helper function to get the current column index
Private Function getColIndex(ByVal X) As Integer
' Purpose: get column number analyzing your click position x
' Method: uses array cumulWidths (created by Dloadbtn_Click() calling initCumulWidths)
' Note: called by dListBox_MouseUp event
Dim i As Integer
For i = 0 To UBound(cumulWidths)
If X < cumulWidths(i) Then
getColIndex = i ' return value (listbox index is zero based)
Exit For
End If
Next i
End Function
B. Improved search procedures
Private Sub DSearchbtn_Click()
' Purpose: find search value in first column and write it to corresponding row in range (column B)
' Method: calls procedure dSearch
dSearch
End Sub
Private Sub dSearch()
' Purpose: find value in first column and write it to corresponding row in range (column B)
' Note: called a) by dListBox_DblClick or b) DSearchbtn_Click()
If Me.dListBox.ListCount = 0 Then Exit Sub
Dim i As Long, bFound As Boolean
'Dim Colodbs As Worksheet
'Set Colodbs = ThisWorkbook.Worksheets("Colodbs")
For i = 0 To Me.dListBox.ListCount - 1
If Me.dListBox.List(i, 0) = FbSNtxt.Text Then
' write to cell in found row (i+1, Column B)
Colodbs.Cells(i + 1, 2).Value = FbSNtxt.Text
' correct ListBox value in Column B
Me.dListBox.List(i, 1) = FbSNtxt.Text
bFound = True: Exit For
End If
Next i
If Not bFound Then
MsgBox FbSNtxt.Text & " ColIndex " & ColIndex & " not found.", vbInformation
End If
End Sub
C. Loading procedures
Improved CommandButton code to speed up the loading process by assigning the range in one statement and using subprocedure initCumulWidths.
Private Sub Dloadbtn_Click()
'Purpose: Load Diret Colo data into Direct Colo Listbox data grid.
Dim ws As Worksheet
Dim rng As Range
Dim hd As Range
Dim Myarray As Variant
'~~> Change your sheetname here
Set ws = ThisWorkbook.Worksheets("Colodbs")
'~~> Set yourrelevant range here
Set rng = ws.Range("A1:N10000")
With Me.dListBox
.Clear
.ColumnHeads = False
.ColumnCount = rng.Columns.Count
' -----------------------------------------------------
' Variant a): assign data to array and array to listbox
' -----------------------------------------------------
' Myarray = rng
' .List = Myarray
' -----------------------------------------------------
' Variant b): assign data in one step to listbox
' -----------------------------------------------------
.List = rng.Value2
'~~> Set the column widths of the column here.
.ColumnWidths = "50;70;30;50;30;120;120;30;150;30;50;50;70;200"
.TopIndex = 0
' =====================================================
' calls subprocedure to cumulate column widths
' -----------------------------------------------------
initCumulWidths
' set total width of listbox to max as it gets shown within a frame
.Width = cumulWidths(UBound(cumulWidths)) + 16
' set scroll width within frame
Frame1.ScrollWidth = .Width ' Frame1.InsideWidth * 4
End With
End Sub
►Subprocedure called by above CommandButton Click event to get cumulated widths
This subprocedure reads the columnwidths described in Points and assigns cumulated values to an array defined in the Userform module's declaration head.
Private Sub initCumulWidths()
' Purpose: calculate cumulated widths and assign values to common userform variable cumulWidths
' Method: splits ColumnWidths property of listbox and sums converted Point values
' Note: called by above Dloadbtn_Click() event
Dim a: a = Split(Me.dListBox.ColumnWidths, ";")
Dim previous As Double, i As Integer
ReDim cumulWidths(UBound(a))
For i = 0 To UBound(a)
cumulWidths(i) = val(Replace(a(i), " Pt", "")) + previous
previous = cumulWidths(i)
Next i
If UBound(a) > 0 Then
Debug.Print cumulWidths(UBound(a))
End If
End Sub
Additional note
As you didn't declare Colodbs as Worksheet in your search procedures, I assume you are using the CodeName property instead of an object.

Empty ListBox Values

I have 3 Listboxes. In the properties I have selected the ListStyle as fmListStyleOption and the MultiSelect as fmMultiSelectMulti. Here is the current code:
Private Sub Userform_Initialize()
Me.lstProperties.RowSource = "FilterData"
Me.lstStmts.RowSource = "ListofProps"
Me.lstLoans.RowSource = "FilterLoans"
End Sub
I am using dynamic named ranges in hope that it will only show data in the Listbox for cells that actually have values. Unfortunately it shows a long list of blank lines with checkboxes anyways.
Does anyone know how to make sure that the Listbox only shows data with values i.e. if there are 2 cells in my named range with data then there are only two checkboxes in my listbox.
Visual
An easier why to do this would be to create an array from your range and assign the array to the Listbox.List property. This will allow you to use SpecialCells to filter the data.
Private Sub Userform_Initialize()
Me.lstProperties.List = getArrayFromRange(Range("FilterData").SpecialCells(xlCellTypeVisible))
Me.lstStmts.List = getArrayFromRange(Range("ListofProps").SpecialCells(xlCellTypeConstants))
Me.lstLoans.RowSource = getArrayFromRange(Range("FilterLoans").SpecialCells(xlCellTypeConstants))
End Sub
Function getArrayFromRange(rng As Range)
Dim arr
Dim a As Range
Dim count As Long, x As Long, y As Integer
ReDim arr(1 To rng.CountLarge, 1 To rng.Columns.count)
For Each a In rng.Areas
For x = 1 To a.Rows.count
count = count + 1
For y = 1 To rng.Columns.count
arr(count, y) = a.Cells(x, y).Value
Next
Next
Next
getArrayFromRange = arr
End Function
The issue was that my dynamic named range was not properly set up. The formula for the named range should have been:
=OFFSET('Property Data'!$A$5,2,,COUNTA('Property Data'!$A$5),14)
and not:
=OFFSET('Property Data'!$A$5,2,,COUNTA('Property Data'!$A$5:N$5),14).

Resources