excel-vba
Распространенные ошибки
Поиск…
Квалификационные ссылки
При обращении к worksheet
, range
или отдельным cells
важно полностью квалифицировать ссылку.
Например:
ThisWorkbook.Worksheets("Sheet1").Range(Cells(1, 2), Cells(2, 3)).Copy
Не является полностью квалифицированным: ссылки на Cells
не имеют рабочей книги и рабочего листа, связанных с ними. Без явной ссылки Cells ссылается на ActiveSheet
по умолчанию. Таким образом, этот код не сработает ( ActiveSheet
неверные результаты), если ActiveSheet
отличным от Sheet1
является текущий ActiveSheet
.
Самый простой способ исправить это использовать With
утверждением следующим образом :
With ThisWorkbook.Worksheets("Sheet1")
.Range(.Cells(1, 2), .Cells(2, 3)).Copy
End With
Кроме того, вы можете использовать переменную Worksheet. (Это, скорее всего, будет предпочтительным, если ваш код должен ссылаться на несколько рабочих листов, например, копирование данных с одного листа на другой.)
Dim ws1 As Worksheet
Set ws1 = ThisWorkbook.Worksheets("Sheet1")
ws1.Range(ws1.Cells(1, 2), ws1.Cells(2, 3)).Copy
Другая частая проблема заключается в ссылке на коллекцию Рабочих таблиц без квалификации рабочей книги. Например:
Worksheets("Sheet1").Copy
Sheet1
не имеет полной квалификации и отсутствует рабочая тетрадь. Это может завершиться неудачей, если в коде ссылаются несколько книг. Вместо этого используйте одно из следующих действий:
ThisWorkbook.Worksheets("Sheet1") '<--ThisWorkbook refers to the workbook containing
'the running VBA code
Workbooks("Book1").Worksheets("Sheet1") '<--Where Book1 is the workbook containing Sheet1
Однако не используйте следующее:
ActiveWorkbook.Worksheets("Sheet1") '<--Valid, but if another workbook is activated
'the reference will be changed
Аналогично для объектов range
, если они явно не определены, range
будет относиться к текущему активному листу:
Range("a1")
Такой же как:
ActiveSheet.Range("a1")
Удаление строк или столбцов в цикле
Если вы хотите удалить строки (или столбцы) в цикле, вы всегда должны начинать цикл с конца диапазона и перемещаться назад на каждом шаге. В случае использования кода:
Dim i As Long
With Workbooks("Book1").Worksheets("Sheet1")
For i = 1 To 4
If IsEmpty(.Cells(i, 1)) Then .Rows(i).Delete
Next i
End With
Вы пропустите несколько строк. Например, если код удаляет строку 3, то строка 4 становится строкой 3. Однако переменная i
изменится на 4. Таким образом, в этом случае код пропустит одну строку и проверит другую, которая ранее не была в диапазоне.
Правильный код будет
Dim i As Long
With Workbooks("Book1").Worksheets("Sheet1")
For i = 4 To 1 Step -1
If IsEmpty(.Cells(i, 1)) Then .Rows(i).Delete
Next i
End With
ActiveWorkbook против ThisWorkbook
ActiveWorkbook
и ThisWorkbook
иногда становятся взаимозаменяемыми новыми пользователями VBA без полного понимания того, к чему относится каждый объект, что может привести к нежелательному поведению во время выполнения. Оба этих объекта относятся к объекту приложения
Объект ActiveWorkbook
относится к ActiveWorkbook
которая в настоящее время находится в самом верхнем виде объекта приложения Excel во время выполнения. (например, книгу, которую вы можете видеть и взаимодействовать в момент, когда этот объект ссылается)
Sub ActiveWorkbookExample()
'// Let's assume that 'Other Workbook.xlsx' has "Bar" written in A1.
ActiveWorkbook.ActiveSheet.Range("A1").Value = "Foo"
Debug.Print ActiveWorkbook.ActiveSheet.Range("A1").Value '// Prints "Foo"
Workbooks.Open("C:\Users\BloggsJ\Other Workbook.xlsx")
Debug.Print ActiveWorkbook.ActiveSheet.Range("A1").Value '// Prints "Bar"
Workbooks.Add 1
Debug.Print ActiveWorkbook.ActiveSheet.Range("A1").Value '// Prints nothing
End Sub
Объект ThisWorkbook
относится к книге, в которой код принадлежит к моменту его выполнения.
Sub ThisWorkbookExample()
'// Let's assume to begin that this code is in the same workbook that is currently active
ActiveWorkbook.Sheet1.Range("A1").Value = "Foo"
Workbooks.Add 1
ActiveWorkbook.ActiveSheet.Range("A1").Value = "Bar"
Debug.Print ActiveWorkbook.ActiveSheet.Range("A1").Value '// Prints "Bar"
Debug.Print ThisWorkbook.Sheet1.Range("A1").Value '// Prints "Foo"
End Sub
Интерфейс с одним документом и несколькими документами
Имейте в виду, что Microsoft Excel 2013 (и выше) использует Single Document Interface (SDI), а Excel 2010 (и ниже) использует Multiple Document Interfaces (MDI).
Это означает, что для Excel 2013 (SDI) каждая рабочая тетрадь в одном экземпляре Excel содержит собственный интерфейс ленты:
И наоборот, для Excel 2010 каждая рабочая тетрадь в одном экземпляре Excel использовала общий интерфейс (MDI) с лентой:
Это вызывает некоторые важные проблемы, если вы хотите перенести код VBA (2010 <-> 2013), который взаимодействует с лентой.
Необходимо создать процедуру обновления ленточных элементов управления пользовательского интерфейса в одном и том же состоянии во всех книгах для Excel 2013 и выше.
Обратите внимание, что :
- Все методы, события и свойства окна на уровне приложения Excel остаются неизменными. (
Application.ActiveWindow
,Application.Windows
...) - В Excel 2013 и выше (SDI) все окна, методы и свойства окна на рабочем столе теперь работают в окне верхнего уровня. Можно получить дескриптор этого окна верхнего уровня с помощью
Application.Hwnd
Чтобы получить более подробную информацию, см. Источник этого примера: MSDN .
Это также вызывает некоторые проблемы с немодальными пользовательскими формами. См. Здесь для решения.