Como evitar o uso de Selecionar no Excel VBA


537

Ouvi falar muito sobre a aversão compreensível do uso .Selectno Excel VBA, mas não tenho certeza de como evitar usá-lo. Estou descobrindo que meu código seria mais reutilizável se eu pudesse usar variáveis ​​em vez de Selectfunções. No entanto, não sei como se referir a coisas (como a ActiveCelletc.) se não estiver usando Select.

Encontrei este artigo sobre intervalos e este exemplo sobre os benefícios de não usar o select, mas não consigo encontrar nada sobre como ?


14
É importante observar que há casos em que o uso Selecte / ou ActiveSheetetc etc são completamente inevitáveis. Aqui está um exemplo que eu encontrei: stackoverflow.com/questions/22796286/…
Rick suporta Monica

9
E há ocasiões - editando dados do gráfico em ppt com um arquivo excel subjacente sendo um - onde ativar ou selecionar são necessários.
brettdj

@ brettdj - aqui está um exemplo recente . Para definir todas as folhas de uma pasta de trabalho com o mesmo valor, parece .Select / .Selectionnecessário.
BruceWayne

3
@bruce do mesmo QA parece que não é
chris Neilsen

Respostas:


565

Alguns exemplos de como evitar a seleção

Use Dimvariáveis ​​d

Dim rng as Range

Seta variável para o intervalo necessário. Existem várias maneiras de se referir a um intervalo de célula única

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

ou um intervalo de várias células

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)

Você pode usar o atalho para o Evaluatemétodo, mas isso é menos eficiente e geralmente deve ser evitado no código de produção.

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

Todos os exemplos acima se referem às células na planilha ativa . A menos que você deseje trabalhar apenas especificamente com a planilha ativa, é melhor reduzir também uma Worksheetvariável

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 você não quer trabalhar com o ActiveSheet, para maior clareza, é melhor ser explícito. Mas tome cuidado, pois alguns Worksheetmétodos alteram a planilha ativa.

Set rng = ActiveSheet.Range("A1")

Novamente, isso se refere à pasta de trabalho ativa . A menos que você deseje trabalhar apenas apenas com o ActiveWorkbookou ThisWorkbook, é melhor reduzir também uma Workbookvariável.

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

Se você não quer trabalhar com o ActiveWorkbook, para maior clareza, é melhor ser explícito. Mas tome cuidado, pois muitos WorkBookmétodos alteram o livro ativo.

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

Você também pode usar o ThisWorkbookobjeto para se referir ao livro que contém o código em execução.

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

Um código (ruim) comum é abrir um livro, obter alguns dados e fechar novamente

Isto é mau:

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 seria melhor como:

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

Passagem varia a seus Subs e Functions como variáveis Gama

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

Você também deve aplicar métodos (como Finde Copy) a variáveis

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 você estiver fazendo loop em um intervalo de células, geralmente é melhor (mais rápido) copiar os valores do intervalo para uma matriz variante primeiro e fazer um loop sobre esse

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

Esta é uma pequena amostra do que é possível.


7
adicionando a essa brilhante resposta que, para trabalhar com um intervalo, você não precisa saber seu tamanho real enquanto conhecer a parte superior esquerda ... por exemplo rng1(12, 12), funcionará mesmo que rng1 tenha sido definido [A1:A10]apenas.
MikeD

3
@chrisneilsen Chris, acredito que você também pode usar o prefixo da planilha antes da notação abreviada de referência de célula para evitar que você digite Rangeassim: ActiveSheet.[a1:a4]ou ws.[b6].
Logan Reed

3
@AndrewWillems ... ou 48 vezes neste post, mas quem está contando. ☺ ... mas sério, é fácil esquecer quando se trabalha com variáveis ​​que mantêm objetos. Uma variantvariável não exige Set até você atribuir um objeto a ela. Por exemplo, Dim x: x = 1está tudo bem, mas Dim x: x = Sheets("Sheet1")irá gerar o Erro 438. No entanto, apenas para confundir / esclarecer Dim x: x = Range("A1"), não criará um erro. Por quê? ... porque ele é atribuído o valor do objeto para a variável, não uma referência ao próprio objeto (uma vez que é o equivalente a Dim x: x = Range("A1").Value)
ashleedawg

1
@ user3932000 Não conheço um cenário em que os nomes das folhas sejam alterados automaticamente. Quanto aos nomes de arquivos, isso seria feito apenas se já houver um arquivo com esse nome na pasta. Basta usar Salvar ... ou codificar o nome do arquivo para salvá-lo como uma string. Se você não conseguir resolver esse problema, faça uma pergunta separada sobre ele, em vez de comentar.
TylerH

1
@ user3932000 que daria um Q interessante. Tenho certeza de que existem maneiras de lidar com isso. você está há tempo suficiente para saber que o seqüestro de um tópico de comentário em um Q antigo não é o caminho a seguir
chris neilsen

212

Duas razões principais pelas quais .Select/ .Activate/ Selection/ Activecell/ Activesheet/ Activeworkbooketc ... devem ser evitadas

  1. Retarda o seu código.
  2. Geralmente é a principal causa de erros de tempo de execução.

Como evitamos isso?

1) Trabalhe diretamente com os objetos relevantes

Considere este código

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

Este código também pode ser escrito como

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

2) Se necessário, declare suas variáveis. O mesmo código acima pode ser escrito como

Dim ws as worksheet

Set ws = Sheets("Sheet1")

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

17
Essa é uma boa resposta, mas o que falta neste tópico é quando realmente precisamos do Activate. Todo mundo diz que é ruim, mas ninguém explica casos em que faz sentido usá-lo. Por exemplo, eu estava trabalhando com duas pastas de trabalho e não podia iniciar uma macro em uma das pastas de trabalho sem ativá-la primeiro. Você poderia elaborar um pouco, talvez? Além disso, se por exemplo eu não ativar planilhas ao copiar um intervalo de uma planilha para outra, quando executo o programa, parece ativar as planilhas respectivas de qualquer maneira, implicitamente.
user3032689

1
Acho que às vezes é necessário ativar uma planilha primeiro, se você precisar colar ou filtrar dados nela. Eu diria que é melhor evitar ativar o máximo possível, mas há casos em que você precisa fazer isso. Portanto, continue ativando e selecionando ao mínimo absoluto, conforme a resposta acima.
Nick

7
Eu acho que o objetivo não é evitar completamente usá-los, mas o máximo possível. se você deseja salvar uma pasta de trabalho, para que, quando alguém a abrir, uma determinada célula em uma determinada planilha seja selecionada, você deverá selecionar essa planilha e célula. copiar / colar é um mau exemplo, pelo menos no caso de valores, isso pode ser feito mais rápido, um código comoSheets(2).[C10:D12].Value = Sheets(1).[A1:B3].Value
robotik

1
@ Nick Você não precisa ativar as folhas para colar ou filtrar. Use o objeto de pasta nos comandos de colar ou filtrar. Torna-se mais fácil à medida que você aprende o modelo de objeto do Excel através da prática. Acredito que a única vez que uso .Activate é quando crio uma nova planilha, mas quero que a planilha original apareça quando o código for concluído.
Phrebh

3
@phrebh Você não precisa usar .Activatepara ir para a página original, basta usar #Application.Goto
GMalc 28/02/19

88

Um pequeno ponto de ênfase acrescentarei a todas as excelentes respostas dadas acima:

Provavelmente, a maior coisa que você pode fazer para evitar o uso do Select é, na medida do possível, usar intervalos nomeados (combinados com nomes de variáveis ​​significativos) no seu código VBA . Este ponto foi mencionado acima, mas encoberto um pouco; no entanto, merece atenção especial.

Aqui estão algumas razões adicionais para fazer uso liberal de intervalos nomeados, embora eu tenha certeza de que poderia pensar em mais.

Intervalos nomeados tornam seu código mais fácil de ler e entender.

Exemplo:

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

É bastante óbvio o que os intervalos nomeados Monthse MonthlySalescontêm e o que o procedimento está fazendo.

Por que isso é importante? Parcialmente porque é mais fácil para outras pessoas entendê-lo, mas mesmo se você for a única pessoa que verá ou usará seu código, você ainda deve usar intervalos nomeados e bons nomes de variáveis, porque VOCÊ ESQUECERÁ o que você queria fazer com ele um ano depois, e você perderá 30 minutos apenas para descobrir o que seu código está fazendo.

Os intervalos nomeados garantem que suas macros não sejam interrompidas quando (não se!) A configuração da planilha for alterada.

Considere, se o exemplo acima tivesse sido escrito assim:

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

Esse código funcionará bem no começo - ou seja, até você ou um usuário futuro decidir "gee wiz, acho que vou adicionar uma nova coluna com o ano na coluna A!" Ou colocar uma coluna de despesas entre os meses e colunas de vendas ou adicione um cabeçalho a cada coluna. Agora, seu código está quebrado. E como você usou nomes terríveis de variáveis, levará muito mais tempo para descobrir como corrigi-lo do que deveria.

Se você usou intervalos nomeados para começar, as colunas Monthse Salespodem ser movidas à sua volta, e seu código continuará funcionando perfeitamente.


6
O debate sobre se os intervalos nomeados são bons ou ruins para o design da planilha continua - eu estou firmemente no campo sem. Na minha experiência, eles aumentam os erros (para usuários padrão que não precisam de código).
27615 Brettdj


12
Eu concordo com sua filosofia de desenvolvimento; no entanto, acho que o papel não faz sentido. Ele fala sobre como os nomes dos intervalos podem confundir os iniciantes que estão depurando planilhas, mas quem usa os novatos para examinar planilhas complexas recebe o que merece! Eu trabalhava para uma empresa que revisava planilhas financeiras e posso dizer que esse não é o tipo de trabalho que você dá a um novato.
DeanOC

8
Não há debate significativo. Quem argumenta contra nomes definidos não teve tempo para entender completamente suas ramificações. As fórmulas nomeadas podem ser a construção mais profunda e útil em todo o Excel.
Excel Hero

10
@brettdj: Sua citação está correta, mas você esqueceu de mencionar que é seguida por seis frases "Exceto ...". Um deles é: " Exceto como um substituto para referências de células na codificação de macros Sempre use nomes do Excel como substitutos para referências de células ao construir macros. Isso evita erros decorrentes da inserção de linhas ou colunas adicionais nas quais a macro de codificação não é mais aponta para os dados de origem pretendidos ".
Marcus Mangelsdorf

47

Vou dar a resposta curta, já que todo mundo deu a longa.

Você obterá .selecione e .ativará sempre que gravar macros e reutilizá-las. Quando você seleciona uma célula ou planilha, ela apenas a ativa. A partir desse momento, sempre que você usar referências não qualificadas, como se Range.Valueelas apenas utilizassem a célula e a folha ativas. Isso também pode ser problemático se você não observar onde seu código está colocado ou se um usuário clicar na pasta de trabalho.

Portanto, você pode eliminar esses problemas referenciando diretamente suas células. Qual é:

'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 você poderia

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

Existem várias combinações desses métodos, mas essa seria a ideia geral expressa o mais breve possível para pessoas impacientes como eu.


33

"... e estou descobrindo que meu código seria mais reutilizável se eu pudesse usar variáveis ​​em vez de funções Select."

Embora eu não consiga pensar em mais do que um punhado isolado de situações em .Selectque seria uma escolha melhor do que o referenciamento direto a células, levanto-me à defesa Selectione aponto que ele não deve ser jogado fora pelas mesmas razões que .Selectdevem ser evitadas.

Há momentos em que sub-rotinas macro curtas e que economizam tempo atribuídas a combinações de teclas de atalho disponíveis com o toque de duas teclas economiza muito tempo. Ser capaz de selecionar um grupo de células para decretar o código operacional nas maravilhas do trabalho ao lidar com dados em bolso que não estão em conformidade com um formato de dados para toda a planilha. Da mesma maneira que você pode selecionar um grupo de células e aplicar uma alteração de formato, selecionar um grupo de células para executar um código de macro especial pode economizar muito tempo.

Exemplos de subestrutura baseada em seleção:

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

O código real a ser processado pode ser qualquer coisa, desde uma única linha até vários módulos. Eu usei esse método para iniciar rotinas de execução longa em uma seleção irregular de células que contêm os nomes de arquivos de pastas de trabalho externas.

Em resumo, não descarte Selectiondevido à sua estreita associação com .Selecte ActiveCell. Como uma propriedade da planilha, ela tem muitos outros propósitos.

(Sim, eu sei que essa pergunta era sobre .Select, não, Selectionmas eu queria remover quaisquer conceitos errados que os iniciantes em codificadores VBA possam inferir.)


13
Selectionpode ser qualquer coisa na planilha; portanto, teste também o tipo do objeto antes de atribuí-lo a uma variável, pois você o declarou explicitamente Range.
L42

29

Observe que, a seguir, estou comparando a abordagem Select (aquela que o OP deseja evitar), com a abordagem Range (e essa é a resposta para a pergunta). Portanto, não pare de ler quando vir o primeiro Select.

Realmente depende do que você está tentando fazer. De qualquer forma, um exemplo simples pode ser útil. Vamos supor que você deseja definir o valor da célula ativa como "foo". Usando o ActiveCell, você escreveria algo como isto:

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

Se você deseja usá-lo para uma célula que não é a célula ativa, por exemplo, para "B2", você deve selecioná-la primeiro, assim:

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

Usando intervalos, você pode escrever uma macro mais genérica que pode ser usada para definir o valor de qualquer célula desejada para o que você desejar:

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

Em seguida, você pode reescrever Macro2 como:

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

E Macro1 como:

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

Espero que isso ajude a esclarecer um pouco as coisas.


1
Obrigado pela excelente resposta tão rapidamente. Então, isso significa que, se eu normalmente adicionasse células ao intervalo, nomeie o intervalo e itere através dele, devo ir direto para a criação de uma matriz?
BiGXERO

Não sei se entendi o que você quer dizer, mas você pode criar um Range com uma única instrução (por exemplo, Range ("B5: C14")) e pode definir o valor de uma só vez (se for o mesmo para cada célula no intervalo), por exemplo Range ( "B5: C14") Value = "abc".
Francesco Baruchelli

29

Evitar Selecte Activateé a mudança que torna você um desenvolvedor de VBA um pouco melhor. Em geral, Selecte Activatesão usadas quando uma macro é gravada, portanto, a Parentplanilha ou o intervalo é sempre considerado o ativo.

É assim que você pode evitar Selecte Activatenos seguintes casos:


Adicionando uma nova planilha e copiando uma célula nela:

De (código gerado com o gravador de 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

Para:

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 você deseja copiar o intervalo entre as planilhas:

De:

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

Para:

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

Usando intervalos nomeados sofisticados

Você pode acessá-los com [], o que é realmente bonito, comparado ao contrário. Verifique você mesmo:

Dim Months As Range
Dim MonthlySales As Range

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

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

O exemplo acima seria assim:

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

Não copiando valores, mas levando-os

Geralmente, se você estiver disposto select, provavelmente está copiando alguma coisa. Se você está interessado apenas nos valores, esta é uma boa opção para evitar a seleção:

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


Tente sempre fazer referência à planilha também

Este é provavelmente o erro mais comum em . Sempre que você copia intervalos, às vezes a planilha não é referenciada e, portanto, o VBA considera a planilha errada como a 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

Posso realmente nunca usar .Selectou .Activatepara qualquer coisa?

  • Um bom exemplo de quando você pode ser justificado em usar .Activatee .Selecté quando deseja garantir que uma planilha específica seja selecionada por motivos visuais. Por exemplo, seu Excel sempre abriria com a planilha de capa selecionada primeiro, desconsiderando qual era o ActiveSheet quando o arquivo era fechado.

Assim, algo como o código abaixo é absolutamente bom:

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

Você pode usar Application.Goto em vez de Worksheets.Activate. Um pouco menos arriscado.
Geoff Griswald 20/02

1
Resposta tardia A FYI - um exemplo agradável e um tanto inesperado para um necessário .Select- e também para o meu trabalho - pode ser encontrada em Como escrever informações idênticas em todas as folhas - @Vityata :)
TM

1
@TM - na verdade, é um exemplo interessante e provavelmente economiza alguns milissegundos para mais de 100 planilhas, mas eu provavelmente a desencorajaria se a ver em algum lugar. De qualquer forma, a seleção não está explicitamente escrita, mas é um resultado de .FillAcrossSheets, então isso está em algum lugar no meio (pelo menos na minha idéia sobre taxonomia VBA)
Vityata

17

Sempre indique a pasta de trabalho, a planilha e a célula / intervalo.

Por exemplo:

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

Como os usuários finais sempre clicam nos botões e, assim que o foco sai da pasta de trabalho, o código deseja trabalhar e as coisas ficam completamente erradas.

E nunca use o índice de uma pasta de trabalho.

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

Você não sabe quais outras pastas de trabalho serão abertas quando o usuário executar seu código.


7
Os nomes das planilhas também podem mudar, você sabe. Use codinomes.
Rick suporta Monica

Os nomes das planilhas podem mudar, com certeza. Mas eu discordo que você deve complicar demais seu código para tentar mitigar isso. Se um usuário alterar o nome de uma planilha e sua macro parar de funcionar, isso estará neles. Geralmente, apenas suponho que os nomes das planilhas serão os mesmos. Para macros particularmente críticas, eu faço uma pequena verificação antes do voo antes de iniciar a macro adequada, que apenas verifica se todas as planilhas que espera encontrar estão realmente lá e, se houver alguma, notifica o usuário.
Geoff Griswald

10

Esses métodos são bastante estigmatizados, assumindo a liderança do @Vityata e do @Jeeped com o objetivo de desenhar uma linha na areia:

Por que não chamar .Activate, .Select, Selection, ActiveSomethingmétodos / propriedades

Basicamente, porque eles são chamados principalmente para lidar com a entrada do usuário por meio da interface do usuário do aplicativo. Como são os métodos chamados quando o usuário manipula objetos através da interface do usuário, eles são os registrados pelo macro-gravador, e é por isso que chamá-los é quebradiço ou redundante na maioria das situações: você não precisa selecionar um objeto para executar uma ação Selectionlogo em seguida.

No entanto, essa definição resolve situações nas quais eles são chamados:

Quando chamar .Activate, .Select, .Selection, .ActiveSomethingmétodos / propriedades

Basicamente, quando você espera que o usuário final desempenhe um papel na execução.

Se você estiver desenvolvendo e espera que o usuário escolha as instâncias do objeto para o seu código, então .Selectionou .ActiveObjecté apropriado.

Por outro lado, .Selecte .Activatesão de uso quando você pode inferir próxima ação do usuário e você quer que seu código para guiar o usuário, possivelmente salvando-lhe algum tempo e cliques do mouse. Por exemplo, se o seu código acabou de criar uma nova instância de um gráfico ou de uma atualizada, o usuário pode querer dar uma olhada e você pode chamá .Activate-lo ou em sua planilha para economizar o tempo que o procura; ou se você souber que o usuário precisará atualizar alguns valores de intervalo, é possível selecionar programaticamente esse intervalo.


6

O uso do IMHO .selectvem de pessoas que, como eu, começaram a aprender VBA por necessidade, gravando macros e modificando o código sem perceber que o .selectsubsequente selectioné apenas um intermediário desnecessário.

.select pode ser evitado, como muitos já postados, trabalhando diretamente com os objetos já existentes, o que permite várias referências indiretas, como calcular iej de maneira complexa e depois editar a célula (i, j), etc.

Caso contrário, não haverá nada implicitamente errado .selecte você poderá encontrar usos para isso facilmente. Por exemplo, eu tenho uma planilha que preenche com a data, ativa uma macro que faz mágica com ela e a exporta em um formato aceitável em uma folha separada, o que , no entanto, requer algumas entradas manuais finais (imprevisíveis) em uma célula adjacente. Então, aqui chega o momento para .selectque me salve esse movimento adicional do mouse e clique.


2
Enquanto você estiver certo, há pelo menos uma coisa implicitamente errada com o select: é lento. Muito lento, comparado a tudo o que acontece em uma macro.
vacip

4

Resposta rápida:

Para evitar o uso do .Selectmétodo, você pode definir uma variável igual à propriedade que deseja.

► Por exemplo, se você quiser o valor, Cell A1poderá definir uma variável igual à propriedade value dessa célula.

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

► Por exemplo, se você quiser o codinome 'Sheet3', poderá definir uma variável igual à propriedade codename dessa planilha.

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

Espero que ajude. Deixe-me saber se você tiver alguma dúvida.


3

Notei que nenhuma dessas respostas menciona a propriedade .Offset . Isso também pode ser usado para evitar o uso da Selectação ao manipular determinadas células, particularmente em referência a uma célula selecionada (como o OP menciona ActiveCell).

Aqui estão alguns exemplos.

Também assumirei que o "ActiveCell" é J4 .

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

  • Isso mudará a célula J6para um valor de 12
  • Um -2 negativo teria referenciado J2

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

  • Isso copiará a célula k4para L4.
  • Observe que "0" não é necessário no parâmetro offset, se não for necessário (, 2)
  • Semelhante ao exemplo anterior, menos 1 seria i4

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

  • Isso limpará os valores em todas as células na coluna k.

Isso não significa que eles são "melhores" do que as opções acima, mas apenas listando alternativas.


0

Trabalhando com o recurso .Parent. Este exemplo mostra como a configuração de apenas uma referência myRng permite acesso dinâmico a todo o ambiente, sem .Select, .Activate, .Activecell, .ActiveWorkbook, .ActiveSheet e assim por diante. (Não há recurso genérico .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

Muito bom, mas não sei o que isso tem a ver com a questão dos OPs. Você não precisa de "Pai" para trabalhar no VBA sem usar o Select ou o ActiveSheet
Geoff Griswald

0

O principal motivo para nunca usar o Select ou o Activesheet é porque a maioria das pessoas terá pelo menos mais duas pastas de trabalho abertas (às vezes dezenas) quando executam sua macro e se clicarem fora da planilha enquanto a macro estiver em execução e clicar em alguma outra livro aberto, a "Folha de atividades" é alterada e a pasta de trabalho de destino de um comando "Selecionar" não qualificado também é alterada.

Na melhor das hipóteses, sua macro falhará; na pior das hipóteses, você pode acabar escrevendo valores ou alterando células na pasta de trabalho errada, sem nenhuma maneira de "Desfazer".

Eu tenho uma regra de ouro simples que sigo: Adicione variáveis ​​denominadas "wb" e "ws" para um objeto de pasta de trabalho e um objeto de planilha e sempre as use para se referir ao meu livro de macro. Se precisar me referir a mais de um livro ou mais de uma folha, adiciono mais variáveis.

por exemplo

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

O comando "Set wb = ThisWorkbook" é absolutamente essencial. "ThisWorkbook" é um valor especial no Excel e significa a pasta de trabalho em que o seu código VBA está sendo executado . Um atalho muito útil para definir sua variável da pasta de trabalho.

Depois de fazer isso na parte superior do seu Sub, usá-los não poderia ser mais simples, basta usá-los onde quer que você use "Seleção":

Portanto, para alterar o valor da célula "A1" em "Saída" para "Olá", em vez de:

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

Agora podemos fazer isso:

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

O que não é apenas muito mais confiável e menos propenso a travar se o usuário estiver trabalhando com várias planilhas, também é muito mais curto, rápido e fácil de escrever.

Como um bônus adicional, se você sempre nomear suas variáveis ​​"wb" e "ws", poderá copiar e colar o código de um livro para outro e geralmente funcionará com as alterações mínimas necessárias, se houver.


1
Não é meu voto negativo, mas não tenho certeza se isso adiciona algo novo ao que já foi proposto nas respostas existentes.
BigBen 02/03

Sim, minha resposta é um pouco redundante, mas as outras respostas são muito longas, contêm muitas coisas supérfluas e ninguém mencionou o uso do ThisWorkbook para definir antecipadamente a variável da planilha. Isso é algo que, se alguém tivesse me mostrado a primeira vez que mergulhei no VBA, teria achado incrivelmente útil. Outros mencionaram o uso de uma variável de planilha, mas realmente não explicam muito bem o motivo e não oferecem um exemplo de código com e sem o uso de variáveis ​​de planilha e pasta de trabalho.
Geoff Griswald

Mas a resposta aceita definitivamente discute ThisWorkbook... Não tenho certeza se o seu comentário está correto.
BigBen 4/03

Sim, você não está errado. Mas não no contexto de usá-lo para definir uma variável da pasta de trabalho e usar essa variável da pasta de trabalho daqui para frente, ou usar essa variável da pasta de trabalho para definir uma variável da planilha, como estou sugerindo. Minha resposta é mais curta, mais simples e mais acessível para iniciantes do que a resposta aceita.
Geoff Griswald 04/03

-3

Este é um exemplo que limpará o conteúdo da célula "A1" (ou mais, se o tipo de seleção for xllastcell, etc). Tudo feito sem ter que selecionar as células.

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

Espero que isso ajude alguém.


1
Não desculpa Não foi isso que você fez lá. O que você realmente fez foi selecionar a célula "A1" usando o comando "Application.GoTo", que não é diferente de usar realmente "Selecionar", em seguida, usou clearcontents em sua seleção. a maneira de fazer isso sem selecionar células seria Workbook(WorkbookName).Worksheets(WorksheetName).Range("A1").ClearContentsqual é uma linha e não duas e, na verdade, funciona sem selecionar células.
Geoff Griswald
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.