Come evitare l'uso di Seleziona in Excel VBA


537

Ho sentito molto parlare della comprensibile disgusto dell'uso .Selectin Excel VBA, ma non sono sicuro di come evitarlo. Sto scoprendo che il mio codice sarebbe più riutilizzabile se fossi in grado di utilizzare le variabili anziché le Selectfunzioni. Tuttavia, non sono sicuro di come fare riferimento a cose (come le ActiveCellecc.) Se non utilizzate Select.

Ho trovato questo articolo sulle gamme e questo esempio sui vantaggi di non usare select ma non riesci a trovare nulla su come ?


14
È importante notare che ci sono casi in cui l'uso Selecte / o ActiveSheetecc. Ecc. È assolutamente inevitabile. Ecco un esempio che ho trovato: stackoverflow.com/questions/22796286/…
Rick supporta Monica

9
E ci sono occasioni - modifica dei dati del grafico in ppt con un file Excel sottostante che è uno - in cui sono richiesti l'attivazione o la selezione.
Brettdj,

@brettdj: ecco un esempio recente . Sembra che .Select / .Selectionsia necessario impostare tutti i fogli di una cartella di lavoro sullo stesso valore .
BruceWayne,

3
@bruce dallo stesso QA sembra che non lo sia
chris neilsen il

Risposte:


565

Alcuni esempi di come evitare la selezione

Usa Dimle variabili

Dim rng as Range

Setla variabile nell'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 multi-cella

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 generalmente dovrebbe essere evitato nel codice di produzione.

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

Tutti gli esempi sopra riportati si riferiscono alle celle sul foglio attivo . A meno che tu non voglia specificamente lavorare solo con il foglio attivo, è meglio anche Dimmerare 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 oscurare anche 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 (cattivo) comune è aprire un libro, recuperare alcuni dati e richiuderlo

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

Passa gli intervalli alle tue Subs e Functions come variabili di intervallo

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

È inoltre necessario applicare i 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 di varianti e passare sopra 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.


7
aggiungendo a questa brillante risposta che per funzionare con un intervallo non è necessario conoscerne le dimensioni effettive fintanto che si conosce in alto a sinistra ... ad es. rng1(12, 12)funzionerà anche se rng1 è impostato su [A1:A10]solo.
MikeD,

3
@chrisneilsen Chris, credo che puoi anche usare il prefisso del foglio di lavoro prima della notazione di riferimento della cella abbreviata per salvarti dalla digitazione in Rangequesto modo: ActiveSheet.[a1:a4]o ws.[b6].
Logan Reed,

3
@AndrewWillems ... o 48 volte in questo post, ma chi conta. ☺ ... ma seriamente, è facile da dimenticare quando si lavora con variabili che contengono oggetti. Una variantvariabile non richiede Set fino a quando non gli si assegna un oggetto. Ad esempio, Dim x: x = 1va bene, ma Dim x: x = Sheets("Sheet1")genererà errore 438. Tuttavia solo per confondere / chiarire Dim x: x = Range("A1")sarà non creare un errore. Perché? ... perché ha assegnato il valore dell'oggetto alla variabile, non un riferimento all'oggetto stesso (poiché è l'equivalente di Dim x: x = Range("A1").Value)
ashleedawg

1
@ user3932000 Non sono a conoscenza di uno scenario in cui i nomi dei fogli cambiano automaticamente. Per quanto riguarda i nomi dei file, lo farebbe solo se nella cartella è già presente un file con quel nome. Basta usare Salva ... o codificare il nome del file per salvarlo come stringa. Se non riesci a risolvere questo problema, dovresti fare una domanda separata a riguardo invece di commentare.
TylerH,

1
@ user3932000 che renderebbe un Q interessante. Sono sicuro che ci sono modi per gestirlo .. sei stato così a lungo abbastanza da sapere dirottare un thread di commento su un vecchio Q non è la strada da percorrere
chris neilsen

212

Due motivi principali per cui .Select/ .Activate/ Selection/ Activecell/ Activesheet/ Activeworkbookecc ... dovrebbero essere evitati

  1. Rallenta il tuo codice.
  2. Di solito è la causa principale di 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, dichiarare le 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

17
Questa è una buona risposta, ma ciò che mi manca in questo argomento è quando abbiamo effettivamente bisogno di Attivare. Tutti dicono che è male, ma nessuno spiega i casi in cui ha senso usarlo. Ad esempio, stavo lavorando con 2 cartelle di lavoro e non potevo avviare una macro su una delle cartelle di lavoro senza prima attivarla. Potresti elaborare un po 'forse? Inoltre, se per esempio non attivo i fogli durante la copia di un intervallo da un foglio all'altro, quando eseguo il programma, sembra attivare i rispettivi fogli in modo implicito, implicitamente.
user3032689

1
Trovo che a volte potresti dover prima attivare un foglio se devi incollare o filtrare i dati su di esso. Direi che è meglio evitare di attivare il più possibile, ma ci sono casi in cui è necessario farlo. Quindi continua ad attivare e selezionare al minimo assoluto secondo la risposta sopra.
Nick,

7
penso che il punto non sia evitare completamente di usarli, ma il più possibile. se vuoi salvare una cartella di lavoro, in modo che quando qualcuno la apre in una determinata cella in un determinato foglio sia selezionata, allora devi selezionare quel foglio e quella cella. copia / incolla è un cattivo esempio, almeno nel caso di valori, può essere fatto più velocemente con un codice comeSheets(2).[C10:D12].Value = Sheets(1).[A1:B3].Value
robotik,

1
@Nick Non è necessario attivare i fogli per incollarli o filtrarli. Utilizzare l'oggetto foglio nei comandi incolla o filtro. Diventa più facile mentre impari il modello a oggetti di Excel attraverso la pratica. Credo che l'unica volta che utilizzo. Activate sia quando creo un nuovo foglio, ma voglio che il foglio originale appaia al termine del codice.
phrebh,

3
@phrebh Non è necessario utilizzare .Activateper passare al foglio originale, basta usareApplication.Goto
GMalc

88

Un piccolo punto di enfasi aggiungerò a tutte le eccellenti risposte fornite sopra:

Probabilmente la cosa più grande che puoi fare per evitare di usare Select è, per quanto possibile, usare intervalli nominali (combinati con nomi di variabili significative) nel tuo codice VBA . Questo punto è stato menzionato sopra, ma è stato leggermente sopraffatto; tuttavia, merita un'attenzione speciale.

Qui ci sono un paio di ragioni in più per fare un uso liberale delle gamme nominate, anche se sono sicuro che potrei pensare 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 gli intervalli denominati Monthse MonthlySalescontengono e cosa sta facendo la procedura.

Perché questo è importante? In parte perché è più facile per le altre persone capirlo, ma anche se sei l'unica persona che vedrà o userà mai il tuo codice, dovresti comunque usare intervalli nominali e nomi di variabili buone perché dimenticherai cosa volevi fare con esso un anno dopo, e perderai 30 minuti solo a capire cosa sta facendo il tuo codice.

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

Considera, se l'esempio sopra 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à bene, fino a quando tu o un futuro utente non deciderete "Accidenti, penso che aggiungerò una nuova colonna con l'anno in Colonna A!" O inserirò una colonna di spesa tra i mesi e colonne di vendita o aggiungi un'intestazione a ciascuna colonna. Ora il tuo codice è rotto. E poiché hai usato nomi di variabili terribili, ci vorrà molto più tempo per capire come risolverlo di quanto dovrebbe impiegare.

Se avevi usato gli intervalli nominati per cominciare, le colonne Monthse Salespotrebbero essere spostate in tutto ciò che desideri e il tuo codice continuerà a funzionare bene.


6
Il dibattito sul fatto che le gamme nominate siano buone o cattive nella progettazione di fogli di calcolo continua - Sono fermamente nel no camp. Nella mia esperienza aumentano gli errori (per utenti standard che non hanno bisogno di codice).
Brettdj,


12
Sono d'accordo con la tua filosofia di sviluppo; comunque penso che il giornale sia una sciocchezza. Parla di come i nomi di intervallo possono confondere i novizi che stanno eseguendo il debug di fogli di calcolo, ma chiunque usi i novizi per guardare fogli di calcolo complessi ottiene ciò che meritano! Lavoravo per un'azienda che ha esaminato i fogli di calcolo finanziari e posso dirti che non è il tipo di lavoro che dai a un principiante.
DecanoOC

8
Non c'è dibattito significativo. Chiunque si opponga a nomi definiti non ha avuto il tempo di comprendere appieno le loro ramificazioni. Le formule nominate possono essere il singolo costrutto più profondo e utile in tutto Excel.
Excel Hero

10
@brettdj: la tua citazione è corretta, ma hai dimenticato di dire che è seguita da sei frasi "Tranne ...". Uno di questi è: " Tranne un sostituto dei riferimenti di cella nella codifica macro Utilizzare sempre i nomi Excel come sostituto dei riferimenti di cella durante la costruzione di macro. Questo per evitare errori derivanti dall'inserimento di righe o colonne aggiuntive per cui la codifica macro non è più punta ai dati di origine previsti. "
Marcus Mangelsdorf,

47

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

Riceverai .select e .activate ogni volta che registri macro e riutilizzale. Quando selezioni una cella o un foglio, la rendi attiva. Da quel momento in poi ogni volta che usi riferimenti non qualificati come Range.Valueloro usano semplicemente la cella e il foglio attivi. Questo può anche essere problematico se non guardi dove si trova il tuo codice o se un utente fa clic sulla cartella di lavoro.

Quindi, puoi eliminare questi problemi facendo direttamente riferimento 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"

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


33

"... e sto scoprendo che il mio codice sarebbe più riutilizzabile se fossi in grado di utilizzare le variabili anziché le funzioni Seleziona."

Anche se non riesco a pensare ad altro che a una manciata isolata di situazioni in cui .Selectsarebbe una scelta migliore del riferimento diretto alle cellule, vorrei sollevare la difesa Selectione sottolineare che non dovrebbe essere eliminato per gli stessi motivi che .Selectdovrebbero essere evitati.

Ci sono momenti in cui le sub-routine macro brevi e veloci assegnate alle combinazioni di tasti di scelta rapida disponibili con il tocco di un paio di tasti fanno risparmiare molto tempo. Essere in grado di selezionare un gruppo di celle per attuare il codice operativo su meraviglie funziona quando si tratta di dati tascabili che non sono conformi a un formato di dati a livello di foglio di lavoro. Allo stesso modo in cui è possibile selezionare un gruppo di celle e applicare un cambio di formato, selezionare un gruppo di celle per eseguire un codice macro speciale può far risparmiare molto tempo.

Esempi di sub framework basato 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 di 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, non Selectionma volevo rimuovere qualsiasi idea sbagliata che i programmatori VBA principianti potrebbero dedurre.)


13
Selectionpuò essere qualsiasi cosa nel foglio di lavoro, quindi potrebbe anche testare prima il tipo di oggetto prima di assegnarlo a una variabile poiché lo hai dichiarato esplicitamente come Range.
L42,

29

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 la prima selezione.

Dipende davvero da cosa stai cercando di fare. Comunque un semplice esempio potrebbe essere utile. Supponiamo che tu voglia impostare il valore della cella attiva su "pippo". Usando ActiveCell scriverai qualcosa del genere:

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

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

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

Usando Ranges puoi scrivere una macro più generica che può essere usata per impostare il valore di ogni cella che vuoi 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

Spero che questo aiuti a chiarire un po 'le cose.


1
Grazie per l'eccellente risposta così rapidamente. Ciò significa che se normalmente aggiungessi celle all'intervallo, denominassi l'intervallo e lo ripetessi, dovrei passare direttamente alla creazione di un array?
BiGXERO,

Non sono sicuro di capire cosa intendi, ma puoi creare un intervallo con una singola istruzione (ad esempio intervallo ("B5: C14")) e puoi persino impostarne il valore in una volta (se deve essere lo stesso per ogni cella nell'intervallo), ad es. Range ("B5: C14"). Valore = "abc"
Francesco Baruchelli,

29

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

Ecco come evitare Selecte Activatenei seguenti casi:


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

Da (codice generato con il 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 si desidera 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")

Usando intervalli nominali di fantasia

Puoi accedervi con [], il che è davvero bello, rispetto all'altro. 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 al seguente:

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

Non copiare valori, ma prenderli

Di solito, se sei disposto a farlo 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 . Ogni volta che copi gli intervalli, a volte il foglio di lavoro non viene referenziato e quindi VBA considera il foglio sbagliato come il foglio di lavoro attivo.

'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'uso .Activateed .Selectè quando vuoi assicurarti che un foglio di lavoro specifico sia selezionato per motivi visivi. Ad esempio, Excel si aprirà sempre con il foglio di lavoro di copertina selezionato per primo, ignorando quale era il foglio attivo quando il file veniva chiuso.

Quindi, qualcosa come il codice qui sotto è assolutamente OK:

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

Puoi usare Application.Goto invece di Worksheets.Activate. Un po 'meno rischioso.
Geoff Griswald,

1
Risposta tardiva FYI - un esempio piacevole e in qualche modo inaspettato per un bisogno .Select- così come il mio lavoro intorno - può essere trovato su Come scrivere informazioni identiche a tutti i fogli - @Vityata :)
TM

1
@TM - in effetti è un esempio interessante e probabilmente risparmia alcuni millisecondi per oltre 100 fogli di lavoro, ma probabilmente lo scoraggerei, se lo vedessi da qualche parte. Ad ogni modo, la Selezione non è scritta esplicitamente ma è un risultato di .FillAcrossSheets, quindi questo è da qualche parte nel mezzo (almeno nella mia idea sulla tassonomia VBA)
Vityata

17

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)

Poiché gli utenti finali faranno sempre clic sui pulsanti e non appena lo stato attivo verrà rimosso dalla cartella di lavoro, il codice vorrà funzionare, quindi le cose andranno completamente male.

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 esegue il tuo codice.


7
Sai, anche i nomi dei fogli di lavoro possono cambiare. Utilizzare invece nomi in codice.
Rick supporta Monica il

I nomi dei fogli di lavoro possono cambiare, certo. Ma non sono d'accordo sul fatto che dovresti complicare troppo il tuo codice per cercare di mitigarlo. Se un utente cambia il nome di un foglio e la sua macro smette di funzionare, è su di loro. In genere suppongo solo che i nomi dei fogli di lavoro saranno gli stessi. Per macro particolarmente critiche, eseguo un piccolo controllo pre-volo prima di lanciarlo nella macro corretta, che controlla solo per accertarmi che tutti i fogli che si aspetta di trovare siano effettivamente lì e, se mancano, avvisa l'utente quale.
Geoff Griswald

10

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

Perché non chiamare .Activate, .Select, Selection, ActiveSomethingi metodi / proprietà

Fondamentalmente perché sono chiamati principalmente per gestire l'input dell'utente attraverso l'interfaccia utente dell'applicazione. Poiché sono i metodi chiamati quando l'utente gestisce gli oggetti attraverso 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 Selectionsubito dopo.

Tuttavia, questa definizione risolve le situazioni in cui sono richieste:

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

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

Se stai sviluppando e ti aspetti che l'utente scelga le istanze dell'oggetto da gestire per il tuo codice, allora .Selectiono .ActiveObjectsono appropriate.

D'altra parte, .Selecte .Activatesono utili quando puoi dedurre la prossima azione dell'utente e vuoi che il tuo codice guidi l'utente, risparmiandogli un po 'di tempo e clic del mouse. Ad esempio, se il tuo codice ha appena creato una nuovissima istanza di un grafico o aggiornata, l'utente potrebbe voler verificarlo e potresti chiamarlo .Activateo il suo foglio per risparmiare all'utente il tempo di cercarlo; o se sai che l'utente dovrà aggiornare alcuni valori di intervallo, puoi selezionare programmaticamente quell'intervallo.


6

L'uso di IMHO .selectproviene da persone, che come me hanno iniziato ad imparare VBA per necessità attraverso la registrazione di macro e quindi la modifica del codice senza rendersene conto .selecte il successivo selectionè solo un inutile intermediario.

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

Altrimenti, non c'è nulla di implicitamente sbagliato in .selectse stesso e puoi trovare facilmente gli usi per questo, ad esempio ho un foglio di calcolo che popolo con la data, attivo 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 .selectche mi fa risparmiare quel movimento e un clic aggiuntivi del mouse.


2
Mentre hai ragione, c'è almeno una cosa implicitamente sbagliata con select: è lento. Davvero molto lento rispetto a tutto ciò che accade in una macro.
Vacip

4

Risposta rapida:

Per evitare di utilizzare il .Selectmetodo è possibile impostare una variabile uguale alla proprietà desiderata.

► 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 di 'Foglio3`, è possibile impostare una variabile uguale alla proprietà del nome in codice di quel foglio di lavoro.

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

Spero che aiuti. Fatemi sapere se avete domande.


3

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 menziona l'OP ActiveCell).

Ecco un paio di esempi.

Presumo anche che "ActiveCell" sia J4 .

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

  • Questo cambierà la cella J6in 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.
  • Si noti che "0" non è necessario nel parametro offset se non necessario (, 2)
  • Simile all'esempio precedente sarebbe meno 1 i4

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

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

Questi non devono dire che sono "migliori" delle opzioni sopra, ma elencano solo le alternative.


0

Lavorare con la funzione .Parent. Questo esempio mostra come l'impostazione di un solo riferimento myRng consenta l'accesso dinamico all'intero ambiente senza .Select, .Activate, .Activecell, .ActiveWorkbook, .ActiveSheet e così via. (Non c'è genere. Caratteristica per bambini)

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

Molto bello, ma non sono sicuro di cosa abbia a che fare con la domanda sui PO. Non hai bisogno di "Parent" per lavorare in VBA senza usare Select o ActiveSheet
Geoff Griswald

0

Il motivo principale per non usare Select o Activesheet è perché la maggior parte delle persone avrà almeno un'altra coppia di cartelle di lavoro aperte (a volte dozzine) quando eseguono la macro e se fanno clic lontano dal foglio mentre la macro è in esecuzione e fanno clic su un altro libro aperto, quindi il "Foglio attivo" cambia e cambia anche la cartella di lavoro di destinazione per un comando "Seleziona" non qualificato.

Nella migliore delle ipotesi, la tua macro andrà in crash, nel peggiore dei casi potresti finire per scrivere valori o cambiare celle nella cartella di lavoro sbagliata senza un modo per "annullarli".

Ho una semplice regola d'oro che seguo: aggiungi le variabili denominate "wb" e "ws" per un oggetto Cartella di lavoro e un oggetto Foglio di lavoro e utilizzale sempre per fare riferimento al mio libro macro. Se devo fare riferimento a più di un libro o a più di un foglio, aggiungo più variabili.

per esempio

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

Il comando "Set wb = ThisWorkbook" è assolutamente chiave. "ThisWorkbook" è un valore speciale in Excel e indica la cartella di lavoro da cui il tuo codice VBA è attualmente in esecuzione . Un collegamento molto utile per impostare la variabile della cartella di lavoro.

Dopo averlo fatto nella parte superiore del tuo Sottomarino, usarli non potrebbe essere più semplice, basta usarli ovunque tu utilizzi "Selezione":

Quindi, per modificare il valore della cella "A1" in "Output" in "Hello", anziché:

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

Ora possiamo fare questo:

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

Che non è solo molto più affidabile e meno soggetto a crash se l'utente sta lavorando con più fogli di calcolo, è anche molto più breve, veloce e facile da scrivere.

Come bonus aggiuntivo, se dai sempre il nome alle tue variabili "wb" e "ws", puoi copiare e incollare il codice da un libro all'altro e di solito funzionerà con eventuali modifiche minime necessarie.


1
Non il mio downvote, ma non sono sicuro che ciò aggiunga qualcosa di nuovo a ciò che è già stato proposto nelle risposte esistenti.
BigBen

Sì, la mia risposta è leggermente ridondante, ma le altre risposte sono troppo lunghe, contengono troppe cose superflue e nessuno ha menzionato l'uso di ThisWorkbook per impostare in anticipo la variabile del foglio di lavoro. Questo è qualcosa che, se qualcuno mi avesse mostrato la prima volta che ho immerso un dito del piede in VBA, avrei trovato incredibilmente utile. Altri hanno menzionato l'uso di una variabile del foglio di lavoro, ma non spiegano molto bene perché, e non offrono un esempio di codice con e senza l'utilizzo delle variabili del foglio di lavoro e della cartella di lavoro.
Geoff Griswald

Ma la risposta accettata discute decisamente ThisWorkbook... Non sono sicuro che il tuo commento sia accurato.
BigBen

Lo fa, non ti sbagli. Ma non nel contesto del suo utilizzo per impostare una variabile della cartella di lavoro e dell'utilizzo di quella variabile della cartella di lavoro in futuro o dell'utilizzo di quella variabile della cartella di lavoro per impostare una variabile del foglio di lavoro, come sto suggerendo. La mia risposta è più breve, più semplice e più accessibile per i principianti rispetto alla risposta accettata.
Geoff Griswald

-3

Questo è un esempio che cancellerà il contenuto della cella "A1" (o più se il tipo di selezione è xllastcell, ecc.). Tutto fatto senza dover selezionare le celle.

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

Spero che questo aiuti qualcuno.


1
No scusa. Non è quello che hai fatto lì. Quello che hai effettivamente fatto è selezionare la cella "A1" usando il comando "Application.GoTo", che non è diverso dall'usare davvero "Seleziona", quindi ha usato i clearcontents sulla tua selezione. il modo per farlo senza selezionare le celle sarebbe Workbook(WorkbookName).Worksheets(WorksheetName).Range("A1").ClearContentsche è una riga e non due, e funziona effettivamente senza selezionare le celle.
Geoff Griswald
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.