Ошибка времени выполнения '424' Требуется объект - Не знаете, что я написал неправильно в этом коде? - PullRequest
0 голосов
/ 25 сентября 2019
Sub automated_gr_lookup()
    Dim st As Long
    Dim en As Long
    Dim c1 As Long
    Dim c2 As Long
    Dim iRowAsset As Integer
    Dim table As Range
    Dim tmpRiskID As Variant

    Sheets("Geotechnical Risk Register").Select

    Application.ScreenUpdating = False

    Set assetTbl = Application.Range("M002_") 'Asset table = DES M002
    Set riskTbl = Application.Range("geotechRisks") 'GRR
    Set compiledTbl = Application.Range("CompiledM002") 'Output for M002


    For iRowRisk = 1 To riskTbl.Rows.Count 'loop through risks
        tmpRiskID = riskTbl.Row.Range("Ref No. ID") 'Temporary risk as it gets overridden


        'if assets chainage match risk iRowRisk (4 conditions)
        If (en > c1 And en < c2) Or (st > c1 And en < c2) Or (st > c1 And st < c2) Or (st < c1 And en > c2) Then
            'copy asset row
            assetTbl.Rows(iRowAsset).Copy
            'paste row in compiled
            compiledTbl.Rows(xlEndRow).PasteSpecial xlPasteValues
            'paste risk id in last column of that row
            compiledTbl.Cells(xlEndRow, ColumnH).Value = tmpRiskID
        End If
    Next iRowRisk

End Sub

Я получаю runtime error 424 на линии tmpRiskID = riskTbl.Row.Range("Ref No. ID") и не знаю, почему?В настоящее время я пытаюсь присвоить его заголовку таблицы.

1 Ответ

0 голосов
/ 25 сентября 2019
Sheets("Geotechnical Risk Register").Select

Здесь начинаются проблемы.Где этот лист?Если он находится в независимо от того, какая рабочая книга активна , то возникает ошибка времени выполнения 9 «индекс вне диапазона», ожидающий напоминания о том, что независимо от того, какая книга активна , необязательно может иметьлист с таким именем.

Если лист находится в той же книге, в которой размещен ваш код VBA, то вы не хотите «какую-либо книгу, активную в данный момент», но ThisWorkbook- это уже было бы лучше:

ThisWorkbook.Worksheets("Geotechnical Risk Register").Select

Но вам не нужно до .Select ничего.Подумайте:

Dim ws As Worksheet
Set ws = ThisWorkbook.Worksheets("Geotechnical Risk Register")

Гораздо лучше: теперь мы можем делать вызовы участников на этот конкретный лист, и мы знаем , мы работаем с правильным листом.

За исключением, если лист существует в ThisWorkbook во время компиляции, то он полностью избыточен.

Выберите модуль листа в инструментальном окне VBE Project Explorer (Ctrl + R), затем вызовитеего свойства (F4).В верхней части списка свойств находится свойство (Name), которое в настоящее время, вероятно, имеет вид Sheet42 - измените его на что-то более значимое, скажем, GeotechRiskRegisterSheet.Теперь у вас есть переменная объекта global-scope (project-scope, фактически) с именем GeotechRiskRegisterSheet, которую вы можете использовать в любом месте вашего проекта VBA для ссылки на этого конкретного листа .

Остальныеэтого ответа предполагает, что вы сделали это.Если лист находится не в ThisWorkbook, тогда придерживайтесь объявления для него локальной переменной Worksheet и извлечения ссылки на объект Worksheet из коллекции Workbook.Worksheets книги, с которой вы собираетесь работать .

Set assetTbl = Application.Range("M002_")

Это присваивание переменной, которая, по-видимому, хочет быть локальной переменной, но нигде не объявлена ​​в локальной области видимости.Application.Range лишь немного лучше, чем Range, и только на один уровень косвенности от наиболее явного ActiveSheet.Range - но опять же, вы не хотите работать независимо от того, является ли ActiveSheet , и, хотя в таблицах do определены именованные диапазоны и именованные диапазоны, можно получить доступ с помощью свойства Range, это было бы гораздо яснее и абсолютно однозначно:

Dim assetTbl As ListObject
Set assetTbl = GeotechRiskRegisterSheet.ListObjects("M002_")

То же самое относится и к двум другим:

Set riskTbl = Application.Range("geotechRisks") 'GRR
Set compiledTbl = Application.Range("CompiledM002") 'Output for M002

Идем с:

Dim riskTbl As ListObject
Set riskTbl = GeotechRisksSheet.ListObjects("geotechRisks")

Dim compiledTbl As ListObject
Set compiledTbl = CompiledSheet.ListObjects("CompiledM002")

И затем мы начинаем цикл:

For iRowRisk = 1 To riskTbl.Rows.Count 'loop through risks

Если riskTbl - это просто еще один Range, то это работает.Если riskTbl является ListObject, мы можем сделать проще и быстрее:

Dim currentRow As ListRow
For Each currentRow In riskTbl.ListRows

И затем мы получаем ошибку времени выполнения, о которой вы спрашиваете:

tmpRiskID = riskTbl.Row.Range("Ref No. ID") 'Temporary risk as it gets overridden

Это ошибка 424 "требуется объект", потому что riskTbl.Row является целым значением Long: у него нет члена Range, поэтому вы не можете сделать Range.Row.Range, как это.Если вы выполняете итерацию простого Range с этим циклом For iRowRisk, то вы хотите, чтобы он выглядел примерно так:

tmpRiskID = riskTbl.Cells(iRowRisk, 12).Value

Где 12 - это жестко закодированный индекс столбца, ссылающийся на "Ref № ID "столбец.Не идеально, правда?Сравните с использованием ListObject API:

Dim refIDColumn As Long
refIDColumn = riskTbl.ListColumns("Ref No. ID").Index

tmpRiskID = currentRow.Range.Cells(ColumnIndex:=refIDColumn).Value

Если вы определите переменную refIDColumn вне цикла, каждая итерация может повторно использовать свое значение и до тех пор, пока заголовок заголовка остается "Ref No. IDmsgstr "столбец может находиться в любом месте, где его хочет пользователь - тогда как при жестко закодированном индексе столбца, если пользователь вставляет новый столбец, код прерывается.

    'if assets chainage match risk iRowRisk (4 conditions)
    If (en > c1 And en < c2) Or (st > c1 And en < c2) Or (st > c1 And st < c2) Or (st < c1 And en > c2) Then

I 'Я надеюсь, что вы объявили и присвоили эти переменные в своем реальном коде - иначе это условное выражение не может быть надежно оценено, поскольку ни один из этих идентификаторов не имеет значения.Подумайте об использовании значимых имен, которые передают намерение использования: en, st, c1 и c2 абсолютно ничего не значат для тех, кто не очень знаком с мыслительным процессом того, кто написал этот код.В любом случае, убедитесь, что в верхней части модуля где-то написано Option Explicit.

        'copy asset row
        assetTbl.Rows(iRowAsset).Copy
        'paste row in compiled
        compiledTbl.Rows(xlEndRow).PasteSpecial xlPasteValues
        'paste risk id in last column of that row
        compiledTbl.Cells(xlEndRow, ColumnH).Value = tmpRiskID

Я не уверен, что вам действительно нужно задействовать буфер обмена здесь.Опять же, рассмотрите возможность использования API ListObject - это должно работать (не проверено):

    With compiledTbl.ListRows.Add
        .Range.Value = currentRow.Range.Value
        .Range.Cells(ColumnIndex:=compiledRiskIdColumn).Value = tmpRiskID
    End With

Где compiledRiskIdColumn будет объявлено и назначено перед телом цикла:

Dim compiledRiskIdColumn As Long
compiledRiskIdColumn = compieldTbl.ListColumns("Risk ID").Index '<~ verify column title
...