提问人:Shiela 提问时间:9/30/2023 最后编辑:Shiela 更新时间:10/5/2023 访问量:236
在 ComBox 更改后使用时间计算填充 ListBox
Populate ListBox with Time Calculation after ComBox change
问:
我这里有一个简单的用户表单,它根据组合框的变化填充列表框。
combobox 中唯一列表的代码:
Private Sub UserForm_Initialize()
'used this code to get a dynamic combobox unique Task list in Sheet1 Column A
'but I wonder why there is an extra space after the last item in combobox
Dim v, e
With Sheets("Sheet1").Range("A2:A10000")
v = .value
End With
With CreateObject("scripting.dictionary")
.CompareMode = 1
For Each e In v
If Not .Exists(e) Then .Add e, Nothing
Next
If .Count Then Me.ComboBox1.List = Application.Transpose(.Keys)
End With
End Sub
原始数据更新(在 excel 中添加了 F 列和 G 列) 请不要介意列的排列方式,因为它们是有目的的。
Task ||ID ||PARAGRAPH #|| START ||END || Month || Name
Writing ||4823 || 1 ||13:00:00 ||13:15:00 || January || Larry
Reading ||4823 || 1 ||13:16:00 ||13:18:00 || February || Larry
Writing ||4823 || 2 ||13:20:00 ||13:30:00 || March || Larry
Reading ||4823 || 2 ||13:31:00 ||13:50:00 || April || Larry
Writing ||4824 || 1 ||14:00:00 ||14:10:00 || October || Cole
Reading ||4824 || 1 ||14:11:00 ||14:14:00 || October || Cole
这是我使用当前月份和列名称进行组合框更改的更新期望结果(不需要在 excel 工作表中具有总时间列,只需在列表框中):
Private Sub ComboBox1_Change()
If ComboBox1.value = "Writing" And Month = current month Then '***
'if values are present then
'calculate time (end - start) for Writing rows
'populate listbox of Writing entries with Total Time Column, Month Column, Name Column
'no need to populate start and end cols
'if there are no values found in Sheet1
'ListBox1 is just blank
ElseIf ComboBox1.value = "Reading" and Month = current month Then '***
'if values are present then
'calculate time (end - start) for Reading rows
'populate listbox of Reading entries with Total Time Column, Month Column, Name Column
'no need to populate start and end cols
'if there are no values found in Sheet1
'ListBox1 is just blank
End If
End Sub
更新了 ListBox 所需的写入结果以及当前月份和列名称:
更新了 ListBox 所需的读取结果以及当前月份和列名称:
注意:月份格式为“现在,”mmmm” 筛选中不需要名称。只需要提出清单。
这个问题也有计算,但它是针对唯一 ID 的。当前问题不需要是唯一的,只要列表框根据组合框选择填充即可。此处的答案 1 满足组合框更改后的显示,但列表框中没有时间计算或总计列(结束-开始)。编辑前的答案 2 有时间计算,但没有月份和列名;答案 2 中编辑的答案返回空白列表框。提前致谢。
答:
找到下面的代码,它将仅显示与组合框中选择的标准匹配的相关项目。
代码位于用户窗体模块中的几个子例程中。 代码注释中的详细说明。
代码(已测试)
Option Explicit
Dim LBDataArr As Variant
Dim CBDataArr As Variant
Dim TaskSelectedStr As String
' ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Private Sub ComboBox1_Change()
TaskSelectedStr = Me.ComboBox1.Value ' save in User-Form Public variable
' ~~ Call Sub that loads only relevant Array items to List-Box, by matching the searched String in the current Combo-Box ~~~
LoadRelevantItemsToListBox
End Sub
' ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Private Sub UserForm_Initialize()
' ~~~ Call Sub that saves the data in "Sheet1" to arrays ~~~
ReadSheet1ToArray
With Me.ComboBox1
.Clear
.List = CBDataArr
End With
' --- populate List-Box ---
With Me.ListBox1
.Clear
.List = LBDataArr
End With
End Sub
' ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Sub ReadSheet1ToArray()
'======================================================================================================================
' Description : Sub reads all rows in "Sheet1" worksheet, to 'LBDataArr' 2-D array, and unique values of "Task" in
' 'CBDataArr' array.
'
' Caller(s) : Sub 'UserForm_Initialize' (in this module)
'======================================================================================================================
Dim i As Long, LastRow As Long, ArrIndex As Long, MatchRow As Variant
Application.ScreenUpdating = False
' === Save "Materials DB" worksheet fields in 'TempArr' 2-D Array ===
With ThisWorkbook.Sheets("Sheet1")
LastRow = .Cells(.Rows.Count, "B").End(xlUp).Row
LBDataArr = .Range(.Cells(2, "A"), .Cells(LastRow, "E")).Value ' save entire "Sheet1" worksheet contents in 2-D array
ReDim CBDataArr(1 To LastRow)
ArrIndex = 0
' --- Loop over LB Array and save only unique values in column A ---
For i = 1 To UBound(LBDataArr, 1)
If LBDataArr(i, 1) <> "" Then ' include only rows with text in them
MatchRow = Application.Match(LBDataArr(i, 1), CBDataArr, 0)
If IsError(MatchRow) Then
ArrIndex = ArrIndex + 1
CBDataArr(ArrIndex) = LBDataArr(i, 1)
End If
End If
Next i
If ArrIndex > 0 Then
ReDim Preserve CBDataArr(1 To ArrIndex)
End If
End With
Application.ScreenUpdating = True
End Sub
' ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Sub LoadRelevantItemsToListBox()
'======================================================================================================================
' Description : Sub scans through the entire 'LBDataArr' (read from "Sheet1" worksheet).
' Per record tries to match the record's data with the values entered in 'Combo-Box1'
'
' Caller(s) : ComboBox1_Change (Combo-Box Change event in this Module)
'======================================================================================================================
Dim i As Long, j As Long, LastRow As Long, Col As Long, ArrIndex As Long, MatchRow As Variant
Dim tempArr As Variant
Application.ScreenUpdating = False
' ~~~ Call Sub that reads all "Sheet1" to 'LBDataArr' 2-D Array ~~~
ReadSheet1ToArray
tempArr = LBDataArr ' save contents of array in 'Temp' array
ReDim LBDataArr(1 To UBound(tempArr, 1), 1 To 5) ' reset Array
ArrIndex = 0
' === loop through arrays >> faster ===
For i = 1 To UBound(tempArr, 1)
If tempArr(i, 1) = TaskSelectedStr Then
' make sure current 'row' passes searched criteria --> add to Array (and List-Box)
ArrIndex = ArrIndex + 1
' - I added the columns one by one in case you want to manipulate the daa in one of the columns -
LBDataArr(ArrIndex, 1) = tempArr(i, 1) ' TASK
LBDataArr(ArrIndex, 2) = tempArr(i, 2) ' ID
LBDataArr(ArrIndex, 3) = tempArr(i, 3) ' PARAGRAPH #
LBDataArr(ArrIndex, 4) = Format(tempArr(i, 4), "h:mm:ss") ' START
LBDataArr(ArrIndex, 5) = Format(tempArr(i, 5), "h:mm:ss") ' END
End If
Next i
' at least 1 record match the criteria in 'Task' Combo-Box
If ArrIndex >= 1 Then
' ~~~ Nice TRICK to redim first Dimension of 2-D array ~~~
tempArr = LBDataArr
ReDim LBDataArr(1 To ArrIndex, 1 To UBound(LBDataArr, 2))
For i = 1 To ArrIndex
For Col = 1 To UBound(LBDataArr, 2)
LBDataArr(i, Col) = tempArr(i, Col)
Next Col
Next i
With Me.ListBox1
.Clear
.List = LBDataArr
End With
Else ' no result match
Me.ListBox1.Clear
MsgBox "No matches for the criteria entered in 'Task' Combo-Box '", vbCritical, "Search Null"
End If
Application.ScreenUpdating = True
End Sub
评论
Format(tempArr(i, 4), "h:mm:ss")
请测试下一个代码:
Private Sub ComboBox1_Change()
Dim sh As Worksheet, lastR As Long, arr, arrFin, count As Long, i As Long, j As Long, k As Long
Set sh = ActiveSheet 'use here the necessary one
lastR = sh.Range("A" & sh.rows.count).End(xlUp).Row
arr = sh.Range("A2:F" & lastR).value 'place the range in an array for faster processing
count = WorksheetFunction.CountIf(sh.Range("A2:A" & lastR), ComboBox1.value) 'count the specific string occurrences
If count > 0 Then
ReDim arrFin(1 To count, 1 To UBound(arr, 2) - 1) 'redim the final aray
For i = 1 To UBound(arr)
If arr(i, 1) = ComboBox1.value Then
k = k + 1
For j = 1 To UBound(arrFin, 2)
If j = UBound(arrFin, 2) Then
arrFin(k, j) = Format(arr(i, j + 1) - arr(i, j), "hh:mm:ss")
Else
arrFin(k, j) = arr(i, j)
End If
Next j
End If
Next i
Else
listBox1.Clear
End If
With listBox1
.ColumnCount = UBound(arrFin, 2)
.List = arrFin
End With
End Sub
当然,您必须根据需要设置每列的宽度。
已编辑:
下一个版本也将过滤第六列(当前月份)上返回的数组,也会带来第七列。注意在 G:G 列中包含 STRINGS 月份名称:
Private Sub ComboBox1_Change()
Dim sh As Worksheet, lastR As Long, arr, arrFin, count As Long, i As Long, j As Long, k As Long
Dim arrMonths: arrMonths = Split("January,February,March,April,May,June,July,August,September,October,November,December", ",")
Dim curMonth As String: curMonth = arrMonths(Month(Date) - 1)
Set sh = ActiveSheet 'use here the necessary one
lastR = sh.Range("A" & sh.Rows.count).End(xlUp).Row 'last row in the range to be processed
arr = sh.Range("A2:G" & lastR).Value 'place the range in an array for faster processing
'calculate the necessary array elements:
count = WorksheetFunction.CountIfs(sh.Range("A2:A" & lastR), ComboBox1.Value, sh.Range("F2:F" & lastR), curMonth)
If count > 0 Then
ReDim arrFin(1 To count, 1 To UBound(arr, 2) - 1) 'redim the necessary array to keep the rows to be loaded in list box
For i = 1 To UBound(arr)
If arr(i, 1) = ComboBox1.Value And arr(i, 6) = curMonth Then
k = k + 1
For j = 1 To UBound(arrFin, 2)
If j = UBound(arrFin, 2) - 2 Then
arrFin(k, j) = Format(arr(i, j + 1) - arr(i, j), "hh:mm:ss")
ElseIf j = UBound(arrFin, 2) - 1 Then
arrFin(k, UBound(arrFin, 2) - 1) = curMonth
ElseIf j = UBound(arrFin, 2) Then
arrFin(k, UBound(arrFin, 2)) = arr(i, j + 1)
Else
arrFin(k, j) = arr(i, j)
End If
Next j
End If
Next i
Else
ListBox1.Clear: Exit Sub
End If
With ListBox1
.ColumnCount = UBound(arrFin, 2)
.List = arrFin
End With
End Sub
如果您需要收集更多列,请将它们放在最后四列之前。根据问题设计一段代码。如果必须返回一个新列,代码可以相对容易地调整,但是如果你想要另一个,然后再返回另外两个,就很难处理了。
如上所述,如果您在最后四个问题之前添加所有这些内容,那么在仅调整下一个问题后,它将运行良好:
- 将要处理的范围扩展到最后一列 (
arr = sh.Range("A2:x" & lastR).value
) - 确定保留月份名称的列,并在计算的第二部分使用它 (
count
sh.Range("x2:x" & lastR), curMonth
) - 在数组处理中使用上述列 NUMBER()。
arr(i, colNo) = curMonth
评论
上一个:在级联组合框上出现错误
评论
List
If Len(e) > 0 Then: If Not .Exists(e) Then .Add e, Empty: End If
Nothing
With ThisWorkbook.Sheets("Sheet1").Range("A1").CurrentRegion: With .Resize(.Rows.Count - 1, 1).Offset(1): v = .Value: End With: End With