Move selected items from one listbox to another - vba

I have two listboxes, named listbox1 and listbox2.
Listbox1 is populated using a SQL query, and contains two columns. Its first column contains values that have commas.
Listbox2 is set as a value list in the "row source type" attribute of the Access property sheet.
My goal is to copy selected items from listbox1 to listbox2 using a control button.
I also need to be aware that listbox1 records contain commas, which act as delimiters during copying. That particular issue has been resolved, though.
I have created two modules to accomplish the copying of selected records from one listbox to another:
Public Sub CopySelected(ByRef frm As Form)
Dim ctlSource As Control
Dim ctlDest As Control
Dim strItems As String
Dim intCurrentRow As Integer
Set ctlSource = Me!listbox1
Set ctlDest = Me!listbox2
For intCurrentRow = 0 To ctlSource.ListCount - 1
If ctlSource.Selected(intCurrentRow) = True Then
'must insert double quote around single quote to escape commas
strItems = strItems & "'" & ctlSource.Column(0, intCurrentRow) & "'" & ";"
Me!listbox2.AddItem (strItems)
End If
Next intCurrentRow
End Sub
And
Private Sub cmdAddSelected_Click()
CopySelected Me
End Sub
I do have the multi-select option on listbox1 set to "extended".
The current problem is that when I click my control button, only the first selection from listbox1 is copied over- with the caveat that it is copied multiple times (copied the same number as those selected records).
Clearly, there is a problem with my For-loop.
R is my main language, and I am only just learning VBA.

In your program you are concatenating all selected values with separator and adding the longer and longer string to Listbox2. The ; acts as a column separator for multicolumn listboxes, i.e. you are kind of "transposing" the selected values from Listbox1 to Listbox2 while the excess items (above the number of columns of Listbox2) are simply ignored. If you want to copy individual values into the single column Listbox2, do this:
For intCurrentRow = 0 To ctlSource.ListCount - 1
If ctlSource.Selected(intCurrentRow) Then
strItems = "'" & ctlSource.Column(0, intCurrentRow) & "'"
Me!listbox2.AddItem (strItems)
End If
Next intCurrentRow

Related

Select name from one list box to another using add item error

Got an issue where I want to select a text from one list box and add the text into another list box. the error only seems to happen if text has a "'" within the text and VBA seems to split the text and add the remaining text to the next column. Also, I'm working with column in my listbox and my code should add each text from left to right. This is fine but I need the whole text (including the ".") instead of the text being split up.
Private Sub btnAddUser_Click()
Dim SelectUser, ItemString1, ItemString2 As String, ItemString3 As String
For Each SelectUser In lstusers.ItemsSelected
Debug.Print lstusers.Column(0, SelectUser)
Debug.Print lstusers.Column(1, SelectUser)
Debug.Print lstusers.Column(2, SelectUser)
ItemString1 = lstusers.Column(0, SelectUser)
ItemString2 = lstusers.Column(1, SelectUser)
ItemString3 = lstusers.Column(2, SelectUser)
Form_frmAS.lstAddedUsers.AddItem ItemString1, 0
Form_frmAS.lstAddedUsers.AddItem ItemString2, 1
Form_frmAS.lstAddedUsers.AddItem ItemString3, 2
Form_frmAS.lstAddedUsers.Requery
Next SelectUser
End Sub
Is there a way to make it work or do I need to find a workaround?
Many Thanks
in the ListBox.AddItem method, the second parameter is the position in the list, i.e. the row number and not the column number. You cannot add the columns one by one.
To make it work, set the Row Source Type of the second ListBox to Value List and change the code to
For Each SelectUser In lstusers.ItemsSelected
lstAddedUsers.AddItem lstusers.Column(0, SelectUser) & ";" _
& lstusers.Column(1, SelectUser) & ";" _
& lstusers.Column(2, SelectUser)
Next
If you don't specify the second parameter in AddItem, the new item will automatically be appended at the end of the list.

Populating header of Listbox from column header

1. Background & purpose
I'm creating a userform to display data from the Excel sheet("DATA") with table ("Tab1") of multi-columns like below picture.
In my form ("TaskMngUserForm"), after clicking on "Task List" button, all data from Tab1 will be displayed on Listbox1 as follows:
Column header in Tab1 will be displayed on Listbox1 as Header.
Data from 2nd row to end in Tab1 will be diplay on Listbox1 corresponding to each columns.
Also I'm adding an event for action "Listbox1_Click()" that returns "Data" sheet row corresponding to the selected Index, from the second column of the selected ListBox1 row.
UserForm and Listbox
2. Code
'4. Event for "Tasks List" button
Private Sub Button_TaskList_Click()
ListBox1.ColumnWidths = "20;100;80;100;60;100;80;80;80;200;200;200"
ListBox1.ColumnCount = 12
With ListBox1
'.ColumnHeads = True
.List = Sheets("DATA").Range("B2").CurrentRegion.Value
.RemoveItem (0)
.ColumnCount = Sheets("DATA").Cells(2, 2).CurrentRegion.Columns.Count
End With
Application.ScreenUpdating = True
Label25.Caption = "Total Tasks: " & (Worksheets("DATA").UsedRange.Rows.Count - 1)
End Sub
'6. Event for "Click Listbox" Action
Private Sub ListBox1_Click()
Dim strAddress As String
Dim dataSht As Worksheet
With Me
If .ListBox1.ListIndex <> -1 Then
Set dataSht = Sheets("DATA")
If IsNull(Me.ListBox1.Value) Then
Call MsgBox("You are selecting on blank row item" & vbNewLine & "Be careful!", vbInformation, "Notification")
Button_TaskList_Click
Else
strAddress = GetIndexRow(.ListBox1.List(.ListBox1.ListIndex, 0), dataSht.Columns("A"))
'<~~ GetIndexRow returns "Data" sheet row corresponding to the selected Index, which is got from the 2nd column of the selected ListBox row
TaskMngUserForm.txtIndex.Value = dataSht.Range("A" & strAddress).Value
TaskMngUserForm.cmbSource.Value = dataSht.Range("B" & strAddress).Value
TaskMngUserForm.cmbType.Value = dataSht.Range("C" & strAddress).Value
TaskMngUserForm.cmbCategory.Value = dataSht.Range("D" & strAddress).Value
TaskMngUserForm.cmbPriority.Value = dataSht.Range("E" & strAddress).Value
TaskMngUserForm.cmbTaskOwner.Value = dataSht.Range("F" & strAddress).Value
TaskMngUserForm.cmbStatus.Value = dataSht.Range("G" & strAddress).Value
TaskMngUserForm.txtOpenDate.Value = dataSht.Range("H" & strAddress).Value
TaskMngUserForm.txtCloseDate.Value = dataSht.Range("I" & strAddress).Value
TaskMngUserForm.txtSubject.Value = dataSht.Range("J" & strAddress).Value
TaskMngUserForm.txtDescription.Value = dataSht.Range("K" & strAddress).Value
TaskMngUserForm.txtSolution.Value = dataSht.Range("L" & strAddress).Value
End If
' TaskMngUserForm.Show
End If
End With
Application.ScreenUpdating = True
Label25.Caption = "Check in Task.No: " & txtIndex.Text
End Sub
3. Problem
I can load data from Tab1 to Listbox1 but I cannot populate column header from Tab1 to Header in Listbox1.
I recently coded a UserForm to include headers and I can answer this for you.
There is only 1 way to populate the headers on a ListBox and that is when you use the ListBox1.RowSource property. In the RowSource property you must assign a Range, this is one example:
UserForm1.ListBox1.RowSource = "Sheet1!A2:H20"
This will populate the data from A2 to H20 on ListBox1 and if the ListBox1 ColumnHeaders property is set to True then anything on Sheet1!A1:H1 will become the headers. This is the only way.
The reason that many users will tell you to just add text labels on top of the ListBox to make it easier is because when you do your list using RowSource, you must always find out what is the last Row used on your Range before you assign the Range to avoid Empty lines on your ListBox. What this means is that if you have 20 rows of data and you assign a range that contains 50 rows, the listbox will populate 50 rows, the last 30 will be empty.
Don't need Code or Formulas. Include the headers as part of the define factor for the data page, mine is named RecordsGoodCharacters, the name of the worksheet.
Highlight the Sheet Cells & Columns required. Include the headers as part of the define factor. Mine is RecordsLanguages for this Worksheet, which is the worksheets name.
Type in the name top left, to DEFINE the highlighted areas and then press ENTER on the keyboard, if you don’t use the keyboard, it won’t work.
Once defined, open your VBA Userform
Click on the ListBox
In it properties on the left of the display, in the RowSource area, Type the defined name used.
The list box will show the list including the headers.

VBA loop through textboxes and insert each box on different row

I have much of this form
coded to what I want but I'm having difficulty with the most significant part of it. As shown in the image, the frame in the form with 30 textboxes is designed to have names entered in it. Each box has a different name. When I click "save data" button I want the names in the textboxes to be entered on the next available row on the worksheet, also in the image.
So, if the form has Bob, Joe, and Jane in the first three boxes, I'd want rows A:2-4 in the worksheet to be populated with each name respectively.
If you can't (or don't want to) rely on textbox names, there are two possible ways out:
exploit TabIndex
if your textboxes inside "Individuals" frame have same TabIndex order as the cells you want to write their content into, then you could go as follows:
Dim i As Long
Dim strng As String
With Me.Frame1 '<--| change "Frame1" to your actual "Individuals" frame name
For i = 0 To .Controls.Count - 1
strng = strng & GetTextBox(i).Value & " "
Next
Cells(Rows.Count, 1).End(xlUp).Offset(1).Resize(.Controls.Count) = Application.Transpose(Split(Trim(strng), " "))
End With
where you exploit the following Function:
Function GetTextBox(tabId As Long) As Control
Dim ctrl As Control
For Each ctrl In Me.Frame1.Controls
If ctrl.TabIndex = tabId Then Exit For
Next
Set GetTextBox = ctrl
End Function
exploit Top and Left control properties
if your textboxes are properly vertically aligned (i.e. all texboxes in the same row share the very same Top property), then you could go as follows:
Dim dict As Object
Dim ctrl As Control
Set dict = CreateObject("Scripting.Dictionary")
With dict
For Each ctrl In Me.Frame1.Controls
.Item(Format(ctrl.Top, "000") & "-" & Format(ctrl.Left, "000")) = ctrl
Next
End With
SortDictionary dict
Cells(Rows.Count, 1).End(xlUp).Offset(1).Resize(Me.Frame1.Controls.Count) = Application.Transpose(dict.items)
where you exploit the following Function:
Sub SortDictionary(dict As Object)
Dim i As Long
Dim key As Variant
With CreateObject("System.Collections.SortedList")
For Each key In dict
.Add key, dict(key)
Next
dict.RemoveAll
For i = 0 To .Keys.Count - 1
dict.Add .GetKey(i), .Item(.GetKey(i))
Next
End With
End Sub

Copy listbox to another one

I'm trying to copy a listbox to another one
So I clear all items in the destination listbox like this
While Forms!SalesCallInformation!lstMills.ListCount > 0
Forms!SalesCallInformation!lstMills.RemoveItem (0)
Wend
Then I copy all items like this
For i = 0 To lstMillsToAdd.ListCount - 1
Forms!SalesCallInformation!lstMills.AddItem (lstMillsToAdd.Column(0, i) & ";" & lstMillsToAdd.Column(1, i) & ";" & lstMillsToAdd.Column(2, i))
Next
The problem is that in the destination listbox, I still got the old items.
But when I set a watch on Forms!SalesCallInformation!lstMills.ListCount when I delete I can see that's it's decrementing so it must remove something.
Is it something with refreshing the form? Cause I tried after inserting the new items to do this: Forms!SalesCallInformation.Refresh but I have the same result.
Does anyone have an idea?
Thank you
Also, the rowSourceType is set to value list
This example works for me:
Private Sub Button1_Click()
Me.List2.RowSource = Me.List0.RowSource
End Sub
Private Sub Button2_Click()
Me.List2.RowSource = ""
End Sub
Both listboxes are Value Lists. The Row Source is set to "abc";"def";"ghi"

cycling through values in a MS Access list box

I have a list box that populates with different sets of data based on user selections.
How can I cycle through any given values that may be in the list box? Is this a For Each statement, or what?
Here is how you iterate through the ListBox:
Dim i as Integer
For i = 0 to Me.ListBoxName.ListCount -1
'Access each item with
'Me.ListBoxName.ItemData(i)
Next i
You can do a For loop to examine each row in the listbox, and do whatever with the rows which are selected. In this example, I display the second column from selected items in the lstLocations listbox. (Column numbering starts with zero.)
Private Sub cmdShowSelections_Click()
Dim lngRow As Long
Dim strMsg As String
With Me.lstLocations
For lngRow = 0 To .ListCount - 1
If .Selected(lngRow) Then
strMsg = strMsg & ", " & .Column(1, lngRow)
End If
Next lngRow
End With
' strip off leading comma and space
If Len(strMsg) > 2 Then
strMsg = Mid(strMsg, 3)
End If
MsgBox strMsg
End Sub
Note I assumed you want the selected items from the list box. If you want all items, selected or not, you could use .ItemData as #DavidRelihan suggested. However, in that case, you could get them from the listbox .RowSource instead.
If working with a listbox in Access I like to capture the listbox recordset and loop through it. Perhaps because I find DAO recordset objects easy to work with.
I'd do something like:
Dim Rst as DAO.Recordset
Set Rst = lbxYourListboxObj.Recordset
'test to assure that there are records
If Rst.EOF then
'some error handling
end if
'I'm just paranoid so I always do this
Rst.MoveFirst
'iterate through list
Do Until Rst.EOF
'do something for each record
'it is nice and convenient to be able to reference the field names directly too!
debug.print Rst!Field1.name,Rst!Field1.value
Rst.MoveNext
Loop