Как избежать использования Select в Excel VBA

537

Я много слышал о понятном отвращении к использованию .Selectв Excel VBA, но не уверен, как его избежать. Я обнаружил, что мой код был бы более пригодным для повторного использования, если бы я мог использовать переменные вместо Selectфункций. Тем не менее, я не уверен, как ссылаться на вещи (например, и ActiveCellт. Д.), Если не использовать Select.

Я нашел эту статью о диапазонах и этот пример о преимуществах неиспользования выбора, но ничего не могу найти о том, как ?

BiGXERO
источник
14
Важно отметить, что есть случаи, когда использование Selectи / или и ActiveSheetт. Д. Совершенно неизбежно. Вот пример, который я нашел: stackoverflow.com/questions/22796286/…
Рик поддерживает Монику
9
И бывают случаи - редактирование данных диаграммы в ppt с использованием файла Excel - когда требуется активация или выбор.
brettdj
@brettdj - вот недавний пример . Кажется .Select / .Selection, для того, чтобы установить для всех листов в книге одно и то же значение .
Брюс Уэйн
3
@ Брюс из того же QA кажется, что это не так
Крис Нилсен

Ответы:

565

Некоторые примеры того, как избежать выбора

Используйте Dimпеременные d

Dim rng as Range

Setпеременная до необходимого диапазона. Есть много способов обратиться к диапазону одной ячейки

Set rng = Range("A1")
Set rng = Cells(1,1)
Set rng = Range("NamedRange")

или многосотовый диапазон

Set rng = Range("A1:B10")
Set rng = Range("A1", "B10")
Set rng = Range(Cells(1,1), Cells(10,2))
Set rng = Range("AnotherNamedRange")
Set rng = Range("A1").Resize(10,2)

Вы можете использовать ярлык для Evaluateметода, но он менее эффективен и его, как правило, следует избегать в рабочем коде.

Set rng = [A1]
Set rng = [A1:B10]

Все приведенные выше примеры относятся к ячейкам на активном листе . Если вы специально не хотите работать только с активным листом, лучше также затемнить Worksheetпеременную.

Dim ws As Worksheet
Set ws = Worksheets("Sheet1")
Set rng = ws.Cells(1,1)
With ws
    Set rng = .Range(.Cells(1,1), .Cells(2,10))
End With

Если вы действительно хотите работать с ActiveSheet, для ясности , что лучше быть явным. Но будьте осторожны, так как некоторые Worksheetметоды меняют активный лист.

Set rng = ActiveSheet.Range("A1")

Опять же, это относится к активной книге . Если вы специально не хотите работать только с ActiveWorkbookили ThisWorkbook, лучше также Dim Workbookпеременную.

Dim wb As Workbook
Set wb = Application.Workbooks("Book1")
Set rng = wb.Worksheets("Sheet1").Range("A1")

Если вы действительно хотите работать с ActiveWorkbook, для ясности , что лучше быть явным. Но будьте осторожны, так как многие WorkBookметоды меняют активную книгу.

Set rng = ActiveWorkbook.Worksheets("Sheet1").Range("A1")

Вы также можете использовать ThisWorkbookобъект для ссылки на книгу, содержащую исполняемый код.

Set rng = ThisWorkbook.Worksheets("Sheet1").Range("A1")

Обычный (плохой) кусок кода - открыть книгу, получить некоторые данные и снова закрыть

Это плохо:

Sub foo()
    Dim v as Variant
    Workbooks("Book1.xlsx").Sheets(1).Range("A1").Clear
    Workbooks.Open("C:\Path\To\SomeClosedBook.xlsx")
    v = ActiveWorkbook.Sheets(1).Range("A1").Value
    Workbooks("SomeAlreadyOpenBook.xlsx").Activate
    ActiveWorkbook.Sheets("SomeSheet").Range("A1").Value = v
    Workbooks(2).Activate
    ActiveWorkbook.Close()
End Sub

И было бы лучше, как:

Sub foo()
    Dim v as Variant
    Dim wb1 as Workbook
    Dim  wb2 as Workbook
    Set wb1 = Workbooks("SomeAlreadyOpenBook.xlsx")
    Set wb2 = Workbooks.Open("C:\Path\To\SomeClosedBook.xlsx")
    v = wb2.Sheets("SomeSheet").Range("A1").Value
    wb1.Sheets("SomeOtherSheet").Range("A1").Value = v
    wb2.Close()
End Sub

Передайте диапазоны своим Subs и Functions как переменные Range

Sub ClearRange(r as Range)
    r.ClearContents
    '....
End Sub

Sub MyMacro()
    Dim rng as Range
    Set rng = ThisWorkbook.Worksheets("SomeSheet").Range("A1:B10")
    ClearRange rng
End Sub

Вы также должны применять методы (такие как Findи Copy) к переменным

Dim rng1 As Range
Dim rng2 As Range
Set rng1 = ThisWorkbook.Worksheets("SomeSheet").Range("A1:A10")
Set rng2 = ThisWorkbook.Worksheets("SomeSheet").Range("B1:B10")
rng1.Copy rng2

Если вы циклически изменяете диапазон ячеек, часто лучше (быстрее) сначала скопировать значения диапазона в вариантный массив и выполнить цикл по нему.

Dim dat As Variant
Dim rng As Range
Dim i As Long

Set rng = ThisWorkbook.Worksheets("SomeSheet").Range("A1:A10000")
dat = rng.Value  ' dat is now array (1 to 10000, 1 to 1)
for i = LBound(dat, 1) to UBound(dat, 1)
    dat(i,1) = dat(i,1) * 10 'or whatever operation you need to perform
next
rng.Value = dat ' put new values back on sheet

Это небольшой дегустатор для того, что возможно.

Крис Нейлсен
источник
7
добавив к этому блестящему ответу, что для работы с диапазоном вам не нужно знать его фактический размер, если вы знаете верхний левый rng1(12, 12)угол ... например, будет работать, даже если для rng1 было установлено [A1:A10]только.
MikeD
3
@chrisneilsen Крис, я полагаю, что вы также можете использовать префикс рабочего листа перед краткой записью ссылки на ячейку, чтобы избавить вас от необходимости печатать Rangeтак: ActiveSheet.[a1:a4]или ws.[b6].
Логан Рид
3
@AndrewWillems ... или 48 раз в этом посте, но кто считает. But ... но если серьезно, это легко забыть при работе с переменными, содержащими объекты. variantПеременная не требует , Set пока вы не назначите объект к нему. Например, Dim x: x = 1все в порядке, но Dim x: x = Sheets("Sheet1")сгенерирует ошибку 438. Однако, просто перепутать / уточнить Dim x: x = Range("A1"), не создаст ошибку. Почему? ... потому что ему присвоено значение объекта переменной, а не ссылка на сам объект (так как он эквивалентен Dim x: x = Range("A1").Value)
ashleedawg
1
@ user3932000 Мне неизвестен сценарий, в котором имена листов меняются автоматически. Что касается имен файлов, он будет делать это только в том случае, если в папке уже есть файл с таким именем. Просто используйте Сохранить ... или жестко закодируйте имя файла, чтобы сохранить его в виде строки. Если вы не можете решить эту проблему, вам следует задать отдельный вопрос, а не комментировать.
TylerH
1
@ user3932000, который сделал бы интересным Q. Я уверен, что есть способы справиться с этим .. Вы были на ТАК достаточно долго, чтобы знать, что перехватить поток комментариев на старом Q - не самый
лучший
212

Две основные причины, по которым .Select/ .Activate/ Selection/ Activecell/ Activesheet/ и Activeworkbookт. Д ... следует избегать

  1. Это замедляет ваш код.
  2. Обычно это основная причина ошибок во время выполнения.

Как мы этого избежать?

1) Непосредственно работать с соответствующими объектами

Рассмотрим этот код

Sheets("Sheet1").Activate
Range("A1").Select
Selection.Value = "Blah"
Selection.NumberFormat = "@"

Этот код также можно записать как

With Sheets("Sheet1").Range("A1")
    .Value = "Blah"
    .NumberFormat = "@"
End With

2) При необходимости объявите свои переменные. Тот же код выше может быть записан как

Dim ws as worksheet

Set ws = Sheets("Sheet1")

With ws.Range("A1")
    .Value = "Blah"
    .NumberFormat = "@"
End With
Сиддхарт Рут
источник
17
Это хороший ответ, но я упускаю эту тему, когда нам действительно нужно активировать. Все говорят, что это плохо, но никто не объясняет случаи, когда есть смысл его использовать. Например, я работал с двумя рабочими книгами и не мог запустить макрос на одной из рабочих книг, не активировав ее сначала. Не могли бы вы уточнить немного, может быть? Кроме того, если, например, я не активирую листы при копировании диапазона с одного листа на другой, то при выполнении программы он, по-видимому, активирует соответствующие листы в любом случае, неявно.
user3032689
1
Я считаю, что вам иногда может понадобиться сначала активировать лист, если вам нужно вставить или отфильтровать данные на нем. Я бы сказал, что лучше избегать активации, насколько это возможно, но есть случаи, когда вам нужно это сделать. Так что продолжайте активировать и выбирать до абсолютного минимума согласно ответу выше.
Ник
7
Я думаю, что дело не в том, чтобы полностью избегать их использования, а в максимально возможной степени. если вы хотите сохранить рабочую книгу, чтобы, когда кто-то открыл ее, была выбрана определенная ячейка на определенном листе, то вы должны выбрать этот лист и ячейку. Копирование / вставка - плохой пример, по крайней мере, в случае значений, это может быть сделано быстрее с помощью кода, такого какSheets(2).[C10:D12].Value = Sheets(1).[A1:B3].Value
robotik
1
@ Ник Вам не нужно активировать листы, чтобы вставить их или отфильтровать. Используйте объект листа в ваших командах вставки или фильтрации. Это становится легче, когда вы изучаете объектную модель Excel на практике. Я полагаю, что единственный раз, когда я использую .Activate, это когда я создаю новый лист, но я хочу, чтобы исходный лист появлялся, когда код готов.
18:30
3
@phrebh Вам не нужно использовать .Activateдля перехода к исходному листу, просто используйтеApplication.Goto
GMalc
88

Я добавлю один небольшой акцент ко всем превосходным ответам, данным выше:

Вероятно, самое большое, что вы можете сделать, чтобы избежать использования Select, - это максимально использовать именованные диапазоны (в сочетании со значимыми именами переменных) в вашем коде VBA . Этот момент был упомянут выше, но немного затенен; Однако это заслуживает особого внимания.

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

Именованные диапазоны облегчают чтение и понимание вашего кода.

Пример:

Dim Months As Range
Dim MonthlySales As Range

Set Months = Range("Months")
'e.g, "Months" might be a named range referring to A1:A12

Set MonthlySales = Range("MonthlySales")
'e.g, "Monthly Sales" might be a named range referring to B1:B12

Dim Month As Range
For Each Month in Months
    Debug.Print MonthlySales(Month.Row)
Next Month

Совершенно очевидно, что именованные диапазоны Monthsи MonthlySalesсодержат, и что делает процедура.

Почему это важно? Частично потому, что другим людям легче понять это, но даже если вы единственный человек, который когда-либо увидит или использует ваш код, вы все равно должны использовать именованные диапазоны и хорошие имена переменных, потому что ВЫ ЗАБУДЕТЕ то, что вы хотели с ним сделать год спустя, и вы потратите 30 минут на то, чтобы понять, что делает ваш код.

Именованные диапазоны гарантируют, что ваши макросы не сломаются, когда (не если!) Изменяется конфигурация электронной таблицы.

Рассмотрим, был ли приведенный выше пример написан так:

Dim rng1 As Range
Dim rng2 As Range

Set rng1 = Range("A1:A12")
Set rng2 = Range("B1:B12")

Dim rng3 As Range
For Each rng3 in rng1 
    Debug.Print rng2(rng3.Row)
Next rng3

Сначала этот код будет работать нормально - пока вы или будущий пользователь не примете решение: «Ну и дела, я думаю, я собираюсь добавить новый столбец с Aуказанием года в столбце !» Или поместить столбец расходов между месяцами и столбцы продаж, или добавьте заголовок к каждому столбцу. Теперь ваш код не работает. А поскольку вы использовали ужасные имена переменных, вам потребуется гораздо больше времени, чтобы понять, как это исправить, чем нужно.

Если вы использовали именованные диапазоны , чтобы начать с того , что Monthsи Salesстолбцы могут быть перемещены все , что вам нравится, и ваш код будет продолжать работать нормально.

Рик поддерживает Монику
источник
6
Дискуссия о том, хороши ли названные диапазоны или нет, дизайн электронных таблиц продолжается - я твердо в лагере. По моему опыту, они увеличивают количество ошибок (для обычных пользователей, которым не нужен код).
brettdj
12
Я согласен с вашей философией развития; Однако, я думаю, что это чепуха. В нем рассказывается о том, как имена диапазонов могут сбить с толку новичков, отлаживающих электронные таблицы, но любой, кто использует новичков для просмотра сложных электронных таблиц, получает то, что заслуживает! Раньше я работал на фирму, которая проверяла финансовые таблицы, и я могу вам сказать, что это не та работа, которую вы даете новичку.
DeanOC
8
Там нет значимой дискуссии. Любой, кто выступает против определенных имен, не нашел время, чтобы полностью понять их последствия. Именованные формулы могут быть единственной наиболее глубокой и полезной конструкцией во всем Excel.
Excel Hero
10
@brettdj: Ваша цитата верна, но вы забыли упомянуть, что за ней следуют шесть фраз "За исключением ...". Одним из них является: « За исключением замены ссылок на ячейки в макрокодировании Всегда используйте имена Excel в качестве замены ссылок на ячейки при построении макросов. Это делается для того, чтобы избежать ошибок, возникающих при вставке дополнительных строк или столбцов, из-за чего макросирование больше не выполняется указывает на предполагаемые исходные данные. "
Маркус Мангельсдорф
47

Я собираюсь дать короткий ответ, так как все остальные дали длинный.

Вы будете получать .select и .activate всякий раз, когда будете записывать макросы и использовать их повторно. Когда вы выбираете ячейку или лист, она просто становится активной. С этого момента всякий раз, когда вы используете неквалифицированные ссылки, как будто Range.Valueони просто используют активную ячейку и лист. Это также может быть проблематично, если вы не смотрите, где находится ваш код, или пользователь нажимает на книгу.

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

'create and set a range
Dim Rng As Excel.Range
Set Rng = Workbooks("Book1").Worksheets("Sheet1").Range("A1")
'OR
Set Rng = Workbooks(1).Worksheets(1).Cells(1, 1)

Или ты мог

'Just deal with the cell directly rather than creating a range
'I want to put the string "Hello" in Range A1 of sheet 1
Workbooks("Book1").Worksheets("Sheet1").Range("A1").value = "Hello"
'OR
Workbooks(1).Worksheets(1).Cells(1, 1).value = "Hello"

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

MattB
источник
33

«... и я обнаружил, что мой код стал бы более пригодным для повторного использования, если бы я мог использовать переменные вместо функций выбора».

Хотя я не могу думать ни о чем большем, чем об отдельной ситуации, когда .Selectвыбор будет лучше, чем прямая ссылка на ячейку, я бы встал на защиту Selectionи указал, что его не следует выбрасывать по тем же причинам, которых .Selectследует избегать.

Бывают случаи, когда короткие, подпрограммы макроса, позволяющие экономить время и назначаемые сочетаниям горячих клавиш, которые доступны при нажатии пары клавиш, экономят много времени. Возможность выбора группы ячеек для введения в действие операционного кода творит чудеса, когда имеешь дело с карманными данными, которые не соответствуют формату данных всей таблицы. Во многом так же, как вы можете выбрать группу ячеек и применить изменение формата, выбор группы ячеек для запуска специального макрокодов может быть значительным сбережением времени.

Примеры подструктуры на основе выбора:

Public Sub Run_on_Selected()
    Dim rng As Range, rSEL As Range
    Set rSEL = Selection    'store the current selection in case it changes
    For Each rng In rSEL
        Debug.Print rng.Address(0, 0)
        'cell-by-cell operational code here
    Next rng
    Set rSEL = Nothing
End Sub

Public Sub Run_on_Selected_Visible()
    'this is better for selected ranges on filtered data or containing hidden rows/columns
    Dim rng As Range, rSEL As Range
    Set rSEL = Selection    'store the current selection in case it changes
    For Each rng In rSEL.SpecialCells(xlCellTypeVisible)
        Debug.Print rng.Address(0, 0)
        'cell-by-cell operational code here
    Next rng
    Set rSEL = Nothing
End Sub

Public Sub Run_on_Discontiguous_Area()
    'this is better for selected ranges of discontiguous areas
    Dim ara As Range, rng As Range, rSEL As Range
    Set rSEL = Selection    'store the current selection in case it changes
    For Each ara In rSEL.Areas
        Debug.Print ara.Address(0, 0)
        'cell group operational code here
        For Each rng In ara.Areas
            Debug.Print rng.Address(0, 0)
            'cell-by-cell operational code here
        Next rng
    Next ara
    Set rSEL = Nothing
End Sub

Фактический код для обработки может быть чем угодно, от одной строки до нескольких модулей. Я использовал этот метод для запуска долго выполняющихся подпрограмм на рваной выборке ячеек, содержащих имена внешних рабочих книг.

Короче говоря, не выбрасывайте Selectionиз-за его тесной связи с .Selectи ActiveCell. В качестве свойства листа он имеет много других целей.

(Да, я знаю, что этот вопрос был о .Select, Selectionно я хотел удалить любые заблуждения, которые могут предположить начинающие программисты VBA.)


источник
13
Selectionв рабочей таблице может быть что угодно, поэтому можно также сначала проверить тип объекта, прежде чем назначить его переменной, поскольку вы явно объявили его как Range.
L42
29

Обратите внимание, что ниже я сравниваю подход Select (тот, который ОП хочет избежать) с подходом Range (и это ответ на вопрос). Так что не прекращайте читать, когда увидите первый выбор.

Это действительно зависит от того, что вы пытаетесь сделать. В любом случае простой пример может быть полезен. Предположим, вы хотите установить значение активной ячейки в «foo». Используя ActiveCell, вы могли бы написать что-то вроде этого:

Sub Macro1()
    ActiveCell.Value = "foo"
End Sub

Если вы хотите использовать его для ячейки, которая не является активной, например, для «B2», вы должны сначала выбрать ее, например, так:

Sub Macro2()
    Range("B2").Select
    Macro1
End Sub

Используя диапазоны, вы можете написать более общий макрос, который можно использовать для установки значения любой ячейки, которую вы хотите, в любое, что вы хотите:

Sub SetValue(cellAddress As String, aVal As Variant)
    Range(cellAddress).Value = aVal
End Sub

Затем вы можете переписать Macro2 как:

Sub Macro2()
    SetCellValue "B2", "foo"
End Sub

И Макро1 как:

Sub Macro1()
    SetValue ActiveCell.Address, "foo"
End Sub

Надеюсь, это поможет немного прояснить ситуацию.

Франческо Баручелли
источник
1
Спасибо за отличный ответ так быстро. Значит ли это, что если я обычно добавляю ячейки к диапазону, называю диапазон и перебираю его, я должен сразу перейти к созданию массива?
BiGXERO
Я не уверен, что понимаю, что вы имеете в виду, но вы можете создать Range с помощью одной инструкции (например, Range ("B5: C14")), и вы даже можете установить его значение сразу (если оно должно быть одинаковым для каждая ячейка в диапазоне), например Range ("B5: C14"). Значение = "abc"
Франческо Баручелли
29

Избегание Selectи Activateэто шаг, который делает вас немного лучше VBA разработчика. В общем, Selectи Activateиспользуются , когда макрос записывается, таким образом, Parentрабочий лист или диапазон всегда считается активным.

Вот как вы можете избежать Selectи Activateв следующих случаях:


Добавление нового рабочего листа и копирование в него ячейки:

From (код, сгенерированный с помощью устройства записи макросов):

Sub Makro2()
    Range("B2").Select
    Sheets.Add After:=ActiveSheet
    Sheets("Tabelle1").Select
    Sheets("Tabelle1").Name = "NewName"
    ActiveCell.FormulaR1C1 = "12"
    Range("B2").Select
    Selection.Copy
    Range("B3").Select
    ActiveSheet.Paste
    Application.CutCopyMode = False
End Sub

Для того, чтобы:

Sub TestMe()
    Dim ws As Worksheet
    Set ws = Worksheets.Add
    With ws
        .Name = "NewName"
        .Range("B2") = 12
        .Range("B2").Copy Destination:=.Range("B3")
    End With
End Sub

Если вы хотите скопировать диапазон между листами:

От:

Sheets("Source").Select
Columns("A:D").Select
Selection.Copy
Sheets("Target").Select
Columns("A:D").Select
ActiveSheet.Paste

Для того, чтобы:

Worksheets("Source").Columns("A:D").Copy Destination:=Worksheets("Target").Range("a1")

Использование модных именованных диапазонов

Вы можете получить к ним доступ [], что действительно прекрасно, по сравнению с другими способами. Проверь себя:

Dim Months As Range
Dim MonthlySales As Range

Set Months = Range("Months")    
Set MonthlySales = Range("MonthlySales")

Set Months =[Months]
Set MonthlySales = [MonthlySales]

Пример сверху будет выглядеть так:

Worksheets("Source").Columns("A:D").Copy Destination:=Worksheets("Target").[A1]

Не копировать значения, но принимать их

Обычно, если вы готовы select, скорее всего, вы что-то копируете. Если вас интересуют только значения, это хороший вариант, чтобы избежать выбора:

Range("B1:B6").Value = Range("A1:A6").Value


Старайтесь всегда ссылаться на рабочий лист

Это, наверное, самая распространенная ошибка в , Всякий раз, когда вы копируете диапазоны, иногда на лист не ссылаются, и поэтому VBA считает неправильный лист ActiveWorksheet.

'This will work only if the 2. Worksheet is selected!
Public Sub TestMe()
    Dim rng As Range
    Set rng = Worksheets(2).Range(Cells(1, 1), Cells(2, 2)).Copy
End Sub

'This works always!
Public Sub TestMe2()
    Dim rng As Range
    With Worksheets(2)
        .Range(.Cells(1, 1), .Cells(2, 2)).Copy
    End With
End Sub

Могу ли я действительно никогда не использовать .Selectили .Activateдля чего-либо?

  • Хороший пример того, когда вас могут оправдать использование, .Activateи .Selectкогда вы хотите убедиться, что конкретный Рабочий лист выбран по визуальным причинам. Например, ваш Excel всегда будет открываться с выбранным первым листом обложки, независимо от того, какой лист ActiveSheet был закрыт при закрытии файла.

Таким образом, что-то вроде приведенного ниже кода абсолютно нормально:

Private Sub Workbook_Open()
    Worksheets("Cover").Activate
End Sub
Vityata
источник
Вы можете использовать Application.Goto вместо Worksheets.Activate. Несколько менее рискованно.
Джефф Грисвальд
1
Поздний ответ К вашему сведению - хороший и несколько неожиданный пример необходимого, .Selectа также моей работы - можно найти в разделе Как написать одинаковую информацию для всех листов - @Vityata :)
ТМ
1
@TM - действительно, это интересный пример, и, вероятно, экономит несколько миллисекунд для 100+ рабочих листов, но я бы, вероятно, отговорил бы его, если я его где-нибудь увижу. Во всяком случае, там отбор не написан явно, а является результатом .FillAcrossSheets, так что это где-то посередине (по крайней мере, в моем представлении о таксономии VBA)
Витята
17

Всегда указывайте рабочую книгу, рабочий лист и ячейку / диапазон.

Например:

Thisworkbook.Worksheets("fred").cells(1,1)
Workbooks("bob").Worksheets("fred").cells(1,1)

Поскольку конечные пользователи всегда будут просто нажимать кнопки, и как только фокус переместится с рабочей книги, код захочет работать с ним, тогда все пойдет не так.

И никогда не используйте индекс рабочей книги.

Workbooks(1).Worksheets("fred").cells(1,1)

Вы не знаете, какие другие рабочие книги будут открыты, когда пользователь выполнит ваш код.

user1644564
источник
7
Имена рабочих листов тоже могут меняться, вы знаете. Вместо этого используйте кодовые имена.
Рик поддерживает Монику
Имена рабочих листов могут меняться, конечно. Но я не согласен с тем, что вы должны слишком усложнять свой код, чтобы попытаться смягчить это. Если пользователь меняет имя листа, и его макрос перестает работать, это на них. Я обычно просто предполагаю, что имена рабочих листов будут одинаковыми. Для особо важных макросов я запускаю небольшую предполетную проверку перед запуском в сам макрос, который просто проверяет, что все листы, которые он ожидает найти, действительно есть, и, если они отсутствуют, он уведомляет пользователя, какой из них.
Джефф Грисвальд
10

Эти методы довольно стигматизированы, поэтому взяли на себя инициативу @Vityata и @Jeeped ради рисования линии на песке:

Почему бы не назвать .Activate, .Select, Selection, ActiveSomethingметоды / свойства

В основном потому, что они вызваны главным образом для обработки пользовательского ввода через пользовательский интерфейс приложения. Так как они являются методами, вызываемыми, когда пользователь обрабатывает объекты через пользовательский интерфейс, они записываются макрос-рекордером, и поэтому их вызов является либо хрупким, либо избыточным для большинства ситуаций: вам не нужно выбирать объект, чтобы выполнить действие Selectionсразу после этого.

Однако это определение разрешает ситуации, к которым они обращаются:

Когда звонить .Activate, .Select, .Selection, .ActiveSomethingметоды / свойства

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

Если вы разрабатываете и ожидаете, что пользователь выберет экземпляры объектов для вашего кода для обработки, тогда .Selectionили .ActiveObjectуместно.

С другой стороны, .Selectи .Activateявляются полезными , когда вы можете вывести следующее действие пользователя , и вы хотите , чтобы ваш код в руководстве пользователя, возможно , спасая ему какое - то время и щелчков мыши. Например, если ваш код только что создал новый экземпляр диаграммы или обновил его, пользователь может захотеть проверить его, и вы можете вызвать .Activateего или его лист, чтобы сэкономить пользователю время на его поиск; или если вы знаете, что пользователю потребуется обновить некоторые значения диапазона, вы можете программно выбрать этот диапазон.

LFB
источник
6

ИМХО использование .selectпроисходит от людей, которые, как и я, начали изучать VBA по необходимости, записывая макросы, а затем модифицируя код, не осознавая, что .selectи последующее selection- просто ненужные посредники.

.select можно избежать, как многие уже опубликовали, напрямую работая с уже существующими объектами, что позволяет различные косвенные ссылки, такие как сложные вычисления i и j, а затем редактирование ячейки (i, j) и т. д.

В противном случае, в этом нет ничего явно неправильного с .selectсамим собой, и вы можете легко найти применение для этого, например, у меня есть электронная таблица, которую я заполняю датой, активирую макрос, который делает с ним что-то волшебное, и экспортирую его в приемлемом формате на отдельный лист, который однако, требуются некоторые окончательные ручные (непредсказуемые) входы в соседнюю ячейку. И вот наступает момент, .selectкоторый спасает меня от дополнительных движений мыши и щелчка.

Eleshar
источник
2
Хотя вы правы, в select есть по крайней мере одна вещь, явно не правая: она медленная. Действительно очень медленный по сравнению со всем, что происходит в макросе.
Vacip
4

Быстрый ответ:

Чтобы избежать использования .Selectметода, вы можете установить переменную, равную требуемому свойству.

► Например, если вы хотите, чтобы значение в Cell A1вас можно было установить переменную, равную значению свойства этой ячейки.

  • пример valOne = Range("A1").Value

► Например, если вы хотите использовать кодовое имя «Sheet3», вы можете установить переменную, равную свойству кодового имени этого листа.

  • пример valTwo = Sheets("Sheet3").Codename

Надеюсь, это поможет. Дайте знать, если у вас появятся вопросы.

FinPro.Online
источник
3

Я заметил, что ни один из этих ответов не упоминает свойство .Offset . Это также можно использовать, чтобы избежать использования Selectдействия при манипулировании определенными ячейками, особенно в отношении выбранной ячейки (как упоминается в OP ActiveCell).

Вот пара примеров.

Я также предполагаю, что ActiveCell - это J4 .

ActiveCell.Offset(2, 0).Value = 12

  • Это изменит ячейку J6на значение 12
  • Минус -2 будет ссылаться на J2

ActiveCell.Offset(0,1).Copy ActiveCell.Offset(,2)

  • Это будет копировать ячейку k4на L4.
  • Обратите внимание, что «0» не требуется в параметре смещения, если не требуется (, 2)
  • Как и в предыдущем примере, минус 1 будет i4

ActiveCell.Offset(, -1).EntireColumn.ClearContents

  • Это очистит значения во всех ячейках в столбце k.

Это не значит, что они «лучше», чем описанные выше варианты, а просто перечисление альтернатив.

PGSystemTester
источник
0

Работа с .Parent функцией. В этом примере показано, как установка только одной ссылки myRng обеспечивает динамический доступ ко всей среде без .Select, .Activate, .Activecell, .ActiveWorkbook, .ActiveSheet и т. Д. (Там нет общего. Детский особенность)

Sub ShowParents()
    Dim myRng As Range
    Set myRng = ActiveCell
    Debug.Print myRng.Address                    ' an address of the selected cell
    Debug.Print myRng.Parent.name                ' the name of sheet, where MyRng is in
    Debug.Print myRng.Parent.Parent.name         ' the name of workbook, where MyRng is in
    Debug.Print myRng.Parent.Parent.Parent.name  ' the name of application, where MyRng is in

    ' You may use this feature to set reference to these objects
    Dim mySh    As Worksheet
    Dim myWbk   As Workbook
    Dim myApp   As Application

    Set mySh = myRng.Parent
    Set myWbk = myRng.Parent.Parent
    Set myApp = myRng.Parent.Parent.Parent
    Debug.Print mySh.name, mySh.Cells(10, 1).Value
    Debug.Print myWbk.name, myWbk.Sheets.Count
    Debug.Print myApp.name, myApp.Workbooks.Count

    ' You may use dynamically addressing
    With myRng
        .Copy

       ' pastes in D1 on sheet 2 in the same workbook, where copied cell is
        .Parent.Parent.Sheets(2).Range("D1").PasteSpecial xlValues
    ' or myWbk.Sheets(2).Range("D1").PasteSpecial xlValues

       ' we may dynamically call active application too
        .Parent.Parent.Parent.CutCopyMode = False
    ' or myApp.CutCopyMode = False
    End With
End Sub
barneyos
источник
Очень приятно, но не уверен, что это имеет отношение к вопросу ОП. Вам совсем не нужен «Родитель», чтобы работать в VBA без использования Select или ActiveSheet
Джефф Грисвальд,
0

Основная причина никогда не использовать Select или Activesheet состоит в том, что у большинства людей при запуске макроса будет открыта как минимум еще пара рабочих книг (иногда десятков), и если они щелкают по листу во время работы макроса и нажимают на другие Книга, которую они открыли, затем «Activesheet» изменяется, и целевая рабочая книга для неквалифицированной команды «Select» также изменяется.

В лучшем случае ваш макрос потерпит крах, в худшем случае вы можете в итоге записать значения или изменить ячейки в неправильной книге, не имея возможности отменить их.

У меня есть простое золотое правило, которому я следую: добавьте переменные с именами "wb" и "ws" для объекта Workbook и объекта Worksheet и всегда используйте их для ссылки на мою книгу макросов. Если мне нужно сослаться на более чем одну книгу или более чем на один лист, я добавляю больше переменных.

например

Dim wb as Workbook
Dim ws as Worksheet
Set wb = ThisWorkBook
Set ws = wb.sheets("Output")

Команда «Set wb = ThisWorkbook» является абсолютно ключевой. «ThisWorkbook» - это специальное значение в Excel, и оно означает рабочую книгу, из которой в данный момент выполняется код VBA . Очень полезный ярлык для установки переменной Workbook с помощью.

После того, как вы сделали это в верхней части вашего Sub, их использование не может быть проще, просто используйте их везде, где вы бы использовали «Selection»:

Таким образом, чтобы изменить значение ячейки «A1» в «Вывод» на «Hello» вместо:

Sheets("Output").Activate
ActiveSheet.Range("A1").Select
Selection.Value = "Hello"

Теперь мы можем сделать это:

ws.Range("A1").Value = "Hello"

Который не только намного более надежен и менее вероятен сбой, если пользователь работает с несколькими электронными таблицами, он также намного короче, быстрее и проще для написания.

В качестве дополнительного бонуса, если вы всегда называете свои переменные «wb» и «ws», вы можете копировать и вставлять код из одной книги в другую, и он обычно будет работать с минимальными необходимыми изменениями, если таковые имеются.

Джефф Грисвальд
источник
1
Не мое отрицательное мнение, но я не уверен, что это добавляет что-то новое к тому, что уже было предложено в существующих ответах.
BigBen
Да, мой ответ немного избыточен, но другие ответы слишком длинные, содержат слишком много лишних вещей, и никто не упомянул об использовании ThisWorkbook для предварительной установки переменной рабочей таблицы. Это то, что, если бы кто-то показал мне, когда я впервые окунулся в VBA, я бы нашел невероятно полезным. Другие упоминали об использовании переменной Worksheet, но не очень хорошо объясняют, почему, и не предлагают пример кода с использованием и без использования переменных таблицы и рабочей книги.
Джефф Грисвальд
Но принятый ответ определенно обсуждает ThisWorkbook... Я не уверен, что ваш комментарий точен.
BigBen
Да, вы не ошибаетесь. Но не в контексте его использования для установки переменной рабочей книги и использования этой переменной рабочей книги в будущем или использования этой переменной рабочей книги для установки переменной рабочей таблицы, как я предлагаю. Мой ответ короче, проще и доступнее для начинающих, чем принятый ответ.
Джефф Грисвальд
-3

Это пример, который очистит содержимое ячейки «A1» (или больше, если тип выбора - xllastcell и т. Д.). Все сделано без выбора ячеек.

Application.GoTo Reference:=Workbook(WorkbookName).Worksheets(WorksheetName).Range("A1")
Range(Selection,selection(selectiontype)).clearcontents 

Я надеюсь, что это помогает кому-то.

marionffavp
источник
1
Нет простите. Это не то, что вы сделали там вообще. Что вы на самом деле сделали, так это выбрали ячейку «A1» с помощью команды «Application.GoTo», которая ничем не отличается от использования «Select», затем использовали clearcontents в вашем выделении. способ сделать это без выбора ячеек будет Workbook(WorkbookName).Worksheets(WorksheetName).Range("A1").ClearContentsодной строкой, а не двумя, и на самом деле работает без выбора ячеек.
Джефф Грисвальд