Jak uniknąć używania opcji Wybierz w programie Excel VBA

537

Słyszałem wiele o zrozumiałej wstręt do używania .Selectw Excel VBA, ale nie jestem pewien, jak tego uniknąć. Zauważyłem, że mój kod byłby bardziej przydatny, gdybym mógł używać zmiennych zamiast Selectfunkcji. Jednak nie jestem pewien, jak odwoływać się do rzeczy (takich jak ActiveCellitp.), Jeśli nie używam Select.

Znalazłem ten artykuł na temat zakresów i ten przykład na temat korzyści płynących z nie używania select, ale nie mogę znaleźć niczego na temat tego, jak ?

BiGXERO
źródło
14
Ważne jest, aby pamiętać, że istnieją przypadki, w których użycie Selecti / lub ActiveSheetitp. Itp. Jest całkowicie nieuniknione. Oto przykład, który znalazłem: stackoverflow.com/questions/22796286/…
Rick obsługuje Monikę
9
I zdarzają się sytuacje - edycja danych wykresu w ppt z leżącym u podstaw plikiem Excela - gdzie wymagana jest aktywacja lub wybór.
brettdj
@ brettdj - oto najnowszy przykład . Aby ustawić wszystkie arkusze w skoroszycie na tę samą wartość, wydaje się .Select / .Selectionto konieczne.
BruceWayne
3
@bruce z tego samego QA wydaje się, że nie jest
Chris Neilsen,

Odpowiedzi:

565

Kilka przykładów unikania wyboru

Użyj Dimzmiennych „d”

Dim rng as Range

Setzmienna do wymaganego zakresu. Istnieje wiele sposobów odniesienia do zakresu jednokomórkowego

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

lub zakres wielu komórek

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)

Państwo może używać skrótu do Evaluatemetody, ale to jest mniej wydajny i generalnie należy unikać w kodzie produkcyjnym.

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

Wszystkie powyższe przykłady odnoszą się do komórek na aktywnym arkuszu . Jeśli nie chcesz pracować tylko z aktywnym arkuszem, lepiej jest również przyciemnić Worksheetzmienną

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

Jeśli nie chcą pracować z ActiveSheet, dla jasności, że najlepiej być jawne. Ale uważaj, ponieważ niektóre Worksheetmetody zmieniają aktywny arkusz.

Set rng = ActiveSheet.Range("A1")

Ponownie odnosi się to do aktywnego skoroszytu . Jeśli nie chcesz pracować tylko z ActiveWorkbooklub ThisWorkbook, lepiej jest również przyciemnić Workbookzmienną.

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

Jeśli nie chcą pracować z ActiveWorkbook, dla jasności, że najlepiej być jawne. Ale uważaj, ponieważ wiele WorkBookmetod zmienia aktywną książkę.

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

Możesz także użyć tego ThisWorkbookobiektu, aby odnieść się do książki zawierającej działający kod.

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

Częstym (złym) fragmentem kodu jest otwieranie książki, pobieranie danych, a następnie zamykanie

To jest złe:

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

I byłoby lepiej, jak:

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

Karnet waha się swoimi Subs i Functions jako zmienne klasy

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

Powinieneś także zastosować metody (takie jak Findi Copy) do zmiennych

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

Jeśli zapętlasz zakres komórek, często lepiej (szybciej) jest najpierw skopiować wartości zakresu do tablicy wariantów i zapętlić

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

To jest mały degustator tego, co jest możliwe.

Chris Neilsen
źródło
7
dodając do tej genialnej odpowiedzi, że aby pracować z zakresem , nie musisz znać jego rzeczywistego rozmiaru, o ile znasz lewy górny rng1(12, 12)róg ... np. będzie działał, nawet jeśli rng1 był ustawiony [A1:A10]tylko na.
MikeD,
3
@ Chrisrisilsen Chris, uważam, że możesz także użyć prefiksu arkusza roboczego przed zapisaniem skrótu odwołania do komórki, aby uchronić Cię przed pisaniem w Rangeten sposób: ActiveSheet.[a1:a4]lub ws.[b6].
Logan Reed
3
@AndrewWillems ... lub 48 razy w tym poście, ale kto się liczy. ☺ ... ale poważnie, łatwo zapomnieć o pracy ze zmiennymi przechowującymi obiekty. variantZmienna nie wymaga Set przypisać obiekt do niego. Na przykład Dim x: x = 1jest w porządku, ale Dim x: x = Sheets("Sheet1")wygeneruje błąd 438. Jednak samo zamieszanie / wyjaśnienie nieDim x: x = Range("A1") spowoduje błędu. Dlaczego? ... ponieważ przypisuje się wartość obiektu do zmiennej, a nie odwołanie do samego obiektu (ponieważ jest to odpowiednik )Dim x: x = Range("A1").Value
ashleedawg
1
@ user3932000 Nie znam scenariusza, w którym nazwy arkuszy zmieniają się automatycznie. Jeśli chodzi o nazwy plików, zrobiłoby to tylko, jeśli w folderze znajduje się już plik o tej nazwie. Wystarczy użyć Zapisz ... lub na stałe wpisać nazwę pliku, aby zapisać go jako ciąg. Jeśli nie możesz rozwiązać tego problemu, zamiast komentowania, powinieneś o niego zapytać osobno.
TylerH
1
@ user3932000, który byłby interesującym pytaniem. Jestem pewien, że istnieją sposoby, aby sobie z tym poradzić .. jesteś na tyle długo, aby wiedzieć, że przechwycenie wątku komentarza do starego Q nie jest
dobrym pomysłem
212

Dwa główne powody, dla których należy unikać .Select/ .Activate/ Selection/ Activecell/ Activesheet/ Activeworkbookitp

  1. Spowalnia twój kod.
  2. Jest to zazwyczaj główna przyczyna błędów w czasie wykonywania.

Jak tego uniknąć?

1) Bezpośrednio pracuj z odpowiednimi obiektami

Rozważ ten kod

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

Ten kod można również zapisać jako

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

2) W razie potrzeby zadeklaruj zmienne. Ten sam kod powyżej można zapisać jako

Dim ws as worksheet

Set ws = Sheets("Sheet1")

With ws.Range("A1")
    .Value = "Blah"
    .NumberFormat = "@"
End With
Siddharth Rout
źródło
17
To dobra odpowiedź, ale brakuje mi tego tematu, kiedy potrzebujemy Aktywować. Wszyscy mówią, że jest źle, ale nikt nie wyjaśnia przypadków, w których warto go używać. Na przykład pracowałem z 2 skoroszytami i nie mogłem uruchomić makra na jednym ze skoroszytów bez wcześniejszej aktywacji. Czy mógłbyś trochę rozwinąć? Również jeśli na przykład nie aktywuję arkuszy podczas kopiowania zakresu z jednego arkusza na inny, podczas wykonywania programu wydaje się, że aktywuje odpowiednie arkusze, domyślnie.
user3032689
1
Uważam, że czasami trzeba najpierw aktywować arkusz, jeśli trzeba go wkleić lub przefiltrować. Powiedziałbym, że najlepiej unikać aktywacji w jak największym stopniu, ale są przypadki, w których trzeba to zrobić. Więc aktywuj i wybieraj do absolutnego minimum zgodnie z powyższą odpowiedzią.
Nick
7
myślę, że nie chodzi o to, aby całkowicie ich nie używać, ale tak bardzo, jak to możliwe. jeśli chcesz zapisać skoroszyt, aby gdy ktoś go otworzy, wybrana została komórka w określonym arkuszu, musisz wybrać ten arkusz i komórkę. kopiuj / wklej to zły przykład, przynajmniej w przypadku wartości, można to zrobić szybciej za pomocą kodu, takiego jakSheets(2).[C10:D12].Value = Sheets(1).[A1:B3].Value
robotik
1
@Nick Nie musisz aktywować arkuszy, aby je wkleić lub przefiltrować. Użyj obiektu arkusza w swoich poleceniach wklejania lub filtrowania. To staje się łatwiejsze, gdy uczysz się modelu obiektowego Excel poprzez praktykę. Wydaje mi się, że używam tylko .Aktywacji jest, kiedy tworzę nowy arkusz, ale chcę, aby oryginalny arkusz pojawił się po zakończeniu kodu.
phrebh
3
@phrebh Nie musisz używać, .Activateaby przejść do oryginalnego arkusza, po prostu użyjApplication.Goto
GMalc
88

Jednym drobnym naciskiem położę nacisk na wszystkie doskonałe odpowiedzi podane powyżej:

Prawdopodobnie największą rzeczą, jaką możesz zrobić, aby uniknąć używania Select, jest jak najwięcej, używanie nazwanych zakresów (w połączeniu z sensownymi nazwami zmiennych) w kodzie VBA . Ten punkt został wspomniany powyżej, ale nieco przesadzony; zasługuje jednak na szczególną uwagę.

Oto kilka dodatkowych powodów, aby swobodnie korzystać z nazwanych zakresów, choć jestem pewien, że mógłbym wymyślić więcej.

Nazwane zakresy ułatwiają czytanie i zrozumienie kodu.

Przykład:

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

Jest dość oczywiste, jaka nazwane zakresy Monthsi MonthlySaleszawierać i jakie procedura robi.

Dlaczego to jest ważne? Częściowo dlatego, że innym ludziom łatwiej jest to zrozumieć, ale nawet jeśli jesteś jedyną osobą, która kiedykolwiek zobaczy lub użyje twojego kodu, powinieneś nadal używać nazwanych zakresów i dobrych nazw zmiennych, ponieważ ZAPOMNIJ, co chciałeś z tym zrobić. rok później i ty zmarnujesz 30 minut na zastanawianie się, co robi Twój kod.

Nazwane zakresy zapewniają, że makra nie będą się łamać, gdy (nie jeśli!) Zmieni się konfiguracja arkusza kalkulacyjnego.

Zastanów się, czy powyższy przykład został napisany w ten sposób:

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

Ten kod na początku będzie działał dobrze - to znaczy dopóki ty lub przyszły użytkownik nie zdecydujesz „gee wiz, myślę, że dodam nową kolumnę z rokiem w kolumnie A !” Lub wstawię kolumnę wydatków między miesiącami a kolumny sprzedaży lub dodaj nagłówek do każdej kolumny. Teraz twój kod jest zepsuty. A ponieważ użyłeś okropnych nazw zmiennych, zajmie ci to dużo więcej czasu, aby wymyślić, jak to naprawić, niż powinno.

Jeśli na początku używałeś nazwanych zakresów, kolumny Monthsi Salesmożna przesuwać w dowolne miejsce, a Twój kod będzie działał dobrze.

Rick wspiera Monikę
źródło
6
Trwa debata na temat tego, czy nazwane zakresy są dobrym czy złym projektem arkusza kalkulacyjnego - jestem zdecydowanie w obozie. Z mojego doświadczenia wynika, że ​​zwiększają błędy (dla standardowych użytkowników, którzy nie potrzebują kodu).
brettdj
12
Zgadzam się z twoją filozofią rozwoju; jednak myślę, że ten papier jest nonsensem. Mówi o tym, w jaki sposób nazwy zakresów mogą wprowadzać w błąd nowicjuszy debugujących arkusze kalkulacyjne, ale każdy, kto używa nowicjuszy do przeglądania złożonych arkuszy kalkulacyjnych, dostaje to, na co zasługuje! Pracowałem kiedyś dla firmy, która przeglądała finansowe arkusze kalkulacyjne i mogę powiedzieć, że nie jest to praca, którą dajesz nowicjuszowi.
DeanOC
8
Nie ma sensownej debaty. Każdy, kto spiera się z określonymi nazwami, nie poświęcił czasu, aby w pełni zrozumieć ich konsekwencje. Nazwane formuły mogą być najgłębszą i najbardziej użyteczną konstrukcją w całym programie Excel.
Excel Hero
10
@brettdj: Twoje cytowanie jest poprawne, ale zapomniałeś wspomnieć, że po nim następuje sześć zwrotów „Z wyjątkiem…”. Jednym z nich jest: „ Z wyjątkiem zastępowania odniesień do komórek w kodowaniu makr Zawsze używaj Nazw Excela jako substytutu odwołań do komórek podczas konstruowania makr. Ma to na celu uniknięcie błędów wynikających z wstawienia dodatkowych wierszy lub kolumn, przez co kodowanie makr nie będzie już wskazuje zamierzone dane źródłowe. ”
Marcus Mangelsdorf,
47

Dam krótką odpowiedź, ponieważ wszyscy inni udzielili długiej.

Otrzymasz .select i .activate za każdym razem, gdy nagrywasz makra i używasz ich ponownie. Po wybraniu komórki lub arkusza aktywuje się. Od tego momentu za każdym razem, gdy korzystasz z niekwalifikowanych odniesień, tak Range.Valuejakby korzystali tylko z aktywnej komórki i arkusza. Może to być również problematyczne, jeśli nie oglądasz, gdzie jest umieszczony kod lub użytkownik klika skoroszyt.

Możesz więc wyeliminować te problemy, bezpośrednio odwołując się do komórek. Który idzie:

'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)

Lub mógłbyś

'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"

Istnieją różne kombinacje tych metod, ale taka byłaby ogólna idea wyrażona jak najkrócej dla niecierpliwych ludzi takich jak ja.

MattB
źródło
33

„... i odkrywam, że mój kod byłby bardziej przydatny, gdybym mógł używać zmiennych zamiast funkcji Select”.

Chociaż nie mogę wymyślić nic poza odosobnioną garstką sytuacji, w których .Selectbyłby lepszy wybór niż bezpośrednie odwoływanie się do komórki, podniosłem się do obrony Selectioni wskazałem, że nie należy go wyrzucać z tych samych powodów, których .Selectnależy unikać.

Są chwile, gdy krótkie, oszczędzające czas makropodprogramy przypisane do kombinacji skrótów dostępnych za naciśnięciem kilku klawiszy oszczędzają dużo czasu. Będąc w stanie wybrać grupę komórek, aby wprowadzić kod operacyjny w operacjach cuda, gdy mamy do czynienia z danymi kieszonkowymi, które nie są zgodne z formatem danych obejmującym cały arkusz roboczy. W ten sam sposób, w jaki możesz wybrać grupę komórek i zastosować zmianę formatu, wybranie grupy komórek, dla których ma zostać uruchomiony specjalny kod makr, może znacznie zaoszczędzić czas.

Przykłady podprogramów opartych na selekcji:

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

Rzeczywisty kod do przetworzenia może być dowolny, od jednej linii po wiele modułów. Użyłem tej metody do zainicjowania długotrwałych procedur na niewyrównanym wyborze komórek zawierających nazwy plików zewnętrznych skoroszytów.

Krótko mówiąc, nie odrzucaj Selectionze względu na bliskie powiązania z .SelectiActiveCell . Jako właściwość arkusza ma wiele innych celów.

(Tak, wiem, że chodziło o to pytanie .Select, Selectionale nie chciałem usunąć nieporozumień, które mogą wnioskować początkujący koderzy VBA).


źródło
13
Selectionmoże być dowolną częścią arkusza roboczego, więc równie dobrze może najpierw przetestować typ obiektu przed przypisaniem go do zmiennej, ponieważ jawnie zadeklarowano go jako Range.
L42
29

Zauważ, że w dalszej części porównuję podejście Select (to, którego OP chce uniknąć), z podejściem Range (i to jest odpowiedź na pytanie). Więc nie przestawaj czytać, gdy zobaczysz pierwszy Wybierz.

To naprawdę zależy od tego, co próbujesz zrobić. W każdym razie przydatny może być prosty przykład. Załóżmy, że chcesz ustawić wartość aktywnej komórki na „foo”. Za pomocą ActiveCell napisałbyś coś takiego:

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

Jeśli chcesz go użyć dla komórki, która nie jest aktywna, na przykład dla „B2”, musisz ją najpierw wybrać, tak jak poniżej:

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

Za pomocą zakresów możesz napisać bardziej ogólne makro, którego można użyć do ustawienia wartości dowolnej komórki na dowolne:

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

Następnie możesz przepisać Macro2 jako:

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

I Macro1 jako:

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

Mam nadzieję, że to pomoże trochę wyjaśnić.

Francesco Baruchelli
źródło
1
Dzięki za tak szybką odpowiedź. Czy to oznacza, że ​​jeśli normalnie dodałbym komórki do zakresu, nazwałbym zakres i iterował, powinienem przejść od razu do tworzenia tablicy?
BiGXERO
Nie jestem pewien, czy rozumiem, co masz na myśli, ale możesz utworzyć Zasięg za pomocą jednej instrukcji (np. Zasięg („B5: C14”)) i możesz nawet ustawić jego wartość naraz (jeśli musi być taki sam dla każda komórka w zakresie), np. Zakres („B5: C14”). Wartość = „abc”
Francesco Baruchelli
29

Unikanie Selecti Activatejest ruchem, który sprawia, że ​​jesteś trochę lepszym programistą VBA. W ogólności, Selecta Activatesą stosowane, gdy makro jest rejestrowane, zatem Parentarkusz lub zakres jest zawsze uważany za aktywnym.

W ten sposób można uniknąć Selectoraz Activatew następujących przypadkach:


Dodanie nowego arkusza i skopiowanie do niego komórki:

Z (kod wygenerowany za pomocą rejestratora makr):

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

Do:

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

Gdy chcesz skopiować zakres między arkuszami:

Od:

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

Do:

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

Używanie fantazyjnych nazwanych zakresów

Możesz uzyskać do nich dostęp za pomocą [], co jest naprawdę piękne, w porównaniu do innych sposobów. Sprawdź się:

Dim Months As Range
Dim MonthlySales As Range

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

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

Przykład z góry wyglądałby tak:

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

Nie kopiowanie wartości, ale ich przyjmowanie

Zwykle, jeśli chcesz select, najprawdopodobniej coś kopiujesz. Jeśli interesują Cię tylko wartości, jest to dobra opcja, aby uniknąć wyboru:

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


Staraj się zawsze odwoływać również do arkusza roboczego

Jest to prawdopodobnie najczęstszy błąd w . Za każdym razem, gdy kopiujesz zakresy, czasami do arkusza nie ma odniesienia, a zatem VBA uważa niewłaściwy arkusz za arkusz ActiveWorks.

'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

Czy naprawdę nigdy nie mogę używać .Selectani .Activatedo niczego?

  • Dobrym przykładem tego, kiedy możesz być uzasadniony w użyciu .Activatei .Selectjest to, kiedy chcesz upewnić się, że wybrany arkusz roboczy został wybrany ze względów wizualnych. Na przykład, że program Excel zawsze otwiera się z zaznaczonym najpierw arkuszem okładki, niezależnie od tego, który arkusz ActiveSheet był zamknięty, gdy plik był zamknięty.

Zatem coś takiego jak poniższy kod jest absolutnie OK:

Private Sub Workbook_Open()
    Worksheets("Cover").Activate
End Sub
Vityata
źródło
Możesz użyć Application.Goto zamiast Arkuszy. Aktywuj. Nieco mniej ryzykowne.
Geoff Griswald
1
Późna odpowiedź FYI - miły i nieco nieoczekiwany przykład potrzebnego .Select- a także mojej pracy - można znaleźć na stronie Jak pisać identyczne informacje na wszystkich arkuszach - @Vityata :)
TM
1
@TM - rzeczywiście jest to ciekawy przykład i prawdopodobnie oszczędza kilka milisekund na ponad 100 arkuszach roboczych, ale prawdopodobnie zniechęciłbym go, jeśli gdzieś go zobaczę. W każdym razie, Wybór tam nie jest wyraźnie napisany, ale wynika z tego .FillAcrossSheets, więc jest to gdzieś pomiędzy (przynajmniej w moim wyobrażeniu o taksonomii VBA)
Vityata
17

Zawsze podawaj skoroszyt, arkusz i komórkę / zakres.

Na przykład:

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

Ponieważ użytkownicy końcowi zawsze po prostu klikają przyciski, a gdy tylko fokus zniknie ze skoroszytu, kod chce z nim pracować, wtedy wszystko idzie zupełnie nie tak.

I nigdy nie używaj indeksu skoroszytu.

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

Nie wiesz, jakie inne skoroszyty będą otwarte, gdy użytkownik uruchomi Twój kod.

użytkownik1644564
źródło
7
Wiesz, że nazwy arkuszy mogą się również zmieniać. Zamiast tego użyj nazw kodowych.
Rick wspiera Monikę
Jasne, że nazwy arkuszy mogą się zmienić. Ale nie zgadzam się, że powinieneś nadmiernie komplikować swój kod, aby spróbować to złagodzić. Jeśli użytkownik zmieni nazwę arkusza, a jego makro przestanie działać, to na nim jest. Zasadniczo zakładam, że nazwy arkuszy będą takie same. W przypadku szczególnie krytycznych makr uruchamiam małe sprawdzenie przed lotem przed uruchomieniem właściwego makra, które sprawdza tylko, czy wszystkie arkusze, które spodziewa się znaleźć, są rzeczywiście dostępne, a jeśli brakuje, powiadamia użytkownika, który z nich.
Geoff Griswald
10

Te metody są raczej stygmatyzowane, więc objęcie roli @Vityata i @Jeeped w celu narysowania linii w piasku:

Dlaczego nie nazwać .Activate, .Select, Selection, ActiveSomethingmetody / właściwości

Zasadniczo dlatego, że są one wywoływane głównie w celu obsługi danych wejściowych użytkownika za pośrednictwem interfejsu aplikacji. Ponieważ są to metody wywoływane, gdy użytkownik obsługuje obiekty przez interfejs użytkownika, są to te zarejestrowane przez rejestrator makr, i dlatego wywoływanie ich jest kruche lub nadmiarowe w większości sytuacji: nie musisz wybierać obiekt, aby wykonać akcję Selectionzaraz potem.

Jednak ta definicja określa sytuacje, w których są one wymagane:

Kiedy zadzwonić .Activate, .Select, .Selection, .ActiveSomethingmetody / właściwości

Zasadniczo, gdy oczekujesz, że użytkownik końcowy będzie odgrywał rolę w wykonaniu.

Jeśli się rozwijasz i spodziewasz się, że użytkownik wybierze instancje obiektów do obsługi twojego kodu, to .Selectionlub .ActiveObjectsą odpowiednie.

Z drugiej strony, .Selecti .Activatesą przydatne, gdy możesz wywnioskować użytkownika o następnej czynności i chcesz, aby Twój kod prowadził użytkownika, prawdopodobnie oszczędzając mu trochę czasu i kliknięć myszą. Na przykład, jeśli Twój kod właśnie utworzył nową instancję wykresu lub zaktualizował ją, użytkownik może chcieć to sprawdzić, a ty możesz wywołać .Activateją lub jej arkusz, aby zaoszczędzić czas na szukanie go; lub jeśli wiesz, że użytkownik będzie musiał zaktualizować niektóre wartości zakresu, możesz programowo wybrać ten zakres.

LFB
źródło
6

Używanie IMHO .selectpochodzi od ludzi, którzy podobnie jak ja, zaczęli uczyć się języka VBA z konieczności poprzez rejestrowanie makr, a następnie modyfikowanie kodu, nie zdając sobie sprawy, że .selecti selectionto po prostu niepotrzebni środkowi ludzie.

.select można uniknąć, jak już wiele opublikowano, poprzez bezpośrednią pracę z już istniejącymi obiektami, co umożliwia różne pośrednie odniesienia, takie jak obliczanie i i j w złożony sposób, a następnie edytowanie komórki (i, j) itp.

W przeciwnym razie nie ma w sobie nic pośredniego .selecti można to łatwo znaleźć, np. Mam arkusz kalkulacyjny, który wypełniam datą, aktywuję makro, które robi z nim magię i eksportuje go w dopuszczalnym formacie na osobnym arkuszu, który wymaga jednak ostatecznych ręcznych (nieprzewidywalnych) danych wejściowych do sąsiedniej komórki. Nadchodzi moment, .selectże oszczędza mi to dodatkowego ruchu myszy i kliknięcia.

Eleshar
źródło
2
Podczas gdy masz rację, przynajmniej jedna rzecz jest domyślnie nie tak z Select: jest powolna. Rzeczywiście bardzo powolny w porównaniu do wszystkiego, co dzieje się w makrze.
vacip
4

Szybka odpowiedź:

Aby uniknąć korzystania z tej .Selectmetody, możesz ustawić zmienną równą żądanej właściwości.

► Na przykład, jeśli chcesz mieć wartość, Cell A1możesz ustawić zmienną równą właściwości wartości tej komórki.

  • Przykład valOne = Range("A1").Value

► Na przykład, jeśli chcesz mieć kryptonim „Arkusz3”, możesz ustawić zmienną równą właściwości kryptonimu tego arkusza.

  • Przykład valTwo = Sheets("Sheet3").Codename

Mam nadzieję że to pomogło. Daj mi znać, jeśli masz jakieś pytania.

FinPro.Online
źródło
3

Zauważyłem, że żadna z tych odpowiedzi nie wspomina o właściwości .Offset . Można to również wykorzystać, aby uniknąć użycia Selectakcji podczas manipulowania niektórymi komórkami, szczególnie w odniesieniu do wybranej komórki (jak wspomina OP ActiveCell).

Oto kilka przykładów.

Zakładam również, że „ActiveCell” to J4 .

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

  • Spowoduje to zmianę komórki J6na wartość 12
  • Minus -2 odwoływałby się do J2

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

  • Spowoduje to skopiowanie komórki k4do L4.
  • Zauważ, że „0” nie jest potrzebne w parametrze przesunięcia, jeśli nie jest potrzebne (, 2)
  • Podobnie jak w poprzednim przykładzie byłby minus 1 i4

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

  • Spowoduje to wyczyszczenie wartości we wszystkich komórkach w kolumnie k.

Nie oznacza to, że są „lepsze” niż powyższe opcje, a jedynie listę alternatyw.

PGSystemTester
źródło
0

Praca z funkcją .Parent. Ten przykład pokazuje, jak ustawienie tylko jednego odwołania myRng umożliwia dynamiczny dostęp do całego środowiska bez funkcji .Select, .Activate, .Activecell, .ActiveWorkbook, .ActiveSheet i tak dalej. (Nie ma funkcji Genereic .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
Barneyos
źródło
Bardzo fajnie, ale nie jestem pewien, co to ma wspólnego z pytaniem dotyczącym PO. W ogóle nie potrzebujesz „rodzica” do pracy w VBA bez użycia Select lub ActiveSheet
Geoff Griswald
0

Głównym powodem, dla którego nigdy nie należy korzystać z Select lub Activesheet, jest to, że większość ludzi będzie miała otwartą co najmniej kolejną parę skoroszytów (czasami dziesiątki) po uruchomieniu makra i jeśli klikną poza arkusz podczas działania makra i klikną inne książkę, którą mają otwartą, następnie zmienia się arkusz „Activesheet”, a także docelowy skoroszyt dla niekwalifikowanego polecenia „Wybierz”.

W najlepszym wypadku makro się zawiesi, w najgorszym wypadku możesz zapisać wartości lub zmienić komórki w niewłaściwym skoroszycie bez możliwości ich „cofnięcia”.

Mam prostą złotą zasadę, którą stosuję: dodaj zmienne o nazwach „wb” i „ws” dla obiektu skoroszytu i obiektu arkusza roboczego i zawsze używaj ich w odniesieniu do mojej książki makr. Jeśli muszę odwoływać się do więcej niż jednej książki lub więcej niż jednego arkusza, dodaję więcej zmiennych.

na przykład

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

Polecenie „Set wb = ThisWorkbook” jest absolutnie kluczowe. „Ten skoroszyt” jest specjalną wartością w programie Excel i oznacza skoroszyt, z którego obecnie działa Twój kod VBA . Bardzo pomocny skrót do ustawienia zmiennej skoroszytu.

Gdy już to zrobisz u góry Sub, użycie ich nie może być prostsze, po prostu użyj ich wszędzie tam, gdzie użyjesz „Zaznaczenia”:

Aby zmienić wartość komórki „A1” w „Wyjściu” na „Cześć”, zamiast:

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

Możemy teraz to zrobić:

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

Który jest nie tylko znacznie bardziej niezawodny i rzadziej ulega awarii, jeśli użytkownik pracuje z wieloma arkuszami kalkulacyjnymi, ale jest także znacznie krótszy, szybszy i łatwiejszy do napisania.

Jako dodatkowy bonus, jeśli zawsze nazywasz swoje zmienne „wb” i „ws”, możesz kopiować i wklejać kod z jednej książki do drugiej i zwykle będzie działał przy minimalnych potrzebnych zmianach, jeśli w ogóle.

Geoff Griswald
źródło
1
Nie moja opinia negatywna, ale nie jestem pewien, czy to dodaje coś nowego do tego, co zostało już zaproponowane w istniejących odpowiedziach.
BigBen
Tak, moja odpowiedź jest nieco zbędna, ale inne odpowiedzi są zbyt długie, zawierają zbyt wiele zbędnych rzeczy i nikt nie wspomniał o użyciu ThisWorkbook do ustawienia zmiennej arkusza roboczego z góry. Jest to coś, co, gdyby ktoś pokazał mi pierwszy raz, gdy zanurzyłem palec u nogi w VBA, byłbym niezwykle pomocny. Inni wspominali o używaniu zmiennej arkusza roboczego, ale tak naprawdę nie wyjaśniają, dlaczego tak dobrze, i nie oferują przykładu kodu z użyciem zmiennych arkusza roboczego i skoroszytu i bez niego.
Geoff Griswald
Ale przyjęta odpowiedź zdecydowanie omawia ThisWorkbook... Nie jestem pewien, czy twój komentarz jest poprawny.
BigBen
Tak, nie mylisz się. Ale nie w kontekście używania go do ustawiania zmiennej skoroszytu i używania tej zmiennej skoroszytu w przyszłości lub używania tej zmiennej skoroszytu do ustawiania zmiennej arkusza roboczego, jak sugeruję. Moja odpowiedź jest krótsza, prostsza i bardziej dostępna dla początkujących niż odpowiedź zaakceptowana.
Geoff Griswald
-3

Jest to przykład, który wyczyści zawartość komórki „A1” (lub więcej, jeśli typem wyboru jest xllastcell itp.). Wszystko zrobione bez konieczności wybierania komórek.

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

Mam nadzieję, że to komuś pomoże.

marionffavp
źródło
1
Nie przepraszam W ogóle nie zrobiliście tego. To, co faktycznie zrobiłeś, to wybierz komórkę „A1” za pomocą polecenia „Application.GoTo”, co nie różni się tak naprawdę od używania „Wybierz”, a następnie zastosuj opcję clearcontent w swoim wyborze. sposobem na to bez wybierania komórek byłaby Workbook(WorkbookName).Worksheets(WorksheetName).Range("A1").ClearContentsjedna linia, a nie dwie, i faktycznie działa ona bez wybierania komórek.
Geoff Griswald