Ich habe viel über den verständlichen Abscheu bei der Verwendung von .Select
in Excel VBA gehört, bin mir aber nicht sicher, wie er es vermeiden kann. Ich finde, dass mein Code wiederverwendbar wäre, wenn ich Variablen anstelle von Select
-Funktionen verwenden könnte. Ich bin mir jedoch nicht sicher, wie ich auf Dinge verweisen soll (wie ActiveCell
usw.), wenn Select
nicht verwendet wird.
Ich habe dieser Artikel zu Bereichen und dieses Beispiel zu den Vorteilen der Verwendung von select gefunden, kann aber nichts über how finden.
Einige Beispiele, wie Sie select vermeiden können
Verwenden Sie Dim
'd-Variablen
Dim rng as Range
Set
die Variable auf den erforderlichen Bereich. Es gibt viele Möglichkeiten, sich auf einen Einzelzellenbereich zu beziehen
Set rng = Range("A1")
Set rng = Cells(1,1)
Set rng = Range("NamedRange")
oder ein Mehrzellenbereich
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)
Sie can verwenden die Verknüpfung zur Evaluate
-Methode, dies ist jedoch weniger effizient und sollte generell im Produktionscode vermieden werden.
Set rng = [A1]
Set rng = [A1:B10]
Alle obigen Beispiele beziehen sich auf Zellen auf dem aktiven Blatt. Wenn Sie nicht ausdrücklich nur mit dem aktiven Arbeitsblatt arbeiten möchten, ist es besser, auch eine Variable Worksheet
zu dimmen
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
Wenn Sie do mit der ActiveSheet
arbeiten möchten, ist es der Klarheit halber am besten, explizit zu sein. Aber seien Sie vorsichtig, da einige Worksheet
-Methoden das aktive Blatt ändern.
Set rng = ActiveSheet.Range("A1")
Dies bezieht sich wiederum auf die aktive Arbeitsmappe. Wenn Sie nicht ausdrücklich nur mit der Variablen ActiveWorkbook
oder ThisWorkbook
arbeiten möchten, ist es besser, auch eine Variable Workbook
zu dimmen.
Dim wb As Workbook
Set wb = Application.Workbooks("Book1")
Set rng = wb.Worksheets("Sheet1").Range("A1")
Wenn Sie do mit der ActiveWorkbook
arbeiten möchten, ist es der Klarheit halber am besten, explizit zu sein. Aber Vorsicht, viele WorkBook
-Methoden ändern das aktive Buch.
Set rng = ActiveWorkbook.Worksheets("Sheet1").Range("A1")
Sie können das ThisWorkbook
-Objekt auch verwenden, um auf das Buch zu verweisen, das den laufenden Code enthält.
Set rng = ThisWorkbook.Worksheets("Sheet1").Range("A1")
Ein üblicher (schlechter) Code ist, ein Buch zu öffnen, Daten zu holen und dann wieder zu schließen
Das ist schlecht:
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
Und wäre besser so:
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
Übergeben Sie Bereiche als änderungsvariable an Sub
und Function
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
Sie sollten auch Methoden (wie Find
und Copy
) auf Variablen anwenden
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
Wenn Sie einen Zellbereich in einer Schleife durchlaufen, ist es oftmals besser (schneller), die Bereichswerte zuerst in ein variantes Array zu kopieren und darüber zu schleifen
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
Dies ist ein kleiner Vorgeschmack auf das Mögliche.
Zwei Hauptgründe, warum .Select
/.Activate
/Selection
/Activecell
/Activesheet
/Activeworkbook
etc ... vermieden werden sollte
Wie vermeiden wir es?
1) Arbeiten Sie direkt mit den relevanten Objekten
Betrachten Sie diesen Code
Sheets("Sheet1").Activate
Range("A1").Select
Selection.Value = "Blah"
Selection.NumberFormat = "@"
Dieser Code kann auch als geschrieben werden
With Sheets("Sheet1").Range("A1")
.Value = "Blah"
.NumberFormat = "@"
End With
2) Falls erforderlich, deklarieren Sie Ihre Variablen. Derselbe Code kann als geschrieben werden
Dim ws as worksheet
Set ws = Sheets("Sheet1")
With ws.Range("A1")
.Value = "Blah"
.NumberFormat = "@"
End With
Ein kleiner Punkt, den ich hervorheben möchte, möchte ich all den oben genannten ausgezeichneten Antworten hinzufügen:
Wahrscheinlich ist die größte Maßnahme, die Sie mit Select vermeiden können, darin,so weit wie möglich zu verwenden. Benannte Bereiche (kombiniert mit sinnvollen Variablennamen) in Ihrem VBA-Codeverwenden. Dieser Punkt wurde oben erwähnt, aber etwas überzogen; es verdient jedoch besondere Aufmerksamkeit.
Hier sind ein paar weitere Gründe, um benannte Bereiche freizügig zu nutzen, obwohl ich mir sicher mehr vorstellen kann.
Beispiel:
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
Es ist ziemlich offensichtlich, was die genannten Bereiche Months
und MonthlySales
enthalten und was die Prozedur macht.
Warum ist das wichtig? Teilweise, weil es für andere Personen leichter zu verstehen ist, aber selbst wenn Sie die einzige Person sind, die Ihren Code jemals sehen oder verwenden wird, sollten Sie dennoch benannte Bereiche und gute Variablennamen verwenden, weilDU VERGESSENWas wollten Sie ein Jahr später damit tun, undSie werden30 Minuten damit verschwenden, herauszufinden, was Ihr Code tut.
Wenn das obige Beispiel so geschrieben wurde:
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
Dieser Code funktioniert zunächst einwandfrei - bis Sie oder ein zukünftiger Benutzer entscheidet, "Ich denke, ich füge eine neue Spalte mit dem Jahr in der Spalte A
!" Hinzu oder stelle eine Spalte mit den Ausgaben zwischen den Monaten ein und Verkaufsspalten, oder fügen Sie jeder Spalte eine Überschrift hinzu. Ihr Code ist jetzt defekt. Und weil Sie schreckliche Variablennamen verwendet haben, benötigen Sie viel mehr Zeit, um herauszufinden, wie Sie das Problem beheben können, als es dauern sollte.
Wenn Sie zuvor benannte Bereiche verwendet hätten, könnten die Spalten Months
und Sales
beliebig verschoben werden, und der Code funktioniert weiterhin einwandfrei.
Ich werde die kurze Antwort geben, da alle anderen die lange gegeben haben.
Sie erhalten .select und .activate, wenn Sie Makros aufzeichnen und wiederverwenden. Wenn Sie eine Zelle oder ein Blatt auswählen, wird es einfach aktiviert. Immer wenn Sie unqualifizierte Verweise wie Range.Value
verwenden, verwenden sie nur die aktive Zelle und das aktive Blatt. Dies kann auch problematisch sein, wenn Sie nicht darauf achten, wo Ihr Code platziert wird oder ein Benutzer auf die Arbeitsmappe klickt.
Sie können diese Probleme also beseitigen, indem Sie direkt auf Ihre Zellen verweisen. Was geht:
'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)
Oder du könntest
'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"
Es gibt verschiedene Kombinationen dieser Methoden, aber das wäre der allgemeine Gedanke, der für ungeduldige Menschen wie mich so kurz wie möglich zum Ausdruck kommt.
"... und ich stelle fest, dass mein Code wiederverwendbar wäre, wenn ich Variablen anstelle von Select-Funktionen verwenden könnte."
Ich kann mir nicht mehr als eine isolierte Handvoll Situationen vorstellen, in denen .Select
die bessere Wahl wäre als die direkte Zellreferenzierung. Ich würde jedoch die Verteidigung von Selection
ansprechen und darauf hinweisen, dass sie nicht aus den gleichen Gründen wie .Select
verworfen werden sollte Gemieden werden.
Es gibt Zeiten, in denen kurze, zeitsparende Makro-Unterprogramme, die Tastenkombinationen zur Verfügung stehen, durch Tippen auf ein paar Tasten verfügbar sind, dies spart viel Zeit. In der Lage zu sein, eine Gruppe von Zellen auszuwählen, um den Betriebscode für Works-Wunder zu implementieren, wenn mit Daten im Taschenformat gearbeitet wird, die nicht einem arbeitsblattweiten Datenformat entsprechen. Auf die gleiche Art und Weise, wie Sie eine Gruppe von Zellen auswählen und eine Formatänderung anwenden, kann die Auswahl einer Gruppe von Zellen für die Ausführung von speziellem Makrocode eine erhebliche Zeitersparnis bedeuten.
Beispiele für ein Auswahl-basiertes Sub-Framework:
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
Der eigentliche zu verarbeitende Code kann von einer einzelnen Zeile bis zu mehreren Modulen reichen. Ich habe diese Methode verwendet, um lange laufende Routinen für eine unregelmäßige Auswahl von Zellen zu initiieren, die die Dateinamen externer Arbeitsmappen enthalten.
Kurz gesagt, verwerfen Sie Selection
nicht, da sie eng mit .Select
und ActiveCell
verbunden ist. Als Eigenschaft eines Arbeitsblatts hat es viele andere Zwecke.
(Ja, ich weiß, dass es sich bei dieser Frage um .Select
und nicht um Selection
handelte, aber ich wollte jegliche Missverständnisse beseitigen, die VBA-Neulinge möglicherweise daraus ziehen.)
Bitte beachten Sie, dass im Folgenden der Select-Ansatz (derjenige, den das OP vermeiden möchte) mit dem Range-Ansatz (und dies ist die Antwort auf die Frage) verglichen wird. Hören Sie also nicht auf zu lesen, wenn Sie die erste Auswahl sehen.
Es hängt wirklich davon ab, was Sie zu tun versuchen. Auf jeden Fall kann ein einfaches Beispiel hilfreich sein. Nehmen wir an, Sie möchten den Wert der aktiven Zelle auf "foo" setzen. Mit ActiveCell würden Sie so etwas schreiben:
Sub Macro1()
ActiveCell.Value = "foo"
End Sub
Wenn Sie es für eine Zelle verwenden möchten, die nicht aktiv ist, z. B. für "B2", sollten Sie sie zuerst wie folgt auswählen:
Sub Macro2()
Range("B2").Select
Macro1
End Sub
Mit Ranges können Sie ein generischeres Makro schreiben, mit dem Sie den Wert einer beliebigen Zelle nach Belieben festlegen können:
Sub SetValue(cellAddress As String, aVal As Variant)
Range(cellAddress).Value = aVal
End Sub
Dann können Sie Macro2 wie folgt umschreiben:
Sub Macro2()
SetCellValue "B2", "foo"
End Sub
Und Macro1 als:
Sub Macro1()
SetValue ActiveCell.Address, "foo"
End Sub
Ich hoffe, das hilft, die Dinge ein wenig aufzuklären.
Das Vermeiden von Select
und Activate
macht Sie zu einem etwas besseren VBA-Entwickler. Im Allgemeinen werden Select
und Activate
verwendet, wenn ein Makro aufgezeichnet wird. Daher wird das Arbeitsblatt oder der Bereich Parent
immer als aktiv betrachtet.
So können Sie Select
und Activate
in den folgenden Fällen vermeiden:
Von (mit dem Makrorecorder generierter Code):
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
Zu:
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
Von:
Sheets("Source").Select
Columns("A:D").Select
Selection.Copy
Sheets("Target").Select
Columns("A:D").Select
ActiveSheet.Paste
Zu:
Worksheets("Source").Columns("A:D").Copy Destination:=Worksheets("Target").Range("a1")
Sie können mit []
Darauf zugreifen. Welches ist wirklich schön, im Vergleich zu den anderen. Überprüfe dich selbst:
Dim Months As Range
Dim MonthlySales As Range
Set Months = Range("Months")
Set MonthlySales = Range("MonthlySales")
Set Months =[Months]
Set MonthlySales = [MonthlySales]
Das Beispiel von oben würde so aussehen:
Worksheets("Source").Columns("A:D").Copy Destination:=Worksheets("Target").[A1]
Wenn Sie zu select
bereit sind, kopieren Sie in der Regel wahrscheinlich etwas. Wenn Sie nur an den Werten interessiert sind, ist dies eine gute Option, um die Auswahl zu vermeiden:
Range("B1:B6").Value = Range("A1:A6").Value
Dies ist wahrscheinlich der häufigste Fehler bei vba . Wann immer Sie Bereiche kopieren, wird das Arbeitsblatt manchmal nicht referenziert und VBA berücksichtigt daher das 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
Oder .Activate
Für irgendetwas verwenden?Sie können nur dann berechtigt sein, .Activate
Und .Select
Zu verwenden, wenn Sie sicherstellen möchten, dass aus visuellen Gründen ein bestimmtes Arbeitsblatt ausgewählt ist. Zum Beispiel, dass Ihre Excel-Datei immer mit dem zuerst ausgewählten Deckblatt geöffnet wurde, wobei nicht berücksichtigt wurde, welches das aktive Blatt war, als die Datei geschlossen wurde. So etwas ist absolut in Ordnung:
Private Sub Workbook_Open()
Worksheets("Cover").Activate
End Sub
Geben Sie immer die Arbeitsmappe, das Arbeitsblatt und die Zelle/den Bereich an.
Zum Beispiel:
Thisworkbook.Worksheets("fred").cells(1,1)
Workbooks("bob").Worksheets("fred").cells(1,1)
Da Endbenutzer immer nur auf die Schaltflächen klicken, und sobald der Fokus von der Arbeitsmappe abweicht, möchte der Code damit arbeiten, dann geht alles schief.
Verwenden Sie niemals den Index einer Arbeitsmappe.
Workbooks(1).Worksheets("fred").cells(1,1)
Sie wissen nicht, welche anderen Arbeitsmappen geöffnet sind, wenn der Benutzer Ihren Code ausführt.
Diese Methoden sind ziemlich stigmatisiert, also übernehmen @Vityata und @Jeeped die Führung, um eine Linie in den Sand zu ziehen:
.Activate
, .Select
, Selection
, ActiveSomething
Methoden/Eigenschaften aufGrundsätzlich, weil sie hauptsächlich zur Verarbeitung von Benutzereingaben über die Anwendungsbenutzeroberfläche aufgerufen werden. Da diese Methoden aufgerufen werden, wenn der Benutzer Objekte über die Benutzeroberfläche verarbeitet, handelt es sich um diejenigen, die vom Makro-Recorder aufgezeichnet werden. Aus diesem Grund ist das Aufrufen dieser Objekte in den meisten Situationen spröde oder überflüssig: Sie müssen keine auswählen Objekt, um unmittelbar danach eine Aktion mit Selection
auszuführen.
Diese Definition regelt jedoch die Situationen, für die sie erforderlich sind:
.Activate
, .Select
, .Selection
, .ActiveSomething
Methoden/Eigenschaften aufGrundsätzlich, wenn Sie davon ausgehen, dass der final-Benutzer bei der Ausführung eine Rolle spielt.
Wenn Sie sich in der Entwicklung befinden und davon ausgehen, dass der Benutzer die Objektinstanzen auswählt, für die Ihr Code verarbeitet werden soll, sind .Selection
oder .ActiveObject
geeignet.
Auf der anderen Seite sind .Select
und .Activate
von Nutzen, wenn Sie die nächste Aktion des Benutzers ableiten können und Sie möchten, dass Ihr Code den Benutzer führt, wodurch er möglicherweise einige Zeit und Mausklicks spart. Wenn Ihr Code beispielsweise gerade eine brandneue Instanz eines Diagramms erstellt oder aktualisiert hat, möchte der Benutzer möglicherweise etwas davon auschecken, und Sie könnten .Activate
auf dem entsprechenden Blatt oder in seinem Arbeitsblatt aufrufen, um dem Benutzer die Zeit zu sparen, nach der er gesucht wird. Wenn Sie wissen, dass der Benutzer einige Bereichswerte aktualisieren muss, können Sie diesen Bereich programmgesteuert auswählen.
Schnelle Antwort:
Um die Verwendung der Methode .Select
zu vermeiden, können Sie eine Variable entsprechend der gewünschten Eigenschaft festlegen.
► Wenn Sie zum Beispiel den Wert in Cell A1
haben möchten, können Sie eine Variable setzen, die der Eigenschaft value dieser Zelle entspricht.
valOne = Range("A1").Value
► Wenn Sie beispielsweise den Codenamen von "Sheet3" benötigen, können Sie eine Variable setzen, die der Codename-Eigenschaft dieses Arbeitsblatts entspricht.
valTwo = Sheets("Sheet3").Codename
Ich hoffe das hilft. Lassen Sie mich wissen, wenn Sie Fragen haben.
IMHO verwendet .select
von Leuten, die wie ich begonnen haben, VBA notwendigerweise durch Aufzeichnen von Makros zu erlernen und dann den Code zu ändern, ohne zu wissen, dass .select
und nachfolgende selection
nur ein unnötiger Mittelmensch sind.
.select
kann, wie bereits bekannt gegeben, vermieden werden, indem direkt mit den bereits vorhandenen Objekten gearbeitet wird. Dies ermöglicht verschiedene indirekte Referenzierungen, wie z. B. das Berechnen von i und j auf komplexe Weise und das Bearbeiten der Zelle (i, j) usw.
Andernfalls ist mit .select
selbst nichts falsch, und Sie können leicht Verwendungen dafür finden, z. Ich habe eine Kalkulationstabelle, die ich mit Datum auffülle, ein Makro aktiviert, das etwas Magie mit sich bringt, und es in einem akzeptablen Format auf einem separaten Blatt exportiert, was jedoch einige abschließende manuelle (unvorhersehbare) Eingaben in eine benachbarte Zelle erfordert. Hier kommt also der Moment für .select
, der mir zusätzliche Mausbewegungen und Mausklicks erspart.
Mir ist aufgefallen, dass in keiner dieser Antworten die . Offset-Eigenschaft erwähnt wird. Dies kann auch verwendet werden, um die Verwendung der Aktion Select
zu vermeiden, wenn bestimmte Zellen bearbeitet werden, insbesondere in Bezug auf eine ausgewählte Zelle (wie im OP mit ActiveCell
erwähnt).
Hier einige Beispiele.
Ich gehe auch davon aus, dass die "ActiveCell" J4 ist.
ActiveCell.Offset(2, 0).Value = 12
J6
Auf einen Wert von 12ActiveCell.Offset(0,1).Copy ActiveCell.Offset(,2)
k4
Nach L4
Kopiert.i4
ActiveCell.Offset(, -1).EntireColumn.ClearContents
Dies soll nicht heißen, dass sie "besser" sind als die oben genannten Optionen, sondern nur Alternativen auflisten.
Dies ist ein Beispiel, das den Inhalt der Zelle "A1" löscht (oder mehr, wenn der Auswahltyp "xllastcell" ist). Alles erledigt, ohne die Zellen auswählen zu müssen.
Application.GoTo Reference:=Workbook(WorkbookName).Worksheets(WorksheetName).Range("A1")
Range(Selection,selection(selectiontype)).clearcontents
Ich hoffe das hilft jemandem.