宏仅在逐步执行时起作用

时间:2019-09-26 19:08:10

标签: excel vba

以下宏仅在我单步执行时起作用,否则它将跳过一条非常重要的行,该行将删除数据表中的行。关于为什么的任何建议?:/

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”并创建一个单独的宏,该宏仅处理“删除”部分,但是没有运气。

1 个答案:

答案 0 :(得分:3)

VBA不会“跳过”任何代码行,只是行不通:肯定有一个更好的,可证明的解释-很可能是For Each循环主体逻辑中的一个细微错误,导致Row_Number保持错误的值。

我建议通过利用ListObject API并摆脱Row_Number变量,然后将这些全局声明移到它们唯一的过程中,简化循环逻辑以使其更明显地正确。实际使用并与之相关。

旁注:所有这些下划线和大小写不一致都令人分心;考虑使用PascalCase表示成员名称,使用camelCase表示本地用户。

Set table = Database_Utveckling.ListObjects("Table_Utveckling")

为什么要重新分配调用方已经给您的参数?让我们重写此过程-将逻辑提取到自己的范围内是一个非常好的决定-您需要尽可能少的专门的小型过程。我们要做什么?给定ListObject,从projectCode删除特定的行。因此,我们的输入需要包含ListObjectString-并且我们不需要其他任何东西:

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>