Comment éviter d'utiliser Select dans Excel VBA


537

J'ai beaucoup entendu parler de l'horreur compréhensible de l'utilisation .Selectdans Excel VBA, mais je ne sais pas comment éviter de l'utiliser. Je trouve que mon code serait plus réutilisable si je pouvais utiliser des variables au lieu de Selectfonctions. Cependant, je ne sais pas comment faire référence à des choses (comme les ActiveCelletc.) si je ne les utilise pas Select.

J'ai trouvé cet article sur les gammes et cet exemple sur les avantages de ne pas utiliser select mais je ne trouve rien sur la façon dont ?


14
Il est important de noter qu'il y a des cas où l'utilisation Selectet / ou ActiveSheetetc etc est complètement inévitable. Voici un exemple que j'ai trouvé: stackoverflow.com/questions/22796286/…
Rick prend en charge Monica

9
Et il y a des occasions - l'édition de données de graphique en ppt avec un fichier excel sous-jacent en étant un - où activer ou sélectionner sont requis.
brettdj

@brettdj - voici un exemple récent . Pour définir toutes les feuilles d'un classeur à la même valeur, il semble .Select / .Selectionnécessaire.
BruceWayne

Réponses:


565

Quelques exemples pour éviter de sélectionner

Utiliser Dimdes variables 'd

Dim rng as Range

Setla variable à la plage requise. Il existe de nombreuses façons de faire référence à une plage unicellulaire

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

ou une gamme multicellulaire

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)

Vous pouvez utiliser le raccourci vers la Evaluateméthode, mais cela est moins efficace et devrait généralement être évité dans le code de production.

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

Tous les exemples ci-dessus se réfèrent aux cellules de la feuille active . À moins que vous ne souhaitiez spécifiquement travailler qu'avec la feuille active, il est préférable de réduire également une Worksheetvariable

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

Si vous ne voulez travailler avec la ActiveSheet, pour plus de clarté , il est préférable d'être explicite. Mais attention, certaines Worksheetméthodes modifient la feuille active.

Set rng = ActiveSheet.Range("A1")

Encore une fois, cela fait référence au classeur actif . À moins que vous ne vouliez spécifiquement travailler qu'avec ActiveWorkbookou ThisWorkbook, il est préférable de réduire également une Workbookvariable.

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

Si vous ne voulez travailler avec la ActiveWorkbook, pour plus de clarté , il est préférable d'être explicite. Mais attention, de nombreuses WorkBookméthodes modifient le livre actif.

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

Vous pouvez également utiliser l' ThisWorkbookobjet pour faire référence au livre contenant le code en cours d'exécution.

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

Un (mauvais) morceau de code courant consiste à ouvrir un livre, à obtenir des données puis à refermer

C'est mauvais:

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

Et ce serait mieux comme:

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

Passez des plages à vos Subs et Functions en tant que variables de plage

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

Vous devez également appliquer des méthodes (telles que Findet Copy) aux variables

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

Si vous effectuez une boucle sur une plage de cellules, il est souvent préférable (plus rapide) de copier d'abord les valeurs de la plage dans un tableau de variantes et de boucler sur celle-ci.

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

Ceci est un petit avant-goût de ce qui est possible.


7
ajoutant à cette brillante réponse que pour travailler avec une plage, vous n'avez pas besoin de connaître sa taille réelle tant que vous connaissez le rng1(12, 12)coin supérieur gauche ... par exemple, cela fonctionnera même si rng1 a été défini sur [A1:A10]seulement.
MikeD

3
@chrisneilsen Chris, je crois que vous pouvez également utiliser le préfixe de feuille de calcul avant la notation de référence de cellule abrégée pour vous éviter de taper Rangecomme ceci: ActiveSheet.[a1:a4]ou ws.[b6].
Logan Reed

3
@AndrewWillems ... ou 48 fois dans ce post, mais qui compte. ☺ ... mais sérieusement, c'est facile à oublier lorsque l'on travaille avec des variables contenant des objets. Une variantvariable ne nécessite pas Set jusqu'à ce que vous lui affectiez un objet. Par exemple, Dim x: x = 1est correct, mais Dim x: x = Sheets("Sheet1")génère Erreur 438. Cependant , juste Confondre / clarifier Dim x: x = Range("A1")va pas créer une erreur. Pourquoi? ... parce que la valeur de l'objet est assignée à la variable, pas une référence à l'objet lui-même (car c'est l'équivalent de Dim x: x = Range("A1").Value)
ashleedawg

1
@ user3932000 Je ne suis pas au courant d'un scénario où les noms de feuilles changent automatiquement. Quant aux noms de fichiers, il ne ferait cela que s'il y a déjà un fichier de ce nom dans le dossier. Utilisez simplement Enregistrer ... ou codez en dur le nom du fichier pour l'enregistrer sous forme de chaîne. Si vous ne pouvez pas résoudre ce problème, vous devez poser une question distincte à ce sujet au lieu de commenter.
TylerH

1
@ user3932000 qui ferait un Q. intéressant. Je suis sûr qu'il existe des moyens de le gérer .. vous avez été sur SO assez longtemps pour savoir détourner un fil de commentaire sur un vieux Q n'est pas la voie à suivre
chris neilsen

212

Deux raisons principales pour lesquelles .Select/ .Activate/ Selection/ Activecell/ Activesheet/ Activeworkbooketc ... devraient être évitées

  1. Cela ralentit votre code.
  2. Il s'agit généralement de la principale cause d'erreurs d'exécution.

Comment l'évitons-nous?

1) Travailler directement avec les objets concernés

Considérez ce code

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

Ce code peut également s'écrire

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

2) Si nécessaire, déclarez vos variables. Le même code ci-dessus peut être écrit comme

Dim ws as worksheet

Set ws = Sheets("Sheet1")

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

17
C'est une bonne réponse, mais ce qui me manque sur ce sujet, c'est quand nous avons réellement besoin d'Activer. Tout le monde dit que c'est mauvais, mais personne n'explique aucun cas où il est logique de l'utiliser. Par exemple, je travaillais avec 2 classeurs et je ne pouvais pas démarrer une macro sur l'un des classeurs sans l'activer au préalable. Pourriez-vous élaborer un peu peut-être? De plus, si par exemple je n'active pas les feuilles lors de la copie d'une plage d'une feuille à une autre, lorsque j'exécute le programme, il semble de toute façon activer les feuilles respectives, implicitement.
user3032689

1
Je trouve que vous devrez peut-être parfois activer une feuille en premier si vous devez coller ou filtrer des données dessus. Je dirais qu'il vaut mieux éviter de l'activer autant que possible, mais il y a des cas où vous devez le faire. Continuez donc à activer et à sélectionner au minimum absolu selon la réponse ci-dessus.
Nick

7
Je pense que le but n'est pas d'éviter complètement de les utiliser, mais autant que possible. si vous souhaitez enregistrer un classeur, de sorte que lorsque quelqu'un l'ouvre, une certaine cellule dans une certaine feuille est sélectionnée, vous devez sélectionner cette feuille et cette cellule. copier / coller est un mauvais exemple, au moins dans le cas des valeurs, cela peut être fait plus rapidement par un code tel queSheets(2).[C10:D12].Value = Sheets(1).[A1:B3].Value
robotik

1
@Nick Vous n'avez pas besoin d'activer les feuilles pour les coller ou les filtrer. Utilisez l'objet feuille dans vos commandes de collage ou de filtrage. Cela devient plus facile à mesure que vous apprenez le modèle d'objet Excel par la pratique. Je crois que la seule fois où j'utilise .Activer, c'est quand je crée une nouvelle feuille, mais je veux que la feuille d'origine apparaisse lorsque le code est terminé.
phrebh

3
@phrebh Vous n'avez pas besoin d'utiliser .Activatepour passer à la feuille d'origine, utilisez simplementApplication.Goto
GMalc

88

Un petit point important que j'ajouterai à toutes les excellentes réponses données ci-dessus:

La plus grande chose que vous puissiez faire pour éviter d'utiliser Select est probablement d'utiliser autant que possible des plages nommées (combinées avec des noms de variables significatifs) dans votre code VBA . Ce point a été mentionné ci-dessus, mais passé sous silence un peu; cependant, il mérite une attention particulière.

Voici quelques raisons supplémentaires pour utiliser librement les plages nommées, même si je suis sûr que je pourrais y penser davantage.

Les plages nommées facilitent la lecture et la compréhension de votre code.

Exemple:

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

Il est assez évident de savoir ce Monthsque MonthlySalescontiennent les plages nommées et ce que fait la procédure.

Pourquoi est-ce important? En partie parce qu'il est plus facile pour les autres de le comprendre, mais même si vous êtes la seule personne qui verra ou utilisera votre code, vous devez toujours utiliser des plages nommées et de bons noms de variables car VOUS OUBLIEREZ ce que vous vouliez faire avec. un an plus tard, et vous perdrez 30 minutes à découvrir ce que fait votre code.

Les plages nommées garantissent que vos macros ne se cassent pas lorsque (pas si!) La configuration de la feuille de calcul change.

Considérez, si l'exemple ci-dessus avait été écrit comme ceci:

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

Ce code fonctionnera très bien au début - c'est jusqu'à ce que vous ou un futur utilisateur décide "gee wiz, je pense que je vais ajouter une nouvelle colonne avec l'année dans la colonne A!", Ou mettre une colonne de dépenses entre les mois et colonnes de vente ou ajoutez un en-tête à chaque colonne. Maintenant, votre code est cassé. Et parce que vous avez utilisé des noms de variables terribles, il vous faudra beaucoup plus de temps pour comprendre comment le réparer qu'il ne le faudrait.

Si vous aviez utilisé des plages nommées pour commencer, les colonnes Monthset Salespourraient être déplacées tout ce que vous voulez, et votre code continuera de fonctionner correctement.


6
Le débat sur la question de savoir si les plages nommées sont une bonne ou une mauvaise conception de feuille de calcul continue - je suis fermement dans le camp du non. D'après mon expérience, ils augmentent les erreurs (pour les utilisateurs standard qui n'ont pas besoin de code).
brettdj


12
Je suis d'accord avec votre philosophie de développement; mais je pense que le papier est un non-sens. Il explique comment les noms de plage peuvent confondre les novices qui déboguent des feuilles de calcul, mais quiconque utilise des novices pour consulter des feuilles de calcul complexes obtient ce qu'il mérite! J'avais l'habitude de travailler pour une entreprise qui révisait des feuilles de calcul financières, et je peux vous dire que ce n'est pas le genre de travail que vous donnez à un novice.
DeanOC

8
Il n'y a pas de débat significatif. Quiconque plaide contre les noms définis n'a pas pris le temps de bien comprendre leurs ramifications. Les formules nommées peuvent être la construction la plus profonde et la plus utile dans Excel.
Excel Hero

10
@brettdj: Votre citation est correcte, mais vous avez oublié de mentionner qu'elle est suivie de six phrases "Sauf ...". L'un d'eux étant: " Sauf en tant que substitut pour les références de cellule dans le codage de macro Utilisez toujours les noms Excel comme substitut pour les références de cellule lors de la construction de macros. Ceci afin d'éviter les erreurs résultant de l'insertion de lignes ou de colonnes supplémentaires selon lesquelles le codage de macro n'est plus pointe vers les données source prévues. "
Marcus Mangelsdorf

47

Je vais donner la réponse courte puisque tout le monde a donné la longue.

Vous obtiendrez .select et .activate chaque fois que vous enregistrez des macros et les réutilisez. Lorsque vous sélectionnez une cellule ou une feuille, cela la rend simplement active. À partir de ce moment, chaque fois que vous utilisez des références non qualifiées, comme Range.Valueelles utilisent simplement la cellule et la feuille actives. Cela peut également être problématique si vous ne regardez pas où votre code est placé ou si un utilisateur clique sur le classeur.

Ainsi, vous pouvez éliminer ces problèmes en référençant directement vos cellules. Ce qui 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)

Ou vous pourriez

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

Il existe différentes combinaisons de ces méthodes, mais ce serait l'idée générale exprimée le plus rapidement possible pour des gens impatients comme moi.


33

"... et je trouve que mon code serait plus réutilisable si je pouvais utiliser des variables au lieu des fonctions Select."

Bien que je ne puisse penser à rien d'autre qu'à une poignée de situations isolées .Selectqui seraient un meilleur choix que le référencement cellulaire direct, je voudrais défendre Selectionet souligner qu'il ne devrait pas être rejeté pour les mêmes raisons qui .Selectdevraient être évitées.

Il y a des moments où avoir des sous-routines de macro courtes et rapides affectées à des combinaisons de touches de raccourci disponibles en appuyant sur quelques touches permet de gagner beaucoup de temps. Être en mesure de sélectionner un groupe de cellules pour appliquer le code opérationnel sur Works fonctionne à merveille lorsqu'il s'agit de données empochées qui ne sont pas conformes à un format de données à l'échelle de la feuille de calcul. De la même manière que vous pouvez sélectionner un groupe de cellules et appliquer un changement de format, la sélection d'un groupe de cellules pour exécuter un code de macro spécial peut être un gain de temps majeur.

Exemples de sous-cadre basé sur la sélection:

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

Le code réel à traiter peut être n'importe quoi, d'une seule ligne à plusieurs modules. J'ai utilisé cette méthode pour lancer des routines de longue durée sur une sélection irrégulière de cellules contenant les noms de fichiers des classeurs externes.

En bref, ne le jetez pas en Selectionraison de son association étroite avec .Selectet ActiveCell. En tant que propriété de feuille de calcul, elle a de nombreuses autres fonctions.

(Oui, je sais que cette question concernait .Select, non, Selectionmais je voulais supprimer toute idée fausse que les codeurs VBA novices pourraient inférer.)


13
Selectionpeut être n'importe quoi dans la feuille de calcul, il pourrait donc aussi bien tester d'abord le type de l'objet avant de l'affecter à une variable puisque vous l'avez explicitement déclaré comme Range.
L42

29

Veuillez noter que dans ce qui suit, je compare l'approche Select (celle que l'OP veut éviter), avec l'approche Range (et c'est la réponse à la question). Alors n'arrêtez pas de lire lorsque vous voyez le premier Select.

Cela dépend vraiment de ce que vous essayez de faire. Quoi qu'il en soit, un exemple simple pourrait être utile. Supposons que vous souhaitiez définir la valeur de la cellule active sur "foo". En utilisant ActiveCell, vous écririez quelque chose comme ceci:

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

Si vous souhaitez l'utiliser pour une cellule qui n'est pas active, par exemple pour "B2", vous devez d'abord la sélectionner, comme ceci:

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

À l'aide des plages, vous pouvez écrire une macro plus générique qui peut être utilisée pour définir la valeur de n'importe quelle cellule de votre choix:

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

Ensuite, vous pouvez réécrire Macro2 comme:

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

Et Macro1 comme:

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

J'espère que cela aide à clarifier un peu les choses.


1
Merci pour l'excellente réponse si rapidement. Cela signifie-t-il que si j'ajoute normalement des cellules à la plage, que je nomme la plage et que j'itère à travers elle, je devrais passer directement à la création d'un tableau?
BiGXERO

Je ne suis pas sûr de comprendre ce que vous voulez dire, mais vous pouvez créer une plage avec une seule instruction (par exemple, Range ("B5: C14")) et vous pouvez même définir sa valeur à la fois (si elle doit être la même pour chaque cellule de la plage), par exemple Plage ("B5: C14"). Valeur = "abc"
Francesco Baruchelli

29

Éviter Selectet Activatec'est le mouvement qui vous rend un peu meilleur développeur VBA. En général, Selectet Activatesont utilisés lorsqu'une macro est enregistrée, la Parentfeuille de calcul ou la plage est donc toujours considérée comme active.

Voici comment vous pouvez éviter Selectet Activatedans les cas suivants:


Ajouter une nouvelle feuille de calcul et copier une cellule dessus:

De (code généré avec l'enregistreur de macros):

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

À:

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

Lorsque vous souhaitez copier une plage entre des feuilles de calcul:

De:

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

À:

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

Utilisation de plages nommées fantaisistes

Vous pouvez y accéder avec [], ce qui est vraiment beau, par rapport à l'inverse. Vérifie toi-même:

Dim Months As Range
Dim MonthlySales As Range

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

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

L'exemple ci-dessus ressemblerait à ceci:

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

Ne pas copier des valeurs, mais les prendre

Habituellement, si vous le souhaitez select, vous copiez probablement quelque chose. Si vous êtes uniquement intéressé par les valeurs, c'est une bonne option pour éviter de sélectionner:

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


Essayez toujours de référencer la feuille de calcul également

C’est probablement l’erreur la plus courante . Chaque fois que vous copiez des plages, la feuille de calcul n'est parfois pas référencée et VBA considère donc la mauvaise feuille comme la feuille 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

Puis-je vraiment jamais utiliser .Selectou .Activatepour quoi que ce soit?

  • Un bon exemple de quand vous pourriez être justifié d'utiliser .Activateet .Selectc'est quand vous voulez vous assurer qu'une feuille de calcul spécifique est sélectionnée pour des raisons visuelles. Par exemple, que votre Excel s'ouvrirait toujours avec la feuille de calcul de couverture sélectionnée en premier, sans tenir compte de la feuille ActiveSheet lorsque le fichier a été fermé.

Ainsi, quelque chose comme le code ci-dessous est absolument OK:

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

Vous pouvez utiliser Application.Goto au lieu de Worksheets.Activate. Un peu moins risqué.
Geoff Griswald

1
Réponse tardive FYI - un exemple agréable et quelque peu inattendu pour un besoin .Select- ainsi que mon travail - peut être trouvé sur Comment écrire des informations identiques sur toutes les feuilles - @Vityata :)
TM

1
@TM - en effet, c'est un exemple intéressant, et économise probablement quelques millisecondes pour plus de 100 feuilles de calcul, mais je le découragerais probablement, si je le vois quelque part. Quoi qu'il en soit, la sélection n'est pas explicitement écrite mais un résultat de .FillAcrossSheets, donc c'est quelque part entre les deux (au moins dans mon idée de la taxonomie VBA)
Vityata

17

Indiquez toujours le classeur, la feuille de calcul et la cellule / plage.

Par exemple:

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

Parce que les utilisateurs finaux cliquent toujours sur les boutons et dès que le focus se déplace hors du classeur, le code veut travailler, puis les choses vont complètement mal.

Et n'utilisez jamais l'index d'un classeur.

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

Vous ne savez pas quels autres classeurs seront ouverts lorsque l'utilisateur exécutera votre code.


7
Le nom des feuilles de calcul peut également changer, vous savez. Utilisez plutôt des noms de code.
Rick soutient Monica

Les noms des feuilles de calcul peuvent changer, bien sûr. Mais je ne suis pas d'accord pour dire que vous devriez trop compliquer votre code pour essayer d'atténuer cela. Si un utilisateur change le nom d'une feuille et que sa macro cesse de fonctionner, c'est sur lui. Je suppose généralement que les noms des feuilles de calcul seront les mêmes. Pour les macros particulièrement critiques, je lance une petite vérification avant le vol avant de me lancer dans la macro proprement dite, qui vérifie simplement que toutes les feuilles qu'il s'attend à trouver sont bien là, et s'il en manque, il en informe l'utilisateur.
Geoff Griswald

10

Ces méthodes sont plutôt stigmatisées, prenant ainsi la tête de @Vityata et @Jeeped pour dessiner une ligne dans le sable:

Pourquoi ne pas appeler .Activate, .Select, Selection, ActiveSomethingméthodes / propriétés

Fondamentalement, car ils sont appelés principalement pour gérer les entrées utilisateur via l'interface utilisateur de l'application. Puisqu'il s'agit des méthodes appelées lorsque l'utilisateur manipule des objets via l'interface utilisateur, ce sont celles enregistrées par le macro-enregistreur, et c'est pourquoi les appeler est soit fragile soit redondant dans la plupart des situations: vous n'avez pas à sélectionner un objet de façon à effectuer une action avec Selectionjuste après.

Cependant, cette définition règle les situations sur lesquelles elles sont appelées:

Quand appeler .Activate, .Select, .Selection, .ActiveSomethingméthodes / propriétés

Fondamentalement, lorsque vous vous attendez à ce que l' utilisateur final joue un rôle dans l'exécution.

Si vous développez et attendez de l'utilisateur qu'il choisisse les instances d'objet à gérer par votre code, alors .Selectionou .ActiveObjectsont appropriées.

D'autre part, .Selectet.Activate sont utiles lorsque vous pouvez déduire la prochaine action de l'utilisateur et que vous souhaitez que votre code guide l'utilisateur, lui permettant éventuellement de gagner du temps et des clics de souris. Par exemple, si votre code vient de créer une toute nouvelle instance d'un graphique ou une mise à jour, l'utilisateur peut vouloir le vérifier, et vous pouvez l'appeler .Activateou sa feuille pour lui faire gagner du temps; ou si vous savez que l'utilisateur devra mettre à jour certaines valeurs de plage, vous pouvez sélectionner cette plage par programmation.


6

L'utilisation .selectà mon humble avis vient de personnes qui, comme moi, ont commencé à apprendre VBA par nécessité en enregistrant des macros, puis en modifiant le code sans s'en rendre compte .selectet par la suite, ce selectionn'est qu'un intermédiaire inutile.

.select peut être évité, comme beaucoup l'ont déjà posté, en travaillant directement avec les objets déjà existants, ce qui permet divers référencements indirects comme calculer i et j de manière complexe puis éditer la cellule (i, j), etc.

Sinon, il n'y a rien de mal implicitement avec .selectlui-même et vous pouvez facilement trouver des utilisations, par exemple, j'ai une feuille de calcul que je remplis avec la date, active la macro qui fait de la magie avec elle et l'exporte dans un format acceptable sur une feuille séparée, qui , cependant, nécessite des entrées manuelles finales (imprévisibles) dans une cellule adjacente. Voici donc le moment .selectqui me permet d'économiser le mouvement et le clic supplémentaires de la souris.


2
Bien que vous ayez raison, il y a au moins une chose implicitement erronée avec select: c'est lent. Très lent en effet par rapport à tout ce qui se passe dans une macro.
vacip

4

Réponse rapide:

Pour éviter d'utiliser la .Selectméthode, vous pouvez définir une variable égale à la propriété souhaitée.

► Par exemple, si vous voulez que la valeur soit dans, Cell A1vous pouvez définir une variable égale à la propriété value de cette cellule.

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

► Par exemple, si vous voulez le nom de code de «Sheet3», vous pouvez définir une variable égale à la propriété de nom de code de cette feuille de calcul.

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

J'espère que ça aide. Faites moi savoir si vous avez des questions.


3

J'ai remarqué qu'aucune de ces réponses ne mentionne la propriété .Offset . Cela peut également être utilisé pour éviter d'utiliser l' Selectaction lors de la manipulation de certaines cellules, en particulier en référence à une cellule sélectionnée (comme l'OP le mentionne ActiveCell).

Voici quelques exemples.

Je suppose également que "ActiveCell" est J4 .

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

  • Cela changera la cellule J6à une valeur de 12
  • Un moins -2 aurait référencé J2

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

  • Cela va copier la cellule k4à L4.
  • Notez que "0" n'est pas nécessaire dans le paramètre offset s'il n'est pas nécessaire (, 2)
  • Similaire à l'exemple précédent, un moins 1 serait i4

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

  • Cela effacera les valeurs dans toutes les cellules de la colonne k.

Cela ne veut pas dire qu'elles sont "meilleures" que les options ci-dessus, mais simplement une liste d'alternatives.


0

Travailler avec la fonctionnalité .Parent. Cet exemple montre comment la définition d'une seule référence myRng permet un accès dynamique à l'ensemble de l'environnement sans .Select, .Activate, .Activecell, .ActiveWorkbook, .ActiveSheet, etc. (Il n'y a pas de fonctionnalité générique .Child)

Sub ShowParents()
    Dim myRng As Range
    Set myRng = ActiveCell
    Debug.Print myRng.Address                    ' an address of the selected cell
    Debug.Print myRng.Parent.name                ' the name of sheet, where MyRng is in
    Debug.Print myRng.Parent.Parent.name         ' the name of workbook, where MyRng is in
    Debug.Print myRng.Parent.Parent.Parent.name  ' the name of application, where MyRng is in

    ' You may use this feature to set reference to these objects
    Dim mySh    As Worksheet
    Dim myWbk   As Workbook
    Dim myApp   As Application

    Set mySh = myRng.Parent
    Set myWbk = myRng.Parent.Parent
    Set myApp = myRng.Parent.Parent.Parent
    Debug.Print mySh.name, mySh.Cells(10, 1).Value
    Debug.Print myWbk.name, myWbk.Sheets.Count
    Debug.Print myApp.name, myApp.Workbooks.Count

    ' You may use dynamically addressing
    With myRng
        .Copy

       ' pastes in D1 on sheet 2 in the same workbook, where copied cell is
        .Parent.Parent.Sheets(2).Range("D1").PasteSpecial xlValues
    ' or myWbk.Sheets(2).Range("D1").PasteSpecial xlValues

       ' we may dynamically call active application too
        .Parent.Parent.Parent.CutCopyMode = False
    ' or myApp.CutCopyMode = False
    End With
End Sub

Très bien, mais je ne sais pas ce que cela a à voir avec la question des PO. Vous n'avez pas du tout besoin de "Parent" pour travailler dans VBA sans utiliser Select ou ActiveSheet
Geoff Griswald

0

La principale raison de ne jamais utiliser Select ou Activesheet est que la plupart des gens auront au moins un autre couple de classeurs ouverts (parfois des dizaines) lorsqu'ils exécutent votre macro, et s'ils cliquent loin de votre feuille pendant que votre macro s'exécute et cliquent sur un autre livre qu'ils ont ouvert, puis la «feuille active» change, et le classeur cible pour une commande «Sélectionner» non qualifiée change également.

Au mieux, votre macro se bloquera, au pire vous pourriez finir par écrire des valeurs ou changer des cellules dans le mauvais classeur sans aucun moyen de les "annuler".

J'ai une règle d'or simple que je suis: ajouter des variables nommées "wb" et "ws" pour un objet Workbook et un objet Worksheet et toujours les utiliser pour faire référence à mon livre de macro. Si je dois faire référence à plusieurs livres ou plusieurs feuilles, j'ajoute plusieurs variables.

par exemple

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

La commande "Set wb = ThisWorkbook" est absolument essentielle. "ThisWorkbook" est une valeur spéciale dans Excel, et cela signifie le classeur à partir duquel votre code VBA s'exécute actuellement . Un raccourci très utile pour définir votre variable de classeur avec.

Après l'avoir fait en haut de votre Sub, leur utilisation ne pourrait pas être plus simple, il suffit de les utiliser partout où vous utiliseriez "Sélection":

Donc, pour changer la valeur de la cellule "A1" dans "Sortie" en "Bonjour", au lieu de:

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

Nous pouvons maintenant le faire:

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

Ce qui est non seulement beaucoup plus fiable et moins susceptible de se bloquer si l'utilisateur travaille avec plusieurs feuilles de calcul, il est également beaucoup plus court, plus rapide et plus facile à écrire.

En prime, si vous nommez toujours vos variables "wb" et "ws", vous pouvez copier et coller du code d'un livre à un autre et cela fonctionnera généralement avec le moins de changements nécessaires, le cas échéant.


1
Pas mon downvote, mais je ne suis pas sûr que cela ajoute quelque chose de nouveau à ce qui a déjà été proposé dans les réponses existantes.
BigBen

Oui, ma réponse est légèrement redondante, mais les autres réponses sont trop longues, contiennent trop de choses superflues et personne n'a mentionné l'utilisation de ThisWorkbook pour définir votre variable de feuille de calcul à l'avance. C'est quelque chose que, si quelqu'un m'avait montré la première fois que j'avais plongé un orteil dans VBA, j'aurais trouvé incroyablement utile. D'autres ont mentionné l'utilisation d'une variable de feuille de calcul, mais n'expliquent pas vraiment très bien pourquoi, et n'offrent pas d'exemple de code avec et sans utilisation de variables de feuille de calcul et de classeur.
Geoff Griswald

Mais la réponse acceptée discute définitivement ThisWorkbook... Je ne suis pas sûr que votre commentaire soit exact.
BigBen

Oui, vous ne vous trompez pas. Mais pas dans le contexte de l'utiliser pour définir une variable de classeur et d'utiliser cette variable de classeur à l'avenir, ou d'utiliser cette variable de classeur pour définir une variable de feuille de calcul, comme je le suggère. Ma réponse est plus courte, plus simple et plus accessible pour les débutants que la réponse acceptée.
Geoff Griswald

-3

Il s'agit d'un exemple qui effacera le contenu de la cellule "A1" (ou plus si le type de sélection est xllastcell, etc.). Tout cela sans avoir à sélectionner les cellules.

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

J'espère que ça aidera quelqu'un.


1
Non désolé. Ce n'est pas du tout ce que vous avez fait là-bas. Ce que vous avez réellement fait est de sélectionner la cellule "A1" en utilisant la commande "Application.GoTo", qui n'est pas différente de l'utilisation de "Select" vraiment, puis d'utiliser le contenu clair sur votre sélection. la façon de le faire sans sélectionner de cellules serait celle Workbook(WorkbookName).Worksheets(WorksheetName).Range("A1").ClearContentsqui est une ligne et non deux, et fonctionne en fait sans sélectionner les cellules.
Geoff Griswald
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.