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