VBA- Printing to a listbox - vba

I'm writing a piece of code in VBA that imports some data, organizes it, and then prints it out in a list box. But i can't figure out what i'm doing wrong when trying to print to the list box. My code looks like this.
Private Sub cmdDisplaySchedule_Click()
Call listSchedule
End Sub
Public Sub listSchedule()
Dim i As Long
For i = 1 To MB2Boats.Count 'MB2Boats is the class where all of my data is containted.
SchedulerUserForm.LBoxBay2.AddItem
SchedulerUserForm.LBoxBay2.List(i - 1, 0) = MB2Boats(i).MB2BoatName
SchedulerUserForm.LBoxBay2.List(i - 1, 1) = MB2Boats(i).MB2Bay
SchedulerUserForm.LBoxBay2.List(i - 1, 2) = MB2Boats(i).MB2Duration
SchedulerUserForm.LBoxBay2.List(i - 1, 3) = MB2Boats(i).MB2Waiting
SchedulerUserForm.LBoxBay2.List(i - 1, 4) = MB2Boats(i).MB2StandardDeviation
Next i
End Sub
The above will only print the boatnames and nothing else. Any help is appreciated. Thanks!

Maybe all data is loaded into the form and you just don't see it yet. Try adding this line to the end of the sub:
SchedulerUserForm.LBoxBay2.ColumnCount = 5
As an update to the comments provided here is an interesting little example (with an empty workbook an empty new UserForm1 and and empty new ListBox1 dropped into the UserForm1:
Public Sub ListBoxColumnTest()
Load UserForm1
UserForm1.ListBox1.AddItem
UserForm1.ListBox1.List(0, 0) = "hello"
UserForm1.ListBox1.List(0, 1) = "hello"
UserForm1.ListBox1.List(0, 2) = "hello"
UserForm1.ListBox1.List(0, 3) = "hello"
UserForm1.ListBox1.List(0, 4) = "hello"
'The below message box will answer with 1
MsgBox "The current column count is: " & UserForm1.ListBox1.ColumnCount
'This will increase the column count to 5 and
'thereby only adjust the column widths (fair distribution of the give space for 5 even columns).
UserForm1.ListBox1.ColumnCount = 5
UserForm1.Show
End Sub

Related

vba excel If condition error on final iteration

I'm having the code takes the input from my checkboxes and grab data from the related worksheet. I ran it line by line and found out that it always gets a runtime error at the If statement on the final loop. Is there something wrong in my code?
Option Explicit
Private Sub UserForm_Initialize()
Dim counter As Long
Dim chkBox As MSForms.CheckBox
''''Add checkboxes based on total sheet count
For counter = 1 To Sheets.count - 2
Set chkBox = Me.Frame1.Controls.Add("Forms.CheckBox.1", "CheckBox" & counter)
chkBox.Caption = Sheets(counter + 2).Name
chkBox.Left = 10
chkBox.Top = 5 + ((counter - 1) * 20)
Next
End Sub
Private Sub cmdContinue_Click()
Dim Series As Object
Dim counter As Long
'''Clear old series
For Each Series In Sheets(2).SeriesCollection
Sheets(2).SeriesCollection(1).Delete
Next
''Cycle through checkboxes
For counter = 1 To Sheets.count - 2
''If the box is checked then
If Me.Frame1.Controls(counter).Value = True Then ''Error here on 4th iteration
''Add new series
With Sheets(2).SeriesCollection.NewSeries
.Name = Sheets(counter + 2).Range("$A$1")
.XValues = Sheets(counter + 2).Range("$A$12:$A$25")
.Values = Sheets(counter + 2).Range("$B$12:$B$25")
End With
End If
Next counter
Me.Hide
End Sub
Also, a second problem is it always run on the wrong loop. If i check box 2 it'll run data for the box 1 sheet, 3 run for 2, 4 run for 3, and 1 run for 4. Can anyone explain the reason behind this?
EDIT: So as VincentG point out below, adding an explicit name "checkbox" in there did the trick (i didn't know you could do that). Index 1 was probably taken by one of the buttons or the frame in the user form, causing it to get off set.
I guess your main problem comes from the fact that the controls have to be accessed starting from index 0. So to loop over all controls, you would do something like
For counter = 0 To Me.Frame1.Controls.Count - 1
Debug.Print counter; Me.Frame1.Controls(counter).Name
Next counter
So, when you stick to you code, I assume you have to change the if-statement to
If Me.Frame1.Controls(counter-1).Value = True Then

how to populate specific cells from a multi column list box - excel vba

I have a list box (lbxStN) with 3 columns (0-fmMultiSelectSingle).
I would like to make a selection (only one) in this list, and have the data from each column of the list shown in a specific cell in a specific sheet (sheet: DeN). First column from the list F19, second column C22 and third column H22. After button (cmdBtnSelect2) is clicked.
Data for the list box is stored in a different sheet in the same workbook.
Private Sub cmdBtnSelect2_Click()
Dim i As Long
Dim myVar4 As String
Dim myVar5 As String
Dim myVar6 As String
For i = 0 To lbxStN.ListCount - 1
If lbxStN.Selected(i) = True Then
lbxStN.List(i, 0).Value = myVar4
lbxStN.List(i, 1).Value = myVar5
lbxStN.List(i, 2).Value = myVar6
End If
Next
ThisWorkbook.Sheets("DeN").Range("F19") = myVar4
ThisWorkbook.Sheets("DeN").Range("C22") = myVar5
ThisWorkbook.Sheets("DeN").Range("H22") = myVar6
End Sub
If I run the code, I get a '424' Object required error.
Which means I'm missing something basic.?
For what you intend to do, there is no need to create new variables. The value from the selected item can be assigned directly to the cells. As follows
Private Sub cmdBtnSelect2_Click()
Dim i as Integer
For i = 0 To lbxStN.ListCount - 1
If lbxStN.Selected(i) Then
ThisWorkbook.Sheets("DeN").Range("F19") = lbxStN.List(i, 0)
ThisWorkbook.Sheets("DeN").Range("C22") = lbxStN.List(i, 1)
ThisWorkbook.Sheets("DeN").Range("H22") = lbxStN.List(i, 2)
Exit For
End If
Next i
End Sub
The previous subroutine iterates for each iteam in the list, and checks if the item is selected. If the item is selected, then it assigns each column to each cell. Exit For will exit the iteration, because there is no need to continue looking for more selected items.
I tested the code with the following subroutine to add items to the list. I think it would be a good idea to compare it with yours in case you have tried to do a complex assignment.
Private Sub CommandButton1_Click()
lbxStN.Clear
lbxStN.AddItem "a"
lbxStN.List(lbxStN.ListCount - 1, 1) = "a2"
lbxStN.List(lbxStN.ListCount - 1, 2) = "a3"
lbxStN.AddItem "b"
lbxStN.List(lbxStN.ListCount - 1, 1) = "b2"
lbxStN.List(lbxStN.ListCount - 1, 2) = "b3"
lbxStN.AddItem "c"
lbxStN.List(lbxStN.ListCount - 1, 1) = "c2"
lbxStN.List(lbxStN.ListCount - 1, 2) = "c3"
End Sub
I usually get better at coding by reading someone elses code and trying to understand it
Cheers!

Static Variables in VBA

I have an excel workbook where the user imports text files into a "Data Importation Sheet". The number of files imported is dependent on how files the user wants to import. So far my workbook works great but I have hit one bump. When the user imports a file an identifier (i.e. 1, 2, 3, etc) gets assigned to that data set. Then the user selects an option from a dropdown box and calculations and plots will automatically be produced. The user also has the option to "Clear all data" where when this is selected all worksheets are cleared and if the user imports new files (after clicking the "clear all data" button) the identifier value restarts at 1. Here is my code for the identifier/counting how many files have been imported..
Public Sub Macro(Optional reset As Boolean = False)
Static i As Integer
If reset Then
i = -1
i = i + 1
Exit Sub
End If
i = i + 1
Worksheets("Hidden").Cells(i + 1, 1).FormulaR1C1 = "=" & i
Worksheets("Hidden").Cells(2, 2).FormulaR1C1 = "=" & i
End Sub
The problem I have ran into now is data will need to be imported into this sheet at a later date so when I save this file and reopen it then import more files the identifier/count for file imports restarts at 1 which I do not want to happen. I want to be able to just keep adding more files and have the code continue, I do not want to have to clear all the imported data and restart. Any ideas as to how I can do this? TIA.
I'd create a standalone function to manage the sequence. Store the value in a Workbook Name entry.
Note - if you had to manage multiple sequences you could promote the name of the sequence to a parameter instead of using a Constant within the Function.
Function NextSequence(Optional reset As Boolean = False)
Const COUNTER_NAME As String = "NM_COUNTER"
Dim nm As Name, i
On Error Resume Next
'is the name already created?
Set nm = ThisWorkbook.Names(COUNTER_NAME)
On Error GoTo 0
If nm Is Nothing Then
'not there yest - create it...
Set nm = ThisWorkbook.Names.Add(COUNTER_NAME, 0)
End If
If Not reset Then
i = Evaluate(nm.RefersTo)
i = i + 1
nm.RefersTo = i
Else
nm.RefersTo = 0
i = 0 '<< or 1 if you want NextSequence(True) to
' return the first sequence value
End If
NextSequence = i
End Function
Usage:
Public Sub Macro(Optional reset As Boolean = False)
Dim i
i = NextSequence(reset)
If reset Then Exit Sub
With Worksheets("Hidden")
.Cells(i + 1, 1).Value = i
.Cells(2, 2).Value = i
End With
End Sub
A quick fix for this would be to store the value of the identifier/count inside a cell and hide/lock the cell. The value inside the cell won't change upon restart yet you still can manipulate it inside VBA.
Very quick feel of how it should look like (probably innacurate as I don't have every info I need.)
Public Sub Macro(Optional reset As Boolean = False)
Static i As Integer
i = ActiveWorkBook.Sheets("Ressource").Range("A1").Value
If reset Then
i = -1
i = i + 1
Exit Sub
End If
i = i + 1
Worksheets("Hidden").Cells(i + 1, 1).FormulaR1C1 = "=" & i
Worksheets("Hidden").Cells(2, 2).FormulaR1C1 = "=" & i
End Sub
You could also create a CustomDocumentProperty to save the sequence number. You can pass a boolean to the method to reset:
Lastly, a helper function will check if the property exists, in order to be added if not.
Public Sub SequenceNumber(Optional ByVal Reset As Boolean = False)
If Not PropertyExists("Identifier") Then
ThisWorkbook.CustomDocumentProperties.Add Name:="Identifier", _
LinkToContent:=False, _
Type:=msoPropertyTypeNumber, _
Value:=0
End If
Dim p As Object
Set p = ThisWorkbook.CustomDocumentProperties("Identifier")
If Reset Then p.Value = 0 Else p.Value = p.Value + 1
End Sub
'Property Exists?
Private Function PropertyExists(ByVal propertyName As String) As Boolean
Dim p As Object
For Each p In ThisWorkbook.CustomDocumentProperties
If p.Name = propertyName Then
PropertyExists = True
Exit Function
End If
Next p
End Function
To call it:
SequenceNumber
SequenceNumber Reset:=True

Excel multi-select, multi-column listboxes

I am having trouble coding a userform that takes the selected data from one multi-column Listbox and adds it to another Listbox on the same userfrom. after adding, the data is removed from the source Listbox
"ListBox" is where the data is located, and "listbox1" is where it is being added to.
Private Sub add_Click()
For i = 0 To ListBox.ListCount - 1
If ListBox.Selected(i) = True Then
NextEmpty = ListBox1.ListCount
ListBox1.List(NextEmpty, 0) = ListBox.List(i, 0)
ListBox1.List(NextEmpty, 1) = ListBox.List(i, 1)
ListBox1.List(NextEmpty, 2) = ListBox.List(i, 2)
ListBox.RemoveItem (i)
End If
Next
End Sub
This code gives me a Run-time error '381'
"Could not set the list property. Invalid property array index."
I have done some looking around but can't seem to pinpoint how to use these properties correctly. Any help is greatly appreciated.
In order to do this, like Daniel said, we need to use an add function.
In the code below you can see how I used the .additem function in my with-block.
To remove the selection after moving it to a new Listbox, I run a backwards loop.
For i = MainListBox.ListCount - 1 To 0 Step -1
Private Sub add_Click()
Dim i As Integer
For i = 0 To MainListBox.ListCount - 1
If MainListBox.Selected(i) Then
With ListBox1
.AddItem
.List(.ListCount - 1, 0) = MainListBox.List(i, 0)
.List(.ListCount - 1, 1) = MainListBox.List(i, 1)
.List(.ListCount - 1, 2) = MainListBox.List(i, 2)
End With
End If
Next i
For i = MainListBox.ListCount - 1 To 0 Step -1
If MainListBox.Selected(i) Then
MainListBox.RemoveItem (i)
End If
Next i
End Sub
You cannot set a value to an index greater than the maximum in the list (and the maximum is exactly ListCount (Or ListCount-1 if zero based).
So, you must add the values with ListBox1.Add

Populating 2nd+ column of listbox on Excel worksheet

I have an ActiveX listbox on an Excel 2007 worksheet. I want to populate it directly, not by pointing its RowSource property to a range, because there is no range that has the desired values.
The listbox's ColumnCount is set to 2.
I set ColumnWidths to "20;20", and now it returns:
20 pt;20 pt
So as far as I understand, two columns in the listbox should be available for writing, right?
Populating the first column is no problem:
activesheet.lstApplyCurves.List = array("Select All","Deselect All","aaa","bbb","ccc")
(or)
activesheet.lstApplyCurves.additem
activesheet.lstApplyCurves.List(0,0) = "Col1, Row1"
But how do I populate column 2? I get an error 380 ("Could not set the list property. Invalid property value.") on this:
activesheet.lstApplyCurves.List(0,1) = "Col2, Row1"
FWIW I've also tried this, but get the same error:
activesheet.lstApplyCurves.List(1,1) = "Col2, Row2"
So...how do I set values in the 2nd column?
UPDATE:
In addition to the answer below, FWIW I also found it's possible to assign a mulit-dimensional array to the List property, which is faster:
Dim ArrayToListbox() As Variant
ReDim ArrayToListbox(0 To 4, 0 To 2)
ArrayToListbox(0, 0) = "Select All"
ArrayToListbox(1, 0) = "Deselect All"
ArrayToListbox(2, 0) = "Row1-Col1"
ArrayToListbox(2, 1) = "Row1-Col2"
ArrayToListbox(2, 2) = "Row1-Col3"
ArrayToListbox(3, 0) = "Row2-Col1"
ArrayToListbox(3, 1) = "Row2-Col2"
ArrayToListbox(3, 2) = "Row2-Col3"
ArrayToListbox(4, 0) = "Row3-Col1"
ArrayToListbox(4, 1) = "Row3-Col2"
ArrayToListbox(4, 2) = "Row3-Col3" '"(" & Join(Array("a", "b", "c"), "|") & ")"
ActiveSheet.lstApplyCurves.Clear
ActiveSheet.lstApplyCurves.ColumnCount = 3
ActiveSheet.lstApplyCurves.List = ArrayToListbox
This works for me. If the below doesn't work on your system then delete the listbox and re-create it and then try this code again.
Private Sub CommandButton1_Click()
With ListBox1
.Clear
.ColumnCount = 2
For i = 1 To 2
.AddItem
.List(i - 1, 0) = "Col1, Row" & i
.List(i - 1, 1) = "Col2, Row" & i
Next i
End With
End Sub