以下宏仅在我单步执行时起作用,否则它将跳过一条非常重要的行,该行将删除数据表中的行。关于为什么的任何建议?:/
Public table As ListObject
Public project_code As String
Public Row_Number As Integer
Public sheet As Worksheet
Public myCol As New Collection
Sub UpdateProject_Utveckling()
Set sheet = Database_Utveckling
Set table = sheet.ListObjects("Table_Utveckling") ' "Table_Utveckling" is a table
project_code = InputSheet_Utveckling.Range("RemoveProject") ' "InputSheet_Utveckling is a worksheet
DeleteProjectUtveckling table, project_code, myCol, sheet
AddProject_Utveckling 'Probably not essential for the Q
Set myCol = Nothing ' This is a collection
End Sub
Sub DeleteProjectUtveckling(table As ListObject, project_code As String, myCol As Collection, sheet As Worksheet)
Dim tableColumn As Range
Set table = Database_Utveckling.ListObjects("Table_Utveckling")
Set tableColumn = table.ListColumns("Projektkod").DataBodyRange ' "Projektkod" is a column in
For Each rng_1 In tableColumn
If project_code = rng_1 Then
Row_Number = rng_1.Row
Exit For
Else
End If
Next rng_1
sheet.Rows(Row_Number).Delete ' It skips this line when I run it (not when I step through
End Sub
这让我发疯了!我已经尝试过“ DoEvents”并创建一个单独的宏,该宏仅处理“删除”部分,但是没有运气。
答案 0 :(得分:3)
VBA不会“跳过”任何代码行,只是行不通:肯定有一个更好的,可证明的解释-很可能是For Each
循环主体逻辑中的一个细微错误,导致Row_Number
保持错误的值。
我建议通过利用ListObject
API并摆脱Row_Number
变量,然后将这些全局声明移到它们唯一的过程中,简化循环逻辑以使其更明显地正确。实际使用并与之相关。
旁注:所有这些下划线和大小写不一致都令人分心;考虑使用PascalCase
表示成员名称,使用camelCase
表示本地用户。
Set table = Database_Utveckling.ListObjects("Table_Utveckling")
为什么要重新分配调用方已经给您的参数?让我们重写此过程-将逻辑提取到自己的范围内是一个非常好的决定-您需要尽可能少的专门的小型过程。我们要做什么?给定ListObject
,从projectCode
删除特定的行。因此,我们的输入需要包含ListObject
和String
-并且我们不需要其他任何东西:
Private Sub DeleteProjectUtveckling(ByVal table As ListObject, ByVal projectCode As String)
End Sub
请注意,该过程为Private
,因为它不需要为Public
。而且由于参数都是非常标准的 inputs ,我们可以将它们传递给ByVal
。
我们要做的第一件事是在table
中找到可能包含projectCode
的列。并且由于该列可能不存在于提供的表中,因此我们需要验证它是否存在。
Private Sub DeleteProjectUtveckling(ByVal table As ListObject, ByVal projectCode As String)
On Error GoTo CleanFail
Dim projectCodeColumnIndex As Long
projectCodeColumnIndex = table.ListColumns("Projektkod").Index
'...todo...
CleanExit:
Exit Sub
CleanFail:
MsgBox "Column 'Projektkod' was not found in table '" & table.Name & "'."
Resume CleanExit
End Sub
接下来,我们需要遍历表行,并确定是否找到了项目代码。
Dim currentRow As ListRow
For Each currentRow In table.ListRows
If currentRow.Range.Cells(ColumnIndex:=projectCodeColumnIndex).Value = projectCode Then
'...todo...
End If
Next
ListRow
的优点是,它已经知道如何删除自身:我们不需要关心任何行号或工作表行:
currentRow.Delete
如果保证该表不包含任何重复的项目代码,那么我们就完成了-我们可以无缘无故地迭代其余行,或者像您一样聪明并立即采取行动。
因此重写的过程变为:
Private Sub DeleteProjectUtveckling(ByVal table As ListObject, ByVal projectCode As String)
On Error GoTo CleanFail
Dim projectCodeColumnIndex As Long
projectCodeColumnIndex = table.ListColumns("Projektkod").Index
Dim currentRow As ListRow
For Each currentRow In table.ListRows
If currentRow.Range.Cells(ColumnIndex:=projectCodeColumnIndex).Value = projectCode Then
currentRow.Delete
Exit For
End If
Next
CleanExit:
Exit Sub
CleanFail:
MsgBox "Column 'Projektkod' was not found in table '" & table.Name & "'."
Resume CleanExit
End Sub
调用代码变为:
DeleteProjectUtveckling table, project_code
现在,该错误处理将起作用,但是如果在该范围内发生任何意外情况,我们将收到有关找不到列的误导性和混乱信息。让我们解决这个问题。
Private Function TryGetColumnIndex(ByVal table As ListObject, ByVal columnName As String, ByRef outIndex) As Boolean
On Error Resume Next
outIndex = table.ListColumns(columnName).Index
TryGetColumnIndex = (Err.Number = 0)
On Error GoTo 0
End Function
这个小功能有一个目的:给定表和列名称,获取列索引。如果有效,它将返回True
,如果无效,将返回False
,并且当其有效时,outIndex
参数将保留我们要查找的列索引。
现在我们可以像这样编写行删除过程:
Private Sub DeleteProjectUtveckling(ByVal table As ListObject, ByVal projectCode As String)
On Error GoTo CleanFail
Dim projectCodeColumnIndex As Long
If Not TryGetColumnIndex(table, "Projektkod", outIndex:=projectCodeColumnIndex) Then
MsgBox "Column 'Projektkod' was not found in table '" & table.Name & "'."
Exit Sub
End If
Dim currentRow As ListRow
For Each currentRow In table.ListRows
If currentRow.Range.Cells(ColumnIndex:=projectCodeColumnIndex).Value = projectCode Then
currentRow.Delete
Exit For
End If
Next
CleanExit:
Exit Sub
CleanFail:
MsgBox "Unexpected error: " & Err.Description
Resume CleanExit <~ F9 to place a breakpoint here
Resume '<~ use for step-through debugging. takes you to the instruction that raised the error
End Sub
仅通过提取一个小的小函数,我们就将模棱两可的错误处理变成了标准控制流(If...End If
),并明确表明在该过程中引发的任何运行时错误都是完全意外的。 / p>