I created an array to dynamically search content in column I in worksheet "gun inventory" and put corresponding information in column B into the array. Then I display these data in the array on the list created.
As you can see from my code, my condition is text in column I is "In Tool". However the last element in array does not have "In Tool" in element though still be included into the array. I have no idea why this happen and I will be appreciated if you can help.
I donno why S0007 will show here too.
Please ignore the variables undefined I defined them as public variables. The program runs well I just don't know why the last element will be included in the array.
Private Sub CheckGun_Click()
Dim gunarr()
Dim col As Integer
'Sheets.Add after:=Worksheets(5)
'ActiveSheet.Name = "tmp"
m = 0
With ThisWorkbook.Worksheets("gun inventory")
g = Application.WorksheetFunction.CountIf(.Range("I:I"), "In Tool")
Debug.Print g
ReDim gunarr(1 To g)
For i = 2 To .Cells(Rows.Count, "A").End(xlUp).row
If .Cells(i, "I").Text = "In Tool" Then
m = m + 1
End If
gunarr(m) = .Cells(i, "B")
Next i
End With
row = UBound(gunarr) - LBound(gunarr)
'Worksheets("tmp").Range("A2").Resize(row + 1).Value = Application.Transpose(gunarr)
With ListBox1
.Font.Size = 10
.ForeColor = vbBlue
.ControlTipText = "Tools are in use"
.ColumnHeads = True
.ColumnCount = Range("a1").CurrentRegion.Columns.Count
.ColumnWidths = "80"
.ListStyle = fmListStyleOption
.MultiSelect = fmMultiSelectMulti
.List = gunarr()
End With
'On Error Resume Next
'Application.DisplayAlerts = False 'prevent alert popping up for deleting the sheet
'ThisWorkbook.Sheets("tmp").Delete
'Application.DisplayAlerts = True
End Sub
As you can see from my code, my condition is text in column I is "In Tool". However the last element in array does not have "In Tool" in element though still be included into the array. I have no idea why this happen and I will be appreciated if you can help.
Related
I try to better explain the problem using this screenshot as example:
As you can see from the screenshot, what's going here is the following:
When an item is received, it is put on column G with the actual quantity received. Also an OrderID is associated to the item.
Everytime an item is shipped, it is put in column A.
What I would like to achieve?
Everytime I ship an item, I would like to progressively subtract the quantity in column B to the first non-zero quantity in column H (corresponding to the same item I just put).
If I would be able to create a list ( as in C++) the pseudo code would be the following:
item = $A2;
While(item =/= blank){
If(QuantityReceived > 0 && item == ItemReceived)
QuantityReceived--; ' here I just decrement by 1, because default quantity shipped is 1
else {
ItemReceived = ItemReceived -> next;
QuantityReceived = QuantityReceived -> next;
}
ItemReceived = $G2;
QuantityReceived = $H2;
item = item -> next;
}
I wrote this code to explain what I would like to achieve.
Do you have any tips/solution/ideas?
Hope I explained the problem well.
Thanks.
put this in the code for the sheet (not a module)
Private Sub Worksheet_Change(ByVal Target As Range)
'
Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Reference")
Application.EnableEvents = True
'restrict to users entering data in column B and only one cell
If Target.Cells.Count = 1 And Target.Cells.Column = 2 Then
' get the item name depending on removed or add
If Target.Value = 1 Then
itemName = Target.Offset(0, -1).Value
amt = -1
ElseIf Target.Value = 0 Then
itemName = ws.Range("A" & Target.Row).Value
amt = 1
Else
End
End If
' set up rng then look through all of the items in column G
Dim rng As Range
For Each rng In Range("G1:G" & Range("J" & Rows.Count).End(xlUp).Row)
' look for the item and a whats left of more than 0
If rng.Value = itemName And rng.Offset(0, 3) > 0 Then
rng.Offset(0, 3) = rng.Offset(0, 3) + amt
ws.Columns("A:B").Clear
lastrow = Range("A" & Range("A" & Rows.Count).End(xlUp).Row).Row
ws.Range("A1:B" & lastrow).Value = Range("A1:B" & lastrow).Value
Application.EnableEvents = True
End
End If
Next rng
' message if item with positive left not found
MsgBox ("no item remaining found")
End If
End Sub
test and let me know how you get on / accept the answer if it works well for what you want
enter image description here
Many thanks for your reply, Please find attached a picture of the user form I Got the data in the list box by some other ways no I am facing an issue to update and edit the data. I am trying to call the data from Listbox to textbox and checkboxes by below code for Editing.
Private Sub ListBox1_DblClick(ByVal Cancel As MSForms.ReturnBoolean) 'UPDATE LISBOX DATA
Dim p As Integer
Me.ComboBoxitem.Value = Me.ListBox1.List(Me.ListBox1.ListIndex, 1)
For p = 0 To Me.ListBox1.ListCount < 1
Me.CheckBoxSmall.Value = Me.ListBox1.List(p, 3)
Me.CheckBoxMedium.Value = Me.ListBox1.List(p, 3)
Me.CheckBoxLarge.Value = Me.ListBox1.List(p, 3)
Me.CheckBoXL.Value = Me.ListBox1.List(p, 3)
Me.CheckBoXXL.Value = Me.ListBox1.List(p, 3)
Me.CheckBoXXXL.Value = Me.ListBox1.List(p, 3)
Me.txtsmallqty.Value = Me.ListBox1.List(p, 4)
Me.TextBoxmedium.Value = Me.ListBox1.List(p, 4)
Me.TextBoxlarge.Value = Me.ListBox1.List(p, 4)
Me.TextBoXL.Value = Me.ListBox1.List(p, 4)
Me.TextBoxxL.Value = Me.ListBox1.List(p, 4)
Me.TextBoxxxL.Value = Me.ListBox1.List(p, 4)
Next
Me.TextBox1.Value = Me.ListBox1.List(Me.ListBox1.ListIndex, 0)
End Sub
and for update the data in excel sheet after editing , I am using below code :
Private Sub CommandButton1_Click() ' Update Data
Dim L As Long
Dim th As Worksheet
Set th = ThisWorkbook.Sheets("Data")
L = Application.WorksheetFunction.Match(CLng(Me.TextBox1.Value), th.Range("A1:A1000"), 0)
th.Range("B" & L) = Me.ComboBoxitem.Value
th.Range("D" & L) = Me.CheckBoxSmall.Value
th.Range("D" & L) = Me.CheckBoxMedium.Value
th.Range("D" & L).Value = Me.CheckBoxLarge.Value
th.Range("D" & L).Value = Me.CheckBoXL.Value
th.Range("D" & L).Value = Me.CheckBoXXL.Value
th.Range("D" & L).Value = Me.CheckBoXXXL.Value
th.Range("E" & L) = Me.txtsmallqty.Value
th.Range("E" & L) = Me.TextBoxmedium.Value
th.Range("E" & L) = Me.TextBoxlarge.Value
th.Range("E" & L) = Me.TextBoXL.Value
th.Range("E" & L) = Me.TextBoxxL.Value
th.Range("E" & L) = Me.TextBoxxxL.Value
Me.CheckBoxSmall.Value = False
Me.CheckBoxMedium.Value = False
Me.CheckBoxLarge.Value = False
Me.CheckBoXL.Value = False
Me.CheckBoXXL.Value = False
Me.CheckBoXXXL.Value = False
Me.txtsmallqty.Value = ""
Me.TextBoxmedium.Value = ""
Me.TextBoxlarge.Value = ""
Me.TextBoXL.Value = ""
Me.TextBoxxL.Value = ""
Me.TextBoxxxL.Value = ""
Me.TextBox1.Value = ""
End Sub
Addition due to comment:
"I am trying to pull Listbox data in 6 checkboxes and 6 text boxes from the first code mention above, the Issue I am facing from this code, shows only data from the first line of Listbox to all text boxes and checkboxes.
By the mean of the second code I have to update data in excel sheet."
But I am not able to get the perfect result, you are requested to please review the above Code and let me know where I am Mistaking.
Your Kind Response will be Highly Appreciated.
As you are displaying always six rows per chosen item (corresponding to six sizes of Small,Medium,...,XXXL) with item info only in the 1st row, a main issue is to get the correct .ListIndex by doubleclicking to any row within the listbox.
1. The start row index p (containing the serial# and product name) can be calculated from the currently double clicked .ListIndex using an int(eger) division multiplied by six rows to get to the first row (see section 1):
p = (Me.ListBox1.ListIndex \ 6) * 6
Example: a double click into .ListIndex of 0..5 results in the start row index p = 0, of 6..11 in 6, ... - i.e. always returning the first row of a bundle of six rows containing sizes.
2. To avoid endless assignments I defined two variant arrays (chkboxes and txtboxes) containing the checkbox and textbox names (see section 2). - Another frequently used method consists in enumerating the control names facilitating assignments in a later loop.
3. The 3rd step assigns the listbox'es main info (3a) and the size-related values (3b) to all single controls; the latter action is executed in a loop referring to the controls via Me.Controls(chkboxes(i)).Value and Me.Controls(txtboxes(i)).Value.
The following code example should give you a start and allow to finish the 2nd procedure by yourself (remind: don't overload a post by too many independant questions, focus to one issue :-;)
Private Sub ListBox1_DblClick(ByVal Cancel As MSForms.ReturnBoolean) 'UPDATE LISBOX DATA
'1. get the start row containing the serial code,
' (even if doubleclicked in one of the five following rows)
Dim p As Long ' instead of Integer
p = (Me.ListBox1.ListIndex \ 6) * 6 ' each item has 6 rows (sizes available)
'2. define arrays containing checkbox|textbox names
Dim chkboxes, txtboxes
chkboxes = Split("CheckBoxSmall,CheckBoxMedium,CheckBoxLarge,CheckBoXL,CheckBoXXL,CheckBoXXXL", ",")
txtboxes = Split("txtsmallqty,TextBoxmedium,TextBoxlarge,TextBoXL,TextBoXXL,TextBoxxxL", ",")
'3. a) write item name & Serial# to corresponding userform controls
Me.ComboBoxitem.Value = Me.ListBox1.List(p, 1) ' Item name
Me.TextBox1.Value = Me.ListBox1.List(p, 0) ' Serial number
'3. b) loop through all six rows representing sizes
Dim i As Long
For i = 0 To 5 ' listbox items and both ctrl arrays are 0-based!
Me.Controls(chkboxes(i)).Value = CBool(Me.ListBox1.List(p + i, 3)) ' 4th column has index 3!
Me.Controls(txtboxes(i)).Value = Me.ListBox1.List(p + i, 4) ' 5th column has index 3!
Next i
End Sub
I'm fairly new to Excel VBA and still learning the ropes, so I need help with a step by step program without using any functions. I understand how to count through an unknown column range and output the quantity. However, for this program, I'm trying to loop through a column, picking out unique numbers and counting its frequency.
So I have an excel file with random numbers down column A. I only put in 20 numbers but let's pretend the range is unknown. How would I go about extracting the unique numbers and inputting them into a separate column along with how many times they appeared in the list?
I have a code, but it's not working and I don't know why.
Public Sub CreateInventoryReport()
Dim bcode As Long
Dim ubcode As Long
'Below is the part that is not working
If Worksheets("Sheet 2").Range("A1")<>" Then
' First Value Is unique
Worksheets("Sheet2").Range("D5") = Worksheets("Sheet2").Range("A1")
Worksheets("Sheet2").Range("E5") = 1
Else
r = MsgBox("no data", , "no data")
Exit Sub
End If
bcode = 2
Do While Worksheets("Sheet 2").Cell(bcode, 1) <> ""
ubcode = 5
IsMatch = False
Do While Worksheets("Sheet 2").Cell(ubcode, 4) <> ""
If Worksheets("Sheet 2").Cell(bcode, 1) = Worksheets("Sheets2").Cell(ubcode, 4) Then
Worksheets("Sheet 2").Cell(ubcode, 5) = Worksheets("Sheet2").Cell(ubcode, 5) + 1
IsMatch = True
Exit Do
End If
ubcode = ubcode + 1
Loop
If IsMatch = False Then
Worksheets("Sheet2").Cell(ubcode, 4) = Worksheets("Sheet2").Cell(bcode, 1)
Worksheets("Sheet 2").Cell(ubcode, 5) = 1
End If
bcode = bcode + 1
Loop
End Sub
I am new to VBA and I am looking for something that is similar to python pandas, i.e. avoiding to loop through each rows many times. I am trying to achieve a quite simple task and it takes way too long. What is the best alternative to loops?
Looking around it seems that AutoFilter and Find might do, however I am not sure on what is the best option in my case.
Sub UpdateManualUpdates()
Dim lookUpSheet As Worksheet, updateSheet As Worksheet
Dim valueToSearch As String
Dim i As Long, t As Long
Set lookUpSheet = Worksheets("Manual price changes")
Set updateSheet = Worksheets("Price Build-up")
lastRowLookup = lookUpSheet.Cells(Rows.Count, "F").End(xlUp).Row
lastRowUpdate = updateSheet.Cells(Rows.Count, "B").End(xlUp).Row
'get the number of the last row with data in sheet1 and in sheet2
For i = 6 To lastRowLookup 'i = 2 to last to omit the first row as that row is for headers
valueType = lookUpSheet.Cells(i, 5) 'Type of update - Both, Planning group or GC
valueGroup = lookUpSheet.Cells(i, 3) 'Family group
valueGC = lookUpSheet.Cells(i, 4) 'GC
ValueChange = lookUpSheet.Cells(i, 6) 'What is the % change
'above get the values from the four column into variables
With Worksheets("Price build-up")
For t = 6 To lastRowUpdate
'AW is column 49 target column to update
'M is target column for group, 13
'C is target column for GC, 3
If valueType = "Both" Then
If .Cells(t, 13) = valueGroup And .Cells(t, 3) = valueGC Then
.Cells(t, 49) = ValueChange
End If
End If
If valueType = "Planning group" Then
If .Cells(t, 13) = valueGroup Then
.Cells(t, 49) = ValueChange
End If
End If
If valueType = "GC" Then
If .Cells(t, 3) = valueGC Then
.Cells(t, 49) = ValueChange
End If
End If
Next t
End With
Next i
End Sub
It is slow to access and update the Workbook object. Based on what you have now, a simple way is to convert the worksheet to an array and read the data from the array. Also, set Application.ScreenUpdating = False would make it a little bit faster.
Sub UpdateManualUpdates()
Application.ScreenUpdating = False
Dim lookUpSheet As Worksheet, updateSheet As Worksheet
Dim valueToSearch As String
Dim i As Long, t As Long
Set lookUpSheet = Worksheets("Manual price changes")
Set updateSheet = Worksheets("Price Build-up")
Dim lookUpSheetArray As Variant
Dim updateSheetArray As Variant
lastRowLookup = lookUpSheet.Cells(Rows.Count, "F").End(xlUp).Row
lastRowUpdate = updateSheet.Cells(Rows.Count, "B").End(xlUp).Row
lookUpSheetArray = lookUpSheet.Range("A1:F" & lastRowLookup).Value
updateSheetArray = updateSheet.Range("A1:AW" & lastRowUpdate).Value
For i = 6 To lastRowLookup 'i = 2 to last to omit the first row as that row is for headers
valueType = lookUpSheetArray(i, 5) 'lookUpSheet.Cells(i, 5) 'Type of update - Both, Planning group or GC
valueGroup = lookUpSheetArray(i, 3) 'Family group
valueGC = lookUpSheetArray(i, 4) 'GC
ValueChange = lookUpSheetArray(i, 6) 'What is the % change
'above get the values from the four column into variables
For t = 6 To lastRowUpdate
'AW is column 49 target column to update
'M is target column for group, 13
'C is target column for GC, 3
If valueType = "Both" Then
If updateSheetArray(t, 13) = valueGroup And updateSheetArray(t, 3) = valueGC Then
updateSheet.Cells(t, 49) = ValueChange
End If
End If
If valueType = "Planning group" Then
If updateSheetArray(t, 13) = valueGroup Then
updateSheet.Cells(t, 49) = ValueChange
End If
End If
If valueType = "GC" Then
If updateSheetArray(t, 3) = valueGC Then
updateSheet.Cells(t, 49) = ValueChange
End If
End If
Next t
Next i
Application.ScreenUpdating = True
End Sub
From my experiment, it is about 35% faster. Not a big improvement but just take a minute to update.
I’m using a userform with 12 listboxes (numbered 2-13). Each list box could contain 0-8 items assigned by user from main listbox1. I run the following code to output the content of each list box (12 boxes) to sheet “Tray” when a button is pressed.
Each listbox is then output into corresponding columns of each tray from columns B-M. Listbox2 fills column 1 of each tray and so on. A maximum of 4 trays can be filled. The code checks the 1st well of each tray and if it contains a value it assumes the tray is full & begins filling the next tray.
Problem: If the first tray contains a blank column(listbox) and the second tray contains values in the same listbox, the code will fill blank column of the frist tray with values that should be in the second tray. Please see pictures below and updated code below:
Listboxes 2,3 and 4 for Tray 1 (note listbox3 is empty)
Listboxes 2,3 and 4 for tray 2 (note listbox3 has data)
Code ran two times: Listbox3 from tray2 appears in tray1 (erroneously!!!)
Expected output:
Sub Worklist()
'
Dim Var, VarName As Variant
Dim i, DblDashPos, FirstPeriodPos, lngColNum, lngRowNum As Long
Dim item As ListBox
Const cstrNames As String = "Listbox2,Listbox3,Listbox4,Listbox5,Listbox6,Listbox7,Listbox8,Listbox9,Listbox10,Listbox11,Listbox12,Listbox13"
Application.ScreenUpdating = False
lngColNum = 2
For Each VarName In Split(cstrNames, ",")
If UserForm2.Controls(VarName).ListIndex <> -1 Then 'if listbox is not blank
If Sheets("Tray").Cells(4, lngColNum).Value = 0 Then
'checks if value in row 3 column "lngColNum" is empty
lngRowNum = 4
ThisWorkbook.Sheets("Tray").Range("C2").Value = UserForm2.TextBox1.Value
ElseIf Sheets("Tray").Cells(15, lngColNum).Value = 0 Then 'checks if value in row 14 column "lngColNum" is empty
lngRowNum = 15
ThisWorkbook.Sheets("Tray").Range("C13").Value = UserForm2.TextBox1.Value
ElseIf Sheets("Tray").Cells(26, lngColNum).Value = 0 Then 'checks if value in row 14 column "lngColNum" is empty
lngRowNum = 26
ThisWorkbook.Sheets("Tray").Range("C24").Value = UserForm2.TextBox1.Value
Else 'otherwise assumes tray starts in row 5, column "lngColNum"
lngRowNum = 37
ThisWorkbook.Sheets("Tray").Range("C35").Value = UserForm2.TextBox1.Value
End If
For i = 0 To UserForm2.Controls(VarName).ListCount - 1
Var = UserForm2.Controls(VarName).List(i)
DblDashPos = InStr(1, Var, "--")
FirstPeriodPos = InStr(1, Var, ".")
Sheets("Tray").Select
ActiveSheet.Cells(lngRowNum, lngColNum) = Left(Var, DblDashPos - 1) & Right(Var, Len(Var) - FirstPeriodPos + 1)
lngRowNum = lngRowNum + 1
Next i
End If
lngColNum = lngColNum + 1
Next
Application.ScreenUpdating = True
End Sub
Thank you very much!
The problem is that you're only testing the column that corresponds to the ListBox to see if the cell is empty. If you want to test that all of the columns in a "tray" are empty, you need to test once for the entire sheet. Something like this (untested because I'm too lazy to rebuild your form):
Private Function FindFirstUnusedRow(sheet As Worksheet) As Long
Dim testColumn As Long, testRow As Long
Dim used As Boolean
For testRow = 4 To 37 Step 11
used = False
For testColumn = 2 To 13
If IsEmpty(sheet.Cells(testRow, testColumn)) = False Then
used = True
Exit For
End If
Next testColumn
If used = False Then
FindFirstUnusedRow = testRow
Exit For
End If
Next testRow
End Function
Then in your code, call it before your loop:
Sub Worklist()
Dim var As Variant
Dim i As Long, dashPos As Long, periodPos As Long, colNum As Long
Dim rowNum As Long, Dim sheet As Worksheet
Application.ScreenUpdating = False
Set sheet = ThisWorkbook.Sheets("Tray")
rowNum = FindFirstUnusedRow(sheet)
If rowNum = 0 Then
Debug.Print "All trays full."
Exit Sub
End If
Dim current As ListBox
For colNum = 2 To 13
Set current = UserForm2.Controls("Listbox" & colNum)
If current.ListIndex <> -1 Then 'if listbox is not blank
sheet.Cells(rowNum - 2, colNum).Value = UserForm2.TextBox1.Value
For i = 0 To current.ListCount - 1
var = current.List(i)
dashPos = InStr(1, var, "--")
periodPos = InStr(1, var, ".")
sheet.Cells(rowNum + i, colNum) = Left$(var, dashPos - 1) & _
Right$(var, Len(var) - periodPos + 1)
Next i
End If
Next colNum
Application.ScreenUpdating = True
End Sub
A couple other notes: You can ditch the Sheets("Tray").Select line entirely - you never use the selection object. Same thing with the mixed references to ActiveSheet and ThisWorkbook.Sheets("Tray"). Grab a reference and use it.
Also, these lines don't do what you think they do:
Dim Var, VarName As Variant
Dim i, DblDashPos, FirstPeriodPos, lngColNum, lngRowNum As Long
Of all the variables you declare, everything is a Variant except lngRowNum. If you want to combine declarations on one line like that, you still need to specify a type for each variable, or they'll default to Variant. See the example code above.