So vermeiden Sie die Verwendung von Select in Excel VBA


537

Ich habe viel über die verständliche Abscheu vor der Verwendung .Selectin Excel VBA gehört, bin mir aber nicht sicher, wie ich die Verwendung vermeiden soll. Ich stelle fest, dass mein Code wiederverwendbarer wäre, wenn ich Variablen anstelle von SelectFunktionen verwenden könnte. Ich bin mir jedoch nicht sicher, wie ich mich auf Dinge (wie die ActiveCellusw.) beziehen soll, wenn ich sie nicht benutze Select.

Ich habe diesen Artikel über Bereiche und dieses Beispiel über die Vorteile der Nichtverwendung von select gefunden , kann aber nichts darüber finden, wie ?


14
Es ist wichtig zu beachten, dass es Fälle gibt, in denen die Verwendung von Selectund / oder ActiveSheetusw. usw. völlig unvermeidbar ist. Hier ist ein Beispiel, das ich gefunden habe: stackoverflow.com/questions/22796286/…
Rick unterstützt Monica

9
Und es gibt Fälle, in denen das Aktivieren oder Auswählen erforderlich ist - das Bearbeiten von Diagrammdaten in ppt mit einer zugrunde liegenden Excel-Datei.
Brettdj

@brettdj - hier ist ein aktuelles Beispiel . Es scheint .Select / .Selectionerforderlich zu sein, alle Blätter in einer Arbeitsmappe auf denselben Wert zu setzen .
BruceWayne

3
@ Bruce aus der gleichen Qualitätssicherung scheint es nicht zu sein
Chris Neilsen

Antworten:


565

Einige Beispiele zur Vermeidung von Auswahl

Verwenden Sie Dim'd Variablen

Dim rng as Range

Setdie Variable auf den gewünschten 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 können die Verknüpfung zur EvaluateMethode verwenden, dies ist jedoch weniger effizient und sollte im Produktionscode generell vermieden werden.

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

Alle obigen Beispiele beziehen sich auf Zellen auf dem aktiven Blatt . Wenn Sie nicht speziell nur mit dem aktiven Blatt arbeiten möchten, ist es besser, auch eine WorksheetVariable 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 mit dem arbeiten möchten ActiveSheet, ist es aus Gründen der Klarheit am besten, explizit zu sein. Aber seien Sie vorsichtig, da einige WorksheetMethoden das aktive Blatt ändern.

Set rng = ActiveSheet.Range("A1")

Dies bezieht sich wiederum auf die aktive Arbeitsmappe . Sofern Sie nicht speziell nur mit dem ActiveWorkbookoder arbeiten möchten ThisWorkbook, ist es besser, auch eine WorkbookVariable zu dimmen .

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

Wenn Sie mit dem arbeiten möchten ActiveWorkbook, ist es aus Gründen der Klarheit am besten, explizit zu sein. Aber seien Sie vorsichtig, da viele WorkBookMethoden das aktive Buch verändern.

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

Sie können das ThisWorkbookObjekt auch verwenden , um auf das Buch zu verweisen, das den laufenden Code enthält.

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

Ein häufiger (schlechter) Code besteht darin, ein Buch zu öffnen, einige Daten abzurufen 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 wie:

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 Bereichsvariablen an Ihre Subs und 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

Sie sollten auch Methoden (wie Findund 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 Bereich von Zellen durchlaufen, ist es häufig besser (schneller), die Bereichswerte zuerst in ein variantes Array zu kopieren und diesen zu durchlaufen

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, was möglich ist.


7
Hinzu kommt, dass Sie, um mit einem Bereich arbeiten zu können, die tatsächliche Größe nicht kennen müssen, solange Sie oben links wissen ... z. B. rng1(12, 12)funktioniert dies, obwohl rng1 [A1:A10]nur auf eingestellt wurde.
MikeD

3
@chrisneilsen Chris, ich glaube, Sie können auch das Arbeitsblattpräfix vor der Kurzbezeichnung der Zellreferenz verwenden, um zu verhindern, dass Sie wie folgt eingeben Range: ActiveSheet.[a1:a4]oder ws.[b6].
Logan Reed

3
@ AndrewWillems ... oder 48 Mal in diesem Beitrag, aber wer zählt. ☺ ... aber im Ernst, es ist leicht zu vergessen, wenn man mit Variablen arbeitet, die Objekte enthalten. Eine variantVariable ist erst erforderlich, Set wenn Sie ihr ein Objekt zuweisen. Dim x: x = 1Ist zum Beispiel in Ordnung, Dim x: x = Sheets("Sheet1")generiert aber den Fehler 438. Nur zu verwirren / zu klären, Dim x: x = Range("A1")führt jedoch nicht zu einem Fehler. Warum? ... weil es der Variablen den Wert des Objekts zugewiesen hat , nicht einen Verweis auf das Objekt selbst (da es das Äquivalent von ist Dim x: x = Range("A1").Value)
ashleedawg

1
@ user3932000 Mir ist kein Szenario bekannt, in dem sich Blattnamen automatisch ändern. Bei Dateinamen ist dies nur möglich, wenn sich bereits eine Datei mit diesem Namen im Ordner befindet. Verwenden Sie einfach Speichern ... oder codieren Sie den Dateinamen fest, um ihn als Zeichenfolge zu speichern. Wenn Sie dieses Problem nicht lösen können, sollten Sie eine separate Frage stellen, anstatt zu kommentieren.
TylerH

1
@ user3932000 das würde ein interessantes Q ergeben. Ich bin sicher, es gibt Möglichkeiten, damit umzugehen. Du warst so lange genug, um zu wissen, dass das Entführen eines Kommentarthreads zu einem alten Q nicht der
richtige

212

Zwei Hauptgründe, warum .Select/ .Activate/ Selection/ Activecell/ Activesheet/ Activeworkbooketc ... vermieden werden sollten

  1. Es verlangsamt Ihren Code.
  2. Dies ist normalerweise die Hauptursache für Laufzeitfehler.

Wie vermeiden wir das?

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) Deklarieren Sie bei Bedarf Ihre Variablen. Der gleiche Code wie oben kann geschrieben werden als

Dim ws as worksheet

Set ws = Sheets("Sheet1")

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

17
Das ist eine gute Antwort, aber was mir zu diesem Thema fehlt, ist, wenn wir tatsächlich Aktivieren brauchen. Jeder sagt, es sei schlecht, aber niemand erklärt Fälle, in denen es sinnvoll ist, es zu verwenden. Zum Beispiel habe ich mit 2 Arbeitsmappen gearbeitet und konnte kein Makro auf einer der Arbeitsmappen starten, ohne es vorher zu aktivieren. Könnten Sie vielleicht etwas näher darauf eingehen? Auch wenn ich zum Beispiel beim Kopieren eines Bereichs von einem Blatt auf ein anderes keine Blätter aktiviere, scheint es beim Ausführen des Programms die jeweiligen Blätter sowieso implizit zu aktivieren.
user3032689

1
Ich finde, dass Sie manchmal zuerst ein Blatt aktivieren müssen, wenn Sie Daten darauf einfügen oder filtern müssen. Ich würde sagen, es ist am besten, so viel wie möglich nicht zu aktivieren, aber es gibt Fälle, in denen Sie dies tun müssen. Aktivieren und wählen Sie also weiterhin ein absolutes Minimum gemäß der obigen Antwort.
Nick

7
Ich denke, es geht nicht darum, sie komplett zu vermeiden, sondern so viel wie möglich. Wenn Sie eine Arbeitsmappe speichern möchten, damit beim Öffnen eine bestimmte Zelle in einem bestimmten Blatt ausgewählt wird, müssen Sie dieses Blatt und diese Zelle auswählen. Kopieren / Einfügen ist ein schlechtes Beispiel, zumindest im Fall von Werten, kann es schneller durch einen Code wieSheets(2).[C10:D12].Value = Sheets(1).[A1:B3].Value
robotik

1
@Nick Sie müssen keine Blätter aktivieren, um sie einzufügen oder zu filtern. Verwenden Sie das Blattobjekt in Ihren Einfüge- oder Filterbefehlen. Es wird einfacher, wenn Sie das Excel-Objektmodell durch Übung lernen. Ich glaube, ich verwende .Activate nur, wenn ich ein neues Blatt erstelle, aber ich möchte, dass das Originalblatt angezeigt wird, wenn der Code fertig ist.
Phrebh

3
@phrebh Sie müssen nicht verwenden .Activate, um zum Originalblatt zu wechseln, verwenden Sie einfachApplication.Goto
GMalc

88

Ein kleiner Schwerpunkt, den ich zu allen oben gegebenen hervorragenden Antworten hinzufügen werde:

Das wahrscheinlich größte Mittel, um die Verwendung von Select zu vermeiden, besteht darin , benannte Bereiche (kombiniert mit aussagekräftigen Variablennamen) in Ihrem VBA-Code so weit wie möglich zu verwenden . Dieser Punkt wurde oben erwähnt, aber etwas beschönigt; es verdient jedoch besondere Aufmerksamkeit.

Hier sind ein paar zusätzliche Gründe, benannte Bereiche liberal zu nutzen, obwohl ich mir sicher mehr vorstellen kann.

Benannte Bereiche erleichtern das Lesen und Verstehen Ihres Codes.

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 Monthsund MonthlySalesenthalten, und was die Prozedur tut.

Warum ist das wichtig? Zum Teil, weil es für andere Menschen einfacher ist, es zu verstehen, 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, da Sie vergessen werden, was Sie damit vorhatten Ein Jahr später verschwenden Sie 30 Minuten damit, herauszufinden, was Ihr Code tut.

Benannte Bereiche stellen sicher, dass Ihre Makros nicht beschädigt werden, wenn (nicht wenn!) Die Konfiguration der Tabelle geändert wird.

Überlegen Sie, ob das obige Beispiel folgendermaßen 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 entscheiden, "gee wiz, ich denke, ich werde eine neue Spalte mit dem Jahr in Spalte hinzufügen A!", Oder eine Spaltenspalte zwischen den Monaten und einfügen Verkaufsspalten oder fügen Sie jeder Spalte einen Header hinzu. Jetzt ist Ihr Code kaputt. Und weil Sie schreckliche Variablennamen verwendet haben, brauchen Sie viel mehr Zeit, um herauszufinden, wie Sie das Problem beheben können, als es dauern sollte.

Wenn Sie zunächst benannte Bereiche verwendet haben, können die Spalten Monthsund und Salesbeliebig verschoben werden, und Ihr Code funktioniert weiterhin einwandfrei.


6
Die Debatte darüber, ob benannte Bereiche gut oder schlecht sind, geht weiter - ich bin fest im No Camp. Nach meiner Erfahrung erhöhen sie die Fehler (für Standardbenutzer, die keinen Code benötigen).
Brettdj


12
Ich stimme Ihrer Entwicklungsphilosophie zu. Ich denke jedoch, dass das Papier Unsinn ist. Es geht darum, wie Bereichsnamen Anfänger verwirren können, die Tabellenkalkulationen debuggen, aber jeder, der Anfänger verwendet, um komplexe Tabellen zu betrachten, bekommt das, was er verdient! Ich habe für eine Firma gearbeitet, die Finanztabellen überprüft hat, und ich kann Ihnen sagen, dass dies nicht die Art von Arbeit ist, die Sie einem Neuling geben.
DeanOC

8
Es gibt keine sinnvolle Debatte. Wer gegen definierte Namen argumentiert, hat sich nicht die Zeit genommen, ihre Auswirkungen vollständig zu verstehen. Benannte Formeln können das tiefgreifendste und nützlichste Konstrukt in ganz Excel sein.
Excel Hero

10
@brettdj: Dein Zitat ist korrekt, aber du hast vergessen zu erwähnen, dass es sechs "Außer ..." Sätze gibt. Eine davon ist: " Außer als Ersatz für Zellreferenzen in der Makrocodierung Verwenden Sie beim Erstellen von Makros immer Excel-Namen als Ersatz für Zellreferenzen. Dies soll Fehler vermeiden, die durch das Einfügen zusätzlicher Zeilen oder Spalten entstehen, wodurch die Makrocodierung nicht mehr erfolgt verweist auf die beabsichtigten Quelldaten. "
Marcus Mangelsdorf

47

Ich werde die kurze Antwort geben, da alle anderen die lange gegeben haben.

Sie erhalten .select und .activate, wenn Sie Makros aufnehmen und wiederverwenden. Wenn Sie eine Zelle oder ein Blatt auswählen, wird es nur aktiviert. Von diesem Punkt an, wenn Sie nicht qualifizierte Referenzen verwenden, wie Range.Valuesie nur die aktive Zelle und das Blatt verwenden. Dies kann auch problematisch sein, wenn Sie nicht beobachten, wo Ihr Code platziert ist oder ein Benutzer auf die Arbeitsmappe klickt.

Sie können diese Probleme also beseitigen, indem Sie direkt auf Ihre Zellen verweisen. Welches 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 die allgemeine Idee, die für ungeduldige Menschen wie mich so kurz wie möglich ausgedrückt wird.


33

"... und stelle fest, dass mein Code wiederverwendbarer wäre, wenn ich Variablen anstelle von Select-Funktionen verwenden könnte."

Ich kann mir zwar nicht mehr als eine isolierte Handvoll Situationen vorstellen, in denen .Selecteine bessere Wahl als eine direkte Zellreferenzierung wäre, aber ich würde mich zur Verteidigung erheben Selectionund darauf hinweisen, dass sie nicht aus den gleichen Gründen verworfen werden .Selectsollte, die vermieden werden sollten.

Es gibt Zeiten, in denen kurze, zeitsparende Makro-Subroutinen, die Tastenkombinationen zugewiesen werden, durch Tippen auf einige Tasten viel Zeit sparen. Die Möglichkeit, eine Gruppe von Zellen auszuwählen, auf die der Betriebscode angewendet werden soll, wirkt Wunder, wenn mit Daten in der Tasche gearbeitet wird, die nicht einem arbeitsblattweiten Datenformat entsprechen. Ähnlich wie Sie eine Gruppe von Zellen auswählen und eine Formatänderung anwenden können, kann die Auswahl einer Gruppe von Zellen, für die spezieller Makrocode ausgeführt werden soll, eine erhebliche Zeitersparnis bedeuten.

Beispiele für ein auf Auswahl basierendes Unterframework:

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 tatsächlich zu verarbeitende Code kann von einer einzelnen Zeile bis zu mehreren Modulen reichen. Ich habe diese Methode verwendet, um lang laufende Routinen für eine unregelmäßige Auswahl von Zellen zu initiieren, die die Dateinamen externer Arbeitsmappen enthalten.

Kurz gesagt, nicht wegwerfen, Selectionda es eng mit .Selectund verbunden ist ActiveCell. Als Arbeitsblatteigenschaft hat es viele andere Zwecke.

(Ja, ich weiß, dass es um diese Frage ging .Select, Selectionaber ich wollte alle Missverständnisse beseitigen, auf die unerfahrene VBA-Codierer schließen könnten.)


13
Selectionkann alles im Arbeitsblatt sein, testen Sie also zuerst den Typ des Objekts, bevor Sie es einer Variablen zuweisen, da Sie es explizit als deklariert haben Range.
L42

29

Bitte beachten Sie, dass ich im Folgenden den Select-Ansatz (den das OP vermeiden möchte) mit dem Range-Ansatz vergleiche (und dies ist die Antwort auf die Frage). Hören Sie also nicht auf zu lesen, wenn Sie die erste Auswahl sehen.

Es hängt wirklich davon ab, was Sie versuchen zu tun. Auf jeden Fall könnte ein einfaches Beispiel nützlich sein. Angenommen, Sie möchten den Wert der aktiven Zelle auf "foo" setzen. Mit ActiveCell würden Sie ungefähr Folgendes 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 es zuerst wie folgt auswählen:

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

Mithilfe von Bereichen können Sie ein allgemeineres Makro schreiben, mit dem Sie den Wert jeder gewünschten Zelle auf einen beliebigen Wert einstellen 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

Hoffe, das hilft, die Dinge ein wenig aufzuklären.


1
Vielen Dank für die hervorragende Antwort so schnell. Bedeutet das also, dass ich direkt zum Erstellen eines Arrays springen sollte, wenn ich normalerweise Zellen zum Bereich hinzufügen, den Bereich benennen und ihn durchlaufen würde?
BiGXERO

Ich bin nicht sicher, ob ich verstehe, was Sie meinen, aber Sie können einen Bereich mit einer einzigen Anweisung erstellen (z. B. Bereich ("B5: C14")) und sogar seinen Wert sofort festlegen (wenn er für identisch sein muss) jede Zelle im Bereich), z. B. Bereich ("B5: C14"). Wert = "abc"
Francesco Baruchelli

29

Vermeiden Selectund Activateist der Schritt, der Sie ein bisschen besser VBA-Entwickler macht. Im Allgemeinen Selectund Activatewenn ein Makro aufgezeichnet wird, wird das ParentArbeitsblatt oder der Bereich immer als aktives betrachtet.

So können Sie Folgendes vermeiden Selectund Activatein folgenden Fällen:


Hinzufügen eines neuen Arbeitsblatts und Kopieren einer Zelle darauf:

Von (Code generiert mit Makrorecorder):

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

Wenn Sie den Bereich zwischen Arbeitsblättern kopieren möchten:

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

Verwenden Sie ausgefallene benannte Bereiche

Sie können mit ihnen zugreifen [], was im Vergleich zur anderen Art wirklich schön ist. Überprüfen Sie sich:

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 folgendermaßen aussehen:

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

Werte nicht kopieren, sondern nehmen

Wenn Sie dazu bereit selectsind, kopieren Sie normalerweise höchstwahrscheinlich 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


Versuchen Sie immer, auch auf das Arbeitsblatt zu verweisen

Dies ist wahrscheinlich der häufigste Fehler in . Wenn Sie Bereiche kopieren, wird manchmal nicht auf das Arbeitsblatt verwiesen, und daher betrachtet VBA das falsche Blatt als 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

Kann ich wirklich nie .Selectoder .Activatefür irgendetwas verwenden?

  • Ein gutes Beispiel dafür, wann die Verwendung gerechtfertigt sein könnte .Activateund .Selectwann Sie sicherstellen möchten, dass ein bestimmtes Arbeitsblatt aus visuellen Gründen ausgewählt ist. Beispiel: Ihr Excel wird immer mit dem zuerst ausgewählten Deckblatt geöffnet, unabhängig davon, welches ActiveSheet beim Schließen der Datei verwendet wurde.

Somit ist so etwas wie der folgende Code absolut in Ordnung:

Private Sub Workbook_Open()
    Worksheets("Cover").Activate
End Sub

Sie können Application.Goto anstelle von Worksheets.Activate verwenden. Etwas weniger riskant.
Geoff Griswald

1
Späte Antwort FYI - ein schönes und etwas unerwartetes Beispiel für ein benötigtes .Select- sowie meine Arbeit herum - finden Sie unter Wie man identische Informationen auf alle Blätter schreibt - @Vityata :)
TM

1
@TM - in der Tat ist es ein interessantes Beispiel und spart wahrscheinlich einige Millisekunden für mehr als 100 Arbeitsblätter, aber ich würde es wahrscheinlich entmutigen, wenn ich es irgendwo sehe. Wie auch immer, die Auswahl dort ist nicht explizit geschrieben, sondern ein Ergebnis von .FillAcrossSheets, also liegt dies irgendwo dazwischen (zumindest in meiner Vorstellung von der VBA-Taxonomie)
Vityata

17

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 Schaltflächen klicken und der Fokus von der Arbeitsmappe abweicht, mit der der Code arbeiten möchte, laufen die Dinge völlig schief.

Und 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.


7
Die Namen von Arbeitsblättern können sich ebenfalls ändern. Verwenden Sie stattdessen Codenamen.
Rick unterstützt Monica

Arbeitsblattnamen können sich natürlich ändern. Ich bin jedoch nicht der Meinung, dass Sie Ihren Code zu kompliziert machen sollten, um dies zu mildern. Wenn ein Benutzer den Namen eines Blattes ändert und sein Makro nicht mehr funktioniert, ist dies der Fall. Ich gehe im Allgemeinen nur davon aus, dass die Namen der Arbeitsblätter gleich sind. Bei besonders kritischen Makros führe ich vor dem Start des eigentlichen Makros eine kleine Überprüfung vor dem Flug durch, bei der lediglich überprüft wird, ob alle erwarteten Blätter tatsächlich vorhanden sind, und wenn sie fehlen, wird der Benutzer darüber informiert, welches.
Geoff Griswald

10

Diese Methoden sind eher stigmatisiert, also übernehmen Sie die Führung von @Vityata und @Jeeped, um eine Linie in den Sand zu ziehen:

Warum nicht nennen .Activate, .Select, Selection, ActiveSomethingMethoden / Eigenschaften

Grundsätzlich, weil sie hauptsächlich aufgerufen werden, um Benutzereingaben über die Benutzeroberfläche der Anwendung zu verarbeiten. Da es sich um die Methoden handelt, die aufgerufen werden, wenn der Benutzer Objekte über die Benutzeroberfläche verarbeitet, werden sie vom Makrorecorder aufgezeichnet. Daher ist das Aufrufen in den meisten Situationen entweder spröde oder redundant: Sie müssen keine auswählen Objekt, um eine Aktion mit Selectiondirekt danach auszuführen .

Diese Definition regelt jedoch Situationen, in denen sie erforderlich sind:

Wenn nennen .Activate, .Select, .Selection, .ActiveSomethingMethoden / Eigenschaften

Grundsätzlich, wenn Sie erwarten, dass der Endbenutzer eine Rolle bei der Ausführung spielt.

Wenn Sie entwickeln und erwarten, dass der Benutzer die Objektinstanzen auswählt , die von Ihrem Code verarbeitet werden sollen, .Selectionoder .ActiveObjectsind Sie entsprechend.

Auf der anderen Seite .Selectund .Activatenützlich, wenn Sie auf die nächste Aktion des Benutzers schließen können und möchten, dass Ihr Code den Benutzer führt, was ihm möglicherweise Zeit und Mausklicks spart. Wenn Ihr Code beispielsweise gerade eine brandneue oder aktualisierte Instanz eines Diagramms erstellt hat, möchte der Benutzer es möglicherweise auschecken, und Sie können .Activatees oder sein Blatt aufrufen , um dem Benutzer die Zeit für die Suche zu sparen. Wenn Sie wissen, dass der Benutzer einige Bereichswerte aktualisieren muss, können Sie diesen Bereich programmgesteuert auswählen.


6

IMHO Verwendung von .selectkommt von Leuten, die wie ich angefangen haben, VBA durch Notwendigkeit zu lernen, indem sie Makros aufzeichneten und dann den Code modifizierten, ohne zu bemerken, dass .selectund anschließend selectionnur ein unnötiger Mittelsmann ist.

.select kann vermieden werden, wie viele bereits gepostet haben, indem direkt mit den bereits vorhandenen Objekten gearbeitet wird, was verschiedene indirekte Verweise wie das komplexe Berechnen von i und j und das anschließende Bearbeiten der Zelle (i, j) usw. ermöglicht.

Ansonsten ist nichts implizit falsch an sich .selectselbst und Sie können leicht eine Verwendung dafür finden, z. B. habe ich eine Tabelle, die ich mit Datum fülle, aktiviere ein Makro, das etwas Magie damit macht, und exportiere es in einem akzeptablen Format auf ein separates Blatt, das erfordert jedoch einige endgültige manuelle (unvorhersehbare) Eingaben in eine benachbarte Zelle. Hier kommt also der Moment .select, der mir diese zusätzliche Mausbewegung und das Klicken erspart.


2
Während Sie Recht haben, gibt es mindestens eine Sache, die implizit mit select falsch ist: Es ist langsam. Sehr langsam im Vergleich zu allem anderen, was in einem Makro passiert.
Vacip

4

Schnelle Antwort:

Um die Verwendung der .SelectMethode zu vermeiden, können Sie eine Variable festlegen, die der gewünschten Eigenschaft entspricht.

► Wenn Sie beispielsweise den Wert in möchten, können Cell A1Sie eine Variable festlegen, die der value-Eigenschaft dieser Zelle entspricht.

  • Beispiel valOne = Range("A1").Value

► Wenn Sie beispielsweise den Codenamen 'Sheet3' möchten, können Sie eine Variable festlegen, die der Codenameneigenschaft dieses Arbeitsblatts entspricht.

  • Beispiel valTwo = Sheets("Sheet3").Codename

Ich hoffe das hilft. Lassen Sie mich wissen, wenn Sie Fragen haben.


3

Mir ist aufgefallen, dass keine dieser Antworten die .Offset-Eigenschaft erwähnt . Dies kann auch verwendet werden, um die Verwendung der SelectAktion beim Bearbeiten bestimmter Zellen zu vermeiden , insbesondere in Bezug auf eine ausgewählte Zelle (wie im OP erwähnt ActiveCell).

Hier sind einige Beispiele.

Ich gehe auch davon aus, dass "ActiveCell" J4 ist .

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

  • Dadurch wird die Zelle J6auf den Wert 12 geändert
  • Ein Minus -2 hätte auf J2 verwiesen

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

  • Dies wird kopieren Sie die Zelle in k4zu L4.
  • Beachten Sie, dass "0" im Offset-Parameter nicht benötigt wird, wenn er nicht benötigt wird (, 2).
  • Ähnlich wie im vorherigen Beispiel wäre eine minus 1 i4

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

  • Dadurch werden Werte in allen Zellen in der Spalte k gelöscht.

Diese sollen nicht sagen, dass sie "besser" sind als die oben genannten Optionen, sondern nur Alternativen auflisten.


0

Arbeiten mit der .Parent-Funktion. Dieses Beispiel zeigt, wie das Festlegen nur einer myRng-Referenz den dynamischen Zugriff auf die gesamte Umgebung ohne .Select, .Activate, .Activecell, .ActiveWorkbook, .ActiveSheet usw. ermöglicht. (Es gibt keine genereische .Kinderfunktion)

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

Sehr schön, aber nicht sicher, was dies mit der Frage des OP zu tun hat. Sie benötigen "Parent" überhaupt nicht, um in VBA zu arbeiten, ohne Select oder ActiveSheet
Geoff Griswald zu verwenden.

0

Der Hauptgrund, warum Sie Select oder Activesheet niemals verwenden, besteht darin, dass die meisten Benutzer mindestens ein paar weitere Arbeitsmappen geöffnet haben (manchmal Dutzende), wenn sie Ihr Makro ausführen und wenn sie während der Ausführung Ihres Makros von Ihrem Blatt wegklicken und auf ein anderes klicken Buch, das sie geöffnet haben, dann ändert sich das "Aktivitätsblatt", und die Zielarbeitsmappe für einen nicht qualifizierten Befehl "Auswählen" ändert sich ebenfalls.

Im besten Fall stürzt Ihr Makro ab, im schlimmsten Fall schreiben Sie möglicherweise Werte oder ändern Zellen in der falschen Arbeitsmappe, ohne sie "rückgängig machen" zu können.

Ich habe eine einfache goldene Regel, der ich folge: Fügen Sie Variablen mit den Namen "wb" und "ws" für ein Arbeitsmappenobjekt und ein Arbeitsblattobjekt hinzu und verwenden Sie diese immer, um auf mein Makrobuch zu verweisen. Wenn ich mich auf mehr als ein Buch oder mehr als ein Blatt beziehen muss, füge ich weitere Variablen hinzu.

zum Beispiel

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

Der Befehl "Set wb = ThisWorkbook" ist absolut wichtig. "ThisWorkbook" ist ein spezieller Wert in Excel und bezeichnet die Arbeitsmappe, aus der Ihr VBA-Code derzeit ausgeführt wird . Eine sehr hilfreiche Verknüpfung zum Festlegen Ihrer Arbeitsmappenvariablen.

Nachdem Sie dies oben in Ihrem Sub getan haben, könnte die Verwendung nicht einfacher sein. Verwenden Sie sie einfach überall dort, wo Sie "Auswahl" verwenden würden:

So ändern Sie den Wert der Zelle "A1" in "Ausgabe" in "Hallo" anstelle von:

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

Wir können dies jetzt tun:

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

Dies ist nicht nur viel zuverlässiger und weniger absturzgefährdet, wenn der Benutzer mit mehreren Tabellen arbeitet, sondern auch viel kürzer, schneller und einfacher zu schreiben.

Wenn Sie Ihre Variablen immer "wb" und "ws" nennen, können Sie als zusätzlichen Bonus Code von einem Buch in ein anderes kopieren und einfügen. Dies funktioniert normalerweise mit minimalen Änderungen, falls vorhanden.


1
Nicht meine Ablehnung, aber ich bin nicht sicher, ob dies etwas Neues zu dem hinzufügt, was bereits in bestehenden Antworten vorgeschlagen wurde.
BigBen

Ja, meine Antwort ist etwas überflüssig, aber die anderen Antworten sind zu lang, enthalten zu viel überflüssiges Material und niemand hat erwähnt, dass Sie ThisWorkbook verwenden, um Ihre Arbeitsblattvariable im Voraus festzulegen. Das ist etwas, was ich unglaublich hilfreich gefunden hätte, wenn mir jemand das erste Mal gezeigt hätte, dass ich einen Zeh in VBA getaucht habe. Andere haben die Verwendung einer Arbeitsblattvariablen erwähnt, erklären jedoch nicht wirklich, warum dies so gut ist, und bieten kein Beispiel für Code mit und ohne Verwendung von Arbeitsblatt- und Arbeitsmappenvariablen.
Geoff Griswald

Aber die akzeptierte Antwort bespricht definitiv ThisWorkbook... Ich bin nicht sicher, ob Ihr Kommentar korrekt ist.
BigBen

Es tut, du liegst nicht falsch. Aber nicht im Zusammenhang mit der Verwendung zum Festlegen einer Arbeitsmappenvariablen und der zukünftigen Verwendung dieser Arbeitsmappenvariablen oder der Verwendung dieser Arbeitsmappenvariablen zum Festlegen einer Arbeitsblattvariablen, wie ich vorschlage. Meine Antwort ist kürzer, einfacher und für Anfänger zugänglicher als die akzeptierte Antwort.
Geoff Griswald

-3

Dies ist ein Beispiel, mit dem der Inhalt der Zelle "A1" gelöscht wird (oder mehr, wenn der Auswahltyp xllastcell usw. 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.


1
Nein Entschuldigung. Das haben Sie dort überhaupt nicht getan. Was Sie tatsächlich getan haben, ist, die Zelle "A1" mit dem Befehl "Application.GoTo" auszuwählen, der sich nicht von der Verwendung von "Select" unterscheidet, und dann eindeutige Inhalte für Ihre Auswahl zu verwenden. Der Weg, dies ohne Auswahl von Zellen zu tun, wäre Workbook(WorkbookName).Worksheets(WorksheetName).Range("A1").ClearContentseine Zeile und nicht zwei und funktioniert tatsächlich ohne Auswahl von Zellen.
Geoff Griswald
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.