Макрос работает только при переходе - PullRequest
1 голос
/ 26 сентября 2019

Следующий макрос работает только когда я прохожу через него, в противном случае он пропускает очень важную строку, которая удаляет строку в таблице данных.Любые предложения относительно того, почему?: /

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 Ответ

3 голосов
/ 26 сентября 2019

VBA не «пропускает» какие-либо строки кода, он просто не работает таким образом: определенно есть лучшее доказуемое объяснение - очень вероятно, что есть небольшая ошибка в логике тела цикла For Each, которая вызывает Row_Number для хранения неправильного значения.

Я бы предложил упростить логику цикла, чтобы сделать ее более очевидно правильной, используя API ListObject и избавившись от переменной 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.И поскольку все параметры являются очень стандартными входами , мы можем передать их 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) и дали понять, что любой запускошибка времени, возникшая в этой процедуре, является совершенно неожиданной.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...