Як уникнути використання Select у Excel VBA


537

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

Я знайшов цю статтю про діапазони, і цей приклад про переваги не використовувати select, але нічого не можна знайти як ?


14
Важливо зазначити, що існують випадки, коли використання Selectта / або ActiveSheetт. Д. І зовсім неминуче. Ось приклад, який я знайшов: stackoverflow.com/questions/22796286/…
Рік підтримує Моніку

9
І бувають випадки - редагування даних діаграми в ppt, причому один основний файл excel - один, де потрібно активувати або вибрати.
brettdj

@brettdj - ось останній приклад . Для встановлення всіх аркушів у робочій книжці однакове значення, здається .Select / .Selection, потрібно.
BruceWayne

Відповіді:


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, краще також розмітити 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 як змінні діапазону

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 Chris, я вважаю, що ви також можете використовувати префікс робочого аркуша перед скороченим посиланням на клітинку, щоб врятувати вас від Rangeтакого типу: ActiveSheet.[a1:a4]або ws.[b6].
Логан Рід

3
@AndrewWillems ... або 48 разів у цій публікації, але хто рахує. ... але серйозно, це легко забути під час роботи зі змінними, що містять об'єкти. 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 Мені невідомий сценарій, коли назви аркушів змінюються автоматично. Що стосується імен файлів, то це зробить це лише в тому випадку, якщо в папці вже є файл цього імені. Просто скористайтеся Save ... або жорстким кодом ім'я файлу, щоб зберегти його як рядок. Якщо ви не можете вирішити цю проблему, вам слід задати окреме запитання щодо неї, а не коментувати.
TylerH

1
@ user3932000, що зробить цікавим Q. Я впевнений, що є способи впоратися з цим. Ви були настільки довгими, щоб знати про викрадення нитки коментарів на старий Q - це не шлях
chris neilsen

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
Це гарна відповідь, але те, що мені не вистачає в цій темі, це коли нам насправді потрібна активація. Усі кажуть, що це погано, але ніхто не пояснює жодних випадків, коли має сенс його використовувати. Наприклад, я працював з 2 робочими книгами і не зміг запустити макрос на одній із робочих книжок, не активувавши її спочатку. Не могли б ви трохи допрацювати? Крім того, якщо, наприклад, я не активую аркуші при копіюванні діапазону з одного аркуша на інший, коли я виконую програму, схоже, активувати відповідні аркуші все одно, неявно.
користувач3032689

1
Я вважаю, що іноді вам може знадобитися активувати аркуш спочатку, якщо вам потрібно вставити або відфільтрувати дані на ньому. Я б сказав, що найкраще уникати активації, наскільки це можливо, але є випадки, коли це потрібно зробити. Тому продовжуйте активувати та вибирати до абсолютного мінімуму відповідно до відповіді вище.
Нік

7
Я думаю, сенс не в тому, щоб повністю уникати їх використання, а так само, наскільки це можливо. якщо ви хочете зберегти робочу книжку, щоб коли хтось відкрив її певну клітинку на певному аркуші, ви повинні вибрати цей аркуш і комірку. Скопіювати / вставити - поганий приклад, принаймні у випадку значень, це можна зробити швидше за допомогою такого коду, якSheets(2).[C10:D12].Value = Sheets(1).[A1:B3].Value
robotik

1
@Nick Вам не потрібно активувати аркуші, щоб вставити їх або відфільтрувати. Використовуйте об’єкт аркуша в командах вставки або фільтра. Це стає простіше, коли ви вивчаєте об'єктну модель Excel на практиці. Я вважаю, що єдиний раз, коли я використовую. Активувати, коли я створюю новий аркуш, але я хочу, щоб оригінальний аркуш з’явився, коли буде зроблено код.
phrebh

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
Дебати про те, чи є названі діапазони хорошими чи поганими дизайном електронних таблиць, тривають - я твердо перебуваю у таборі no. На мій досвід вони збільшують помилки (для стандартних користувачів, які не потребують коду).
brettdj


12
Я згоден з вашою філософією розвитку; однак я вважаю, що папір - це нісенітниця. У ньому йдеться про те, як імена діапазону можуть збити з пантелику новачків, які налагоджують електронні таблиці, але кожен, хто використовує новачків для перегляду складних електронних таблиць, отримує те, що заслуговує! Раніше я працював у фірмі, яка переглядала фінансові таблиці, і можу вам сказати, що це не та робота, яку ви даєте новачку.
DeanOC

8
Суттєвих дебатів немає. Кожен, хто сперечається з певними іменами, не знайшов часу, щоб повністю зрозуміти їх наслідки. Названі формули можуть бути єдиною найглибшою та найкориснішою конструкцією у всіх Excel.
Герой Excel

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"

Існують різні комбінації цих методів, але це було б загальне уявлення, висловлене якомога коротше для нетерплячих людей, як я.


33

"... і я знаходжу, що мій код буде більш корисним для використання, якби я міг використовувати змінні замість функцій Select."

Хоча я не можу придумати будь-яку іншу ситуацію, коли .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 (і це відповідь на питання). Тому не переставайте читати, коли побачите перший Select.

Це дійсно залежить від того, що ви намагаєтеся зробити. У будь-якому випадку простий приклад може бути корисним. Припустимо, що ви хочете встановити значення активної комірки на "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

І Macro1 як:

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

Сподіваюсь, це допомагає трохи прояснити речі.


1
Дякую за відмінну відповідь так швидко. Так це означає, що якщо я зазвичай додаю комірки в діапазон, називаю діапазон і повторюю його, я повинен перейти прямо до створення масиву?
BiGXERO

Я не впевнений, що я розумію, що ви маєте на увазі, але ви можете створити діапазон за допомогою однієї інструкції (наприклад, діапазон ("B5: C14")), і ви навіть можете відразу встановити його значення (якщо воно має бути однаковим для кожна клітинка в діапазоні), наприклад, діапазон ("B5: C14"). Значення = "abc"
Франческо Баручеллі,

29

Як уникнути Selectі Activateце крок , який робить вас трохи краще VBA розробника. Загалом, Selectі Activateвикористовуються , коли макрос записується, таким чином, Parentробочий лист або діапазон завжди вважається активним.

Ось як можна уникнути Selectі Activateв наступних випадках:


Додавання нового робочого листа та копіювання комірки на ньому:

З (код, згенерований за допомогою макрореєстратора):

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

Ви можете використовувати Application.Goto замість Worksheets.Activate. Дещо менш ризиковані.
Джефф Грісвальд

1
Пізня відповідь FYI - приємний і дещо несподіваний приклад для потрібного .Select- як і моєї роботи навколо - можна знайти на сайті Як написати однакову інформацію на всі аркуші - @Vityata :)
TM

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)

Ви не знаєте, які ще робочі книжки будуть відкриті, коли користувач запустить ваш код.


7
Назви робочих аркушів теж можуть змінюватися, ви знаєте. Замість цього використовуйте кодові назви.
Рік підтримує Моніку

Назви робочих листів, безумовно, можуть змінюватися. Але я не погоджуюся, що вам слід надмірно ускладнювати свій код, щоб спробувати це пом'якшити. Якщо користувач змінює назву аркуша, і їх макрос перестає працювати, це на них. Я взагалі просто припускаю, що назви робочих аркушів будуть однаковими. Що стосується особливо критичних макросів, я запускаю невелику перевірку перед польотом перед тим, як запустити в макрос, який просто перевіряє, чи впевнені, що всі аркуші, які він очікує, дійсно є, і якщо такі відсутні, він сповіщає користувача, який саме.
Джефф Грісвальд

10

Ці методи досить стигматизовані, тому переймаючи лідируючі позиції @Vityata та @Jeeped заради нанесення лінії на піску:

Чому б не назвати .Activate, .Select, Selection, ActiveSomethingметоди / властивості

В основному тому, що вони покликані в основному обробляти введення користувачів через інтерфейс програми. Оскільки вони називаються методами, коли користувач обробляє об'єкти через користувальницький інтерфейс, вони є тими, які записані макрореєстратором, і саме тому виклик їх є крихким або зайвим у більшості ситуацій: вам не потрібно вибирати об'єкта, щоб виконати дію Selectionвідразу після цього.

Однак це визначення врегулює ситуації, щодо яких вони закликаються:

Коли дзвонити .Activate, .Select, .Selection, .ActiveSomethingметоди / властивості

В основному, коли ви очікуєте, що кінцевий користувач відіграє певну роль у виконанні.

Якщо ви розробляєте і розраховуєте, що користувач вибере екземпляри об'єкта для обробки вашого коду, то .Selectionабо .ActiveObjectє відповідним.

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


6

Використання IMHO .selectпоходить від людей, які, як я, почали вивчати VBA за необхідності, записуючи макроси, а потім змінюючи код, не усвідомлюючи, що .selectі подальше selection- це лише непотрібна середня людина.

.select можна уникнути, як уже багато опублікованих, безпосередньо, працюючи з уже існуючими об'єктами, що дозволяє різні непрямі посилання, такі як обчислення i та j складно, а потім редагування комірки (i, j) тощо.

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


2
Хоча ти маєш рацію, є щонайменше одне, що явно не так у виборі: це повільно. Дійсно повільно порівняно з усім іншим, що відбувається в макросі.
vacip

4

Швидкий відповідь:

Щоб уникнути використання .Selectметоду, ви можете встановити змінну, рівну властивості, яку ви хочете.

► Наприклад, якщо ви хочете, щоб значення у Cell A1вас було, ви можете встановити змінну, рівну властивості значення цієї комірки.

  • Приклад valOne = Range("A1").Value

► Наприклад, якщо ви хочете кодове ім'я "Sheet3", ви можете встановити змінну, рівну властивості кодового імені цього робочого листа.

  • Приклад valTwo = Sheets("Sheet3").Codename

Я сподіваюся, що це допомагає. Повідомте мене, якщо у вас є питання.


3

Я помітив, що жодна з цих відповідей не згадує властивість .Offset . Це також може бути використане, щоб уникнути використання Selectдії під час маніпулювання певними клітинками, особливо стосовно посилань на вибрану клітинку (як згадує ОП 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.

Це не означає, що вони "кращі", ніж наведені вище варіанти, а лише перелік альтернатив.


0

Робота з функцією .Parent. Цей приклад показує, як встановлення лише однієї посилання myRng забезпечує динамічний доступ до всього середовища без .Select, .Activate, .Activecell, .ActiveWorkbook, .ActiveSheet тощо. (Немає загальної функції .Child)

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

Дуже приємно, але не впевнений, що це стосується питання щодо ОП. Вам взагалі не потрібен "Батько", щоб працювати в VBA, не використовуючи Select або ActiveSheet
Джефф Грісвальд

0

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

У кращому випадку ваш макрос вийде з ладу, в гіршому випадку ви можете закінчити запис значень або зміни клітинок у неправильній робочій книжці, не маючи змоги їх "скасувати".

У мене є просте золоте правило, якого я дотримуюся: додайте змінні з назвою "wb" і "ws" для об'єкта Workbook і об'єкта Worksheet і завжди використовуйте їх для позначення моєї книги макросів. Якщо мені потрібно посилатися на більш ніж одну книгу або більше одного аркуша, я додаю більше змінних.

наприклад

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

Команда "Встановити wb = thisWorkbook" абсолютно важлива. "ThisWorkbook" - це особливе значення в Excel, і це означає робочу книжку, з якої зараз працює ваш код VBA . Дуже корисний ярлик для встановлення змінної вашої робочої книги.

Після того, як ви зробите це у верхній частині свого Sub, використання їх не може бути простішим, просто використовуйте їх, де б ви не використовували "Selection":

Отже, щоб змінити значення комірки "A1" у "Output" на "Hello", а не:

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

Тепер ми можемо це зробити:

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

Що не тільки набагато надійніше і менше шансів на збій, якщо користувач працює з кількома електронними таблицями, це також набагато коротше, швидше і простіше писати.

Як додатковий бонус, якщо ви завжди називаєте свої змінні "wb" і "ws", ви можете скопіювати та вставити код з однієї книги в іншу, і вона, як правило, спрацює з необхідними мінімальними змінами.


1
Не моя суть, але я не впевнений, що це додає нічого нового до того, що вже було запропоновано в існуючих відповідях.
BigBen

Так, моя відповідь дещо зайва, але інші відповіді занадто довгі, містять занадто багато зайвих речей, і ніхто не згадав про використання цієї книжки для встановлення змінної робочого листа вперед. Це те, що якби хтось показав мені, коли я вперше занурив палець у VBA, я вважав би неймовірно корисною. Інші згадали про використання змінної Workheet, але насправді не пояснюють, чому це дуже добре, і не пропонують приклад коду з і без використання змінних робочого листа та робочої книги.
Джефф Грісвальд

Але прийнята відповідь безумовно обговорює ThisWorkbook... Я не впевнений, що ваш коментар є точним.
BigBen

Це так, ти не помилився. Але не в контексті його використання для встановлення змінної робочої книги та використання цієї змінної робочої книги вперед або використання цієї змінної книги для встановлення змінної робочого листа, як я пропоную. Моя відповідь коротша, простіша і доступніша для початківців, ніж прийнята відповідь.
Джефф Грісвальд

-3

Це приклад, який очистить вміст комірки "A1" (або більше, якщо типом вибору є xllastcell тощо). Все зроблено без вибору комірок.

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

Я сподіваюся, що це комусь допоможе.


1
Ні вибач. Це зовсім не те, що ви там робили. Насправді ви вибрали клітинку "A1" за допомогою команди "Application.GoTo", яка нічим не відрізняється від використання "Вибрати" насправді, а потім використали чіткі змісти для вашого вибору. спосіб зробити це без вибору комірок, Workbook(WorkbookName).Worksheets(WorksheetName).Range("A1").ClearContentsякий буде одним рядком, а не двома, і насправді працює без вибору комірок.
Джефф Грісвальд
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.