Come evitare di utilizzare Seleziona in Excel VBA

May 23 2012

Ho sentito molto parlare della comprensibile ripugnanza dell'utilizzo .Selectin Excel VBA, ma non sono sicuro di come evitare di usarlo. Sto scoprendo che il mio codice sarebbe più riutilizzabile se fossi in grado di utilizzare variabili invece di Selectfunzioni. Tuttavia, non sono sicuro di come fare riferimento a cose (come ActiveCell, ecc.) Se non lo uso Select.

Ho trovato questo articolo sugli intervalli e questo esempio sui vantaggi di non utilizzare select , ma non riesco a trovare nulla su come .

Risposte

578 chrisneilsen May 23 2012 at 17:23

Alcuni esempi di come evitare select

Usa Dimle variabili 'd

Dim rng as Range

Setla variabile all'intervallo richiesto. Esistono molti modi per fare riferimento a un intervallo di celle singole:

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

O un intervallo multicella:

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)

È possibile utilizzare il collegamento al Evaluatemetodo, ma questo è meno efficiente e dovrebbe essere generalmente evitato nel codice di produzione.

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

Tutti gli esempi precedenti si riferiscono alle celle del foglio attivo . A meno che tu non voglia specificamente lavorare solo con il foglio attivo, è meglio anche Dim una Worksheetvariabile:

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

Se non vogliono lavorare con il ActiveSheet, per chiarezza è meglio essere espliciti. Ma attenzione, poiché alcuni Worksheetmetodi cambiano il foglio attivo.

Set rng = ActiveSheet.Range("A1")

Ancora una volta, questo si riferisce alla cartella di lavoro attiva . A meno che tu non voglia specificamente lavorare solo con ActiveWorkbooko ThisWorkbook, è meglio anche Dim una Workbookvariabile.

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

Se non vogliono lavorare con il ActiveWorkbook, per chiarezza è meglio essere espliciti. Ma attenzione, poiché molti WorkBookmetodi cambiano il libro attivo.

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

È inoltre possibile utilizzare l' ThisWorkbookoggetto per fare riferimento al libro contenente il codice in esecuzione.

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

Un pezzo di codice comune (cattivo) è aprire un libro, ottenere alcuni dati e poi chiuderlo di nuovo

Questo non va bene:

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

E sarebbe meglio come:

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

Passo va ai vostri Subs ed Functions come variabili Gamma:

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

Dovresti anche applicare metodi (come Finde Copy) alle variabili:

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

Se esegui il ciclo su un intervallo di celle, spesso è meglio (più veloce) copiare prima i valori dell'intervallo su un array variante e ripetere il ciclo su quello:

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

Questo è un piccolo assaggio di ciò che è possibile.

217 SiddharthRout May 23 2012 at 17:33

Due motivi principali per cui .Select, .Activate, Selection, Activecell, Activesheet, Activeworkbook, ecc dovrebbe essere evitato

  1. Rallenta il tuo codice.
  2. Di solito è la causa principale degli errori di runtime.

Come lo evitiamo?

1) Lavora direttamente con gli oggetti rilevanti

Considera questo codice

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

Questo codice può anche essere scritto come

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

2) Se necessario, dichiara le tue variabili. Lo stesso codice sopra può essere scritto come

Dim ws as worksheet

Set ws = Sheets("Sheet1")

With ws.Range("A1")
    .Value = "Blah"
    .NumberFormat = "@"
End With
89 RicksupportsMonica May 28 2014 at 21:04

Aggiungerò un piccolo punto di enfasi a tutte le eccellenti risposte fornite in precedenza:

Probabilmente la cosa più importante che puoi fare per evitare di utilizzare Seleziona è, per quanto possibile, utilizzare intervalli denominati (combinati con nomi di variabili significativi) nel codice VBA . Questo punto è stato menzionato sopra, ma è stato un po 'sorvolato; tuttavia, merita un'attenzione speciale.

Ecco un paio di motivi aggiuntivi per fare un uso liberale di intervalli denominati, anche se sono sicuro che potrei pensare a qualcosa di più.

Gli intervalli denominati semplificano la lettura e la comprensione del codice.

Esempio:

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

È abbastanza ovvio cosa contengono Monthse gli intervalli denominati MonthlySalese cosa sta facendo la procedura.

Perché questo è importante? In parte perché è più facile per altre persone capirlo, ma anche se sei l'unica persona che vedrà o utilizzerà il tuo codice, dovresti comunque usare intervalli denominati e buoni nomi di variabili perché dimenticherai cosa intendevi farne un anno dopo, e sprecherai 30 minuti solo per capire cosa sta facendo il tuo codice.

Gli intervalli denominati assicurano che le macro non si interrompano quando (non se!) La configurazione del foglio di calcolo cambia.

Considera, se l'esempio precedente fosse stato scritto in questo modo:

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

All'inizio questo codice funzionerà perfettamente, ovvero fino a quando tu o un futuro utente non deciderete "gee wiz, penso che aggiungerò una nuova colonna con l'anno nella colonna A!", O inserirete una colonna delle spese tra i mesi e colonne delle vendite o aggiungi un'intestazione a ciascuna colonna. Ora, il tuo codice è rotto. E poiché hai usato nomi di variabili terribili, ti ci vorrà molto più tempo per capire come risolverlo di quanto dovrebbe.

Se avessi usato intervalli denominati per cominciare, le colonne Monthse Salespotrebbero essere spostate come preferisci e il tuo codice continuerebbe a funzionare perfettamente.

48 MattB Feb 28 2014 at 04:09

Darò la risposta breve poiché tutti gli altri hanno dato quella lunga.

Otterrai .select e .activate ogni volta che registri macro e le riutilizzi. Quando selezioni una cella o un foglio, questo lo rende attivo. Da quel momento in poi ogni volta che usi riferimenti non qualificati come Range.Valuese usassero semplicemente la cella e il foglio attivi. Questo può anche essere problematico se non guardi dove è posizionato il tuo codice o un utente fa clic sulla cartella di lavoro.

Quindi, puoi eliminare questi problemi facendo riferimento direttamente alle tue celle. Che va:

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

Oppure potresti

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

Ci sono varie combinazioni di questi metodi, ma questa sarebbe l'idea generale espressa il più presto possibile per le persone impazienti come me.

34 Noname Feb 24 2015 at 22:41

"... e sto scoprendo che il mio codice sarebbe più riutilizzabile se fossi in grado di utilizzare le variabili invece delle funzioni di selezione."

Sebbene non riesca a pensare a nient'altro che a una manciata isolata di situazioni in cui .Selectsarebbe una scelta migliore rispetto al riferimento diretto alle celle, mi solleverei in difesa Selectione sottolineerei che non dovrebbe essere buttato fuori per le stesse ragioni che .Selectdovrebbero essere evitate.

Ci sono momenti in cui avere sottoprogrammi macro brevi e che fanno risparmiare tempo assegnate a combinazioni di tasti di scelta rapida disponibili con il semplice tocco di un paio di tasti fa risparmiare molto tempo. Essere in grado di selezionare un gruppo di celle per emanare il codice operativo funziona a meraviglia quando si tratta di dati intascati che non sono conformi a un formato di dati a livello di foglio di lavoro. Più o meno nello stesso modo in cui potresti selezionare un gruppo di celle e applicare un cambio di formato, selezionare un gruppo di celle su cui eseguire un codice macro speciale può essere un importante risparmio di tempo.

Esempi di sottostruttura basata sulla selezione:

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

Il codice effettivo da elaborare potrebbe essere qualsiasi cosa, da una singola riga a più moduli. Ho usato questo metodo per avviare routine di lunga durata su una selezione irregolare di celle contenenti i nomi dei file di cartelle di lavoro esterne.

In breve, non scartare a Selectioncausa della sua stretta associazione con .Selecte ActiveCell. Come proprietà del foglio di lavoro, ha molti altri scopi.

(Sì, so che questa domanda riguardava .Select, no, Selectionma volevo rimuovere qualsiasi idea sbagliata che i programmatori VBA inesperti potrebbero dedurre.)

30 Vityata Mar 08 2016 at 17:04

Evitare Selected Activateè la mossa che ti rende un po 'migliore sviluppatore VBA. In generale, Selecte Activatevengono utilizzati quando viene registrata una macro, quindi il Parentfoglio di lavoro o l'intervallo è sempre considerato quello attivo.

Ecco come puoi evitare Selecte Activatenei seguenti casi:


Aggiunta di un nuovo foglio di lavoro e copia di una cella su di esso:

Da (codice generato con registratore di macro):

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

Per:

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

Quando vuoi copiare l'intervallo tra i fogli di lavoro:

A partire dal:

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

Per:

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

Utilizzo di intervalli con nomi fantasiosi

Puoi accedervi con [], che è davvero bello, rispetto all'altro modo. Controllati:

Dim Months As Range
Dim MonthlySales As Range

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

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

L'esempio dall'alto sarebbe simile a questo:

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

Non copiare valori, ma prenderli

Di solito, se lo desideri select, molto probabilmente stai copiando qualcosa. Se sei interessato solo ai valori, questa è una buona opzione per evitare di selezionare:

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


Prova sempre a fare riferimento anche al foglio di lavoro

Questo è probabilmente l'errore più comune in vba . Ogni volta che si copiano intervalli, a volte non viene fatto riferimento al foglio di lavoro e quindi VBA considera il foglio sbagliato l'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

Non posso davvero mai usare .Selecto .Activateper niente?

  • Un buon esempio di quando potresti essere giustificato nell'usare .Activateed .Selectè quando vuoi assicurarti che un foglio di lavoro specifico sia selezionato per motivi visivi. Ad esempio, il tuo Excel si aprirà sempre con il foglio di lavoro di copertina selezionato per primo, ignorando quale fosse l'ActiveSheet quando il file è stato chiuso.

Quindi, qualcosa come il codice seguente è assolutamente OK:

Private Sub Workbook_Open()
    Worksheets("Cover").Activate
End Sub
  • Un altro buon esempio è quando è necessario esportare tutti i fogli in un file PDF, come menzionato in questo caso - Come evitare le istruzioni select / active in VBA in questo esempio?

  • Quando un comando funziona solo con ActiveWindowcome ActiveWindow.Zoom o ActiveWindow.FreezePanes

30 FrancescoBaruchelli May 23 2012 at 13:11

Si noti che di seguito sto confrontando l'approccio Select (quello che l'OP vuole evitare), con l'approccio Range (e questa è la risposta alla domanda). Quindi non smettere di leggere quando vedi il primo Seleziona.

Dipende davvero da cosa stai cercando di fare. Ad ogni modo, un semplice esempio potrebbe essere utile. Supponiamo che tu voglia impostare il valore della cella attiva su "foo". Usando ActiveCell scriveresti qualcosa del genere:

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

Se vuoi usarlo per una cella che non è quella attiva, ad esempio per "B2", devi prima selezionarlo, in questo modo:

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

Usando gli intervalli puoi scrivere una macro più generica che può essere utilizzata per impostare il valore di qualsiasi cella che desideri su quello che vuoi:

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

Quindi puoi riscrivere Macro2 come:

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

E Macro1 come:

Sub Macro1()
    SetValue ActiveCell.Address, "foo"
End Sub
18 user1644564 Aug 25 2014 at 16:54

Indicare sempre la cartella di lavoro, il foglio di lavoro e la cella / intervallo.

Per esempio:

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

Perché gli utenti finali faranno sempre clic sui pulsanti e non appena il focus si sposta dalla cartella di lavoro il codice vuole lavorare, allora le cose vanno completamente storte.

E non usare mai l'indice di una cartella di lavoro.

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

Non sai quali altre cartelle di lavoro saranno aperte quando l'utente eseguirà il tuo codice.

11 LFB Jun 16 2018 at 21:18

Questi metodi sono piuttosto stigmatizzati, quindi prendere il comando di Vityata e Jeeped per il gusto di tracciare una linea nella sabbia:

Chiamata .Activate, .Select, Selection, ActiveSomethingi metodi / proprietà

Fondamentalmente perché vengono chiamati principalmente per gestire l'input dell'utente tramite l'interfaccia utente dell'applicazione. Poiché sono i metodi chiamati quando l'utente gestisce gli oggetti tramite l'interfaccia utente, sono quelli registrati dal registratore di macro, ed è per questo che chiamarli è fragile o ridondante per la maggior parte delle situazioni: non è necessario selezionare un oggetto in modo da eseguire un'azione con Selectionsubito dopo.

Tuttavia, questa definizione regola le situazioni in cui sono richiesti:

Quando chiamare .Activate, .Select, .Selection, .ActiveSomethingi metodi / proprietà

Fondamentalmente quando ti aspetti che l' utente finale giochi un ruolo nell'esecuzione.

Se stai sviluppando e ti aspetti che l'utente scelga le istanze dell'oggetto che il tuo codice deve gestire, allora .Selectiono .ActiveObjectsei appropriato.

D'altra parte, .Selecte .Activatesono utili quando puoi dedurre l'azione successiva dell'utente e vuoi che il tuo codice guidi l'utente, possibilmente risparmiandogli un po 'di tempo e clic del mouse. Ad esempio, se il tuo codice ha appena creato un'istanza nuova di zecca di un grafico o ne ha aggiornato uno, l'utente potrebbe volerlo controllare e potresti richiamarlo .Activateo il suo foglio per risparmiare tempo all'utente che lo cerca; oppure, se sai che l'utente dovrà aggiornare alcuni valori dell'intervallo, puoi selezionare quell'intervallo a livello di codice.

6 Eleshar Nov 20 2016 at 22:02

L'utilizzo di IMHO .selectproviene da persone che come me hanno iniziato ad imparare VBA per necessità registrando macro e quindi modificando il codice senza rendersene conto .selecte il successivo selectionè solo un intermediario non necessario.

.select può essere evitato, come molti già postati, lavorando direttamente con gli oggetti già esistenti, il che consente vari riferimenti indiretti come calcolare i e j in modo complesso e quindi modificare la cella (i, j), ecc.

Altrimenti, non c'è nulla di implicitamente sbagliato in .selectse stesso e puoi trovare facilmente degli usi per questo, ad esempio ho un foglio di calcolo che ho popolato con la data, attivo una macro che fa un po 'di magia con esso ed esporta in un formato accettabile su un foglio separato, che , tuttavia, richiede alcuni input manuali finali (imprevedibili) in una cella adiacente. Quindi ecco che arriva il momento per .selectquesto mi fa risparmiare quel movimento e clic aggiuntivi del mouse.

4 FinPro.Online Sep 08 2016 at 13:36

Per evitare di utilizzare il .Selectmetodo, puoi impostare una variabile uguale alla proprietà che desideri.

► Ad esempio, se si desidera inserire il valore, Cell A1è possibile impostare una variabile uguale alla proprietà value di quella cella.

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

► Ad esempio, se si desidera il nome in codice della you could set a variable equal to theproprietà 'Sheet3 Codename` di quel foglio di lavoro.

  • Esempio valTwo = Sheets("Sheet3").Codename
3 PGSystemTester Aug 17 2018 at 03:07

Ho notato che nessuna di queste risposte menziona la proprietà .Offset . Questo può anche essere usato per evitare di usare l' Selectazione quando si manipolano determinate celle, in particolare in riferimento a una cella selezionata (come menzionato dall'OP ActiveCell).

Ecco un paio di esempi.

Assumerò anche che "ActiveCell" sia J4 .

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

  • Questo cambierà la cella J6con un valore di 12
  • Un meno -2 avrebbe fatto riferimento a J2

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

  • Questo copierà la cella in k4a L4.
  • Nota che "0" non è necessario nel parametro offset se non è necessario (, 2)
  • Simile all'esempio precedente sarebbe un meno 1 i4

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

  • Ciò cancellerà i valori in tutte le celle nella colonna k.

Questi non vogliono dire che siano "migliori" delle opzioni di cui sopra, ma solo elencare le alternative.

1 Dominique Oct 30 2020 at 22:55

Come evitare il copia-incolla?

Let's face it: this one appears a lot when recording macros:

Range("X1").Select
Selection.Copy
Range("Y9).Select
Selection.Paste

While the only thing the person wants is:

Range("Y9").Value = Range("X1").Value

Therefore, instead of using copy-paste in VBA macros, I'd advise the following simple approach:

Destination_Range.Value = Source_Range.Value
barneyos Oct 30 2019 at 16:45

Working with the .Parent feature, this example shows how setting only one myRng reference enables dynamic access to the entire environment without any .Select, .Activate, .Activecell, .ActiveWorkbook, .ActiveSheet and so on. (There isn't any generic .Child feature.)

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 the 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
GeoffGriswald Mar 02 2020 at 20:04

The main reason never to use Select or Activesheet is because most people will have at least another couple of workbooks open (sometimes dozens) when they run your macro, and if they click away from your sheet while your macro is running and click on some other book they have open, then the "Activesheet" changes, and the target workbook for an unqualified "Select" command changes as well.

At best, your macro will crash, at worst you might end up writing values or changing cells in the wrong workbook with no way to "Undo" them.

I have a simple golden rule that I follow: Add variables named "wb" and "ws" for a Workbook object and a Worksheet object and always use those to refer to my macro book. If I need to refer to more than one book, or more than one sheet, I add more variables.

For example,

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

The "Set wb = ThisWorkbook" command is absolutely key. "ThisWorkbook" is a special value in Excel, and it means the workbook that your VBA code is currently running from. A very helpful shortcut to set your Workbook variable with.

After you've done that at the top of your Sub, using them could not be simpler, just use them wherever you would use "Selection":

So to change the value of cell "A1" in "Output" to "Hello", instead of:

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

We can now do this:

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

Which is not only much more reliable and less likely to crash if the user is working with multiple spreadsheets; it's also much shorter, quicker and easier to write.

As an added bonus, if you always name your variables "wb" and "ws", you can copy and paste code from one book to another and it will usually work with minimal changes needed, if any.