Cómo evitar usar Select en Excel VBA

537

He escuchado mucho acerca de la aborrecible comprensión del uso .Selecten Excel VBA, pero no estoy seguro de cómo evitar usarlo. Estoy descubriendo que mi código sería más reutilizable si pudiera usar variables en lugar de Selectfunciones. Sin embargo, no estoy seguro de cómo referirme a cosas (como el ActiveCelletc.) si no las estoy usando Select.

He encontrado este artículo sobre rangos y este ejemplo sobre los beneficios de no usar select pero no puedo encontrar nada sobre cómo .

BiGXERO
fuente
14
Es importante tener en cuenta que hay casos en los que usar Selecty / o ActiveSheetetc., etc. es completamente inevitable. Aquí hay un ejemplo que encontré: stackoverflow.com/questions/22796286/…
Rick apoya a Monica
99
Y hay ocasiones, editar datos de gráficos en ppt con un archivo de Excel subyacente, donde se requiere activar o seleccionar.
brettdj
@brettdj: aquí hay un ejemplo reciente . Para establecer todas las hojas en un libro de trabajo con el mismo valor, parece .Select / .Selectionser necesario.
BruceWayne
3
@bruce del mismo control de calidad parece que no lo es
chris neilsen

Respuestas:

565

Algunos ejemplos de cómo evitar seleccionar

Use Dim'd variables

Dim rng as Range

Setla variable al rango requerido. Hay muchas formas de referirse a un rango de celda única

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

o un rango de celdas múltiples

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)

Usted puede utilizar el acceso directo al Evaluatemétodo, pero esto es menos eficiente y por lo general se debe evitar en el código de producción.

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

Todos los ejemplos anteriores se refieren a celdas en la hoja activa . A menos que desee trabajar específicamente solo con la hoja activa, también es mejor atenuar una 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 no desea trabajar con el ActiveSheet, para mayor claridad lo mejor es ser explícita. Pero tenga cuidado, ya que algunos Worksheetmétodos cambian la hoja activa.

Set rng = ActiveSheet.Range("A1")

Nuevamente, esto se refiere al libro de trabajo activo . A menos que desee trabajar específicamente solo con ActiveWorkbooko ThisWorkbook, es mejor atenuar una Workbookvariable también.

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

Si no desea trabajar con el ActiveWorkbook, para mayor claridad lo mejor es ser explícita. Pero tenga cuidado, ya que muchos WorkBookmétodos cambian el libro activo.

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

También puede usar el ThisWorkbookobjeto para referirse al libro que contiene el código en ejecución.

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

Un código común (malo) es abrir un libro, obtener algunos datos y luego cerrar de nuevo

Esto es malo:

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

Y sería mejor 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

Pase rangos a su Subsy Functions como variables de rango

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

También debe aplicar métodos (como Findy Copy) a las 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 realiza un bucle en un rango de celdas, a menudo es mejor (más rápido) copiar primero los valores del rango en una matriz variante y hacer un bucle sobre ese

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 es una pequeña muestra de lo que es posible.

Chris Neilsen
fuente
77
agregando a esta brillante respuesta que para trabajar con un rango no es necesario conocer su tamaño real siempre que conozca la parte superior izquierda ... por ejemplo rng1(12, 12), funcionará aunque rng1 esté configurado en [A1:A10]solo.
MikeD
3
@chrisneilsen Chris, creo que también puedes usar el prefijo de la hoja de trabajo antes de la notación de referencia de celda abreviada para evitar que escribas Rangeasí: ActiveSheet.[a1:a4]o ws.[b6].
Logan Reed
3
@AndrewWillems ... o 48 veces en esta publicación, pero quién está contando. ☺ ... pero en serio, es fácil de olvidar cuando se trabaja con variables que contienen objetos. Una variantvariable no requiere Set hasta que le asigne un objeto. Por ejemplo, Dim x: x = 1está bien, pero Dim x: x = Sheets("Sheet1")generará un error 438. Sin embargo sólo para confundir / aclarar Dim x: x = Range("A1")va a no crear un error. ¿Por qué? ... porque se le asigna el valor del objeto a la variable, no una referencia al objeto en sí (ya que es el equivalente de Dim x: x = Range("A1").Value)
ashleedawg
1
@ user3932000 No conozco un escenario en el que los nombres de las hojas cambien automáticamente. En cuanto a los nombres de archivo, solo haría eso si ya hay un archivo con ese nombre en la carpeta. Simplemente use Guardar ... o codifique el nombre del archivo para guardarlo como una cadena. Si no puede resolver este problema, debe hacer una pregunta por separado al respecto en lugar de comentar.
TylerH
1
@ user3932000 que haría una Q interesante. Estoy seguro de que hay formas de manejarlo ... has estado TANTO el tiempo suficiente como para saber que secuestrar un hilo de comentarios en una Q anterior no es el camino a seguir
Chris Neilsen
212

Dos razones principales por las que .Select/ .Activate/ Selection/ Activecell/ Activesheet/ Activeworkbooketc ... deben evitarse

  1. Se ralentiza su código.
  2. Suele ser la causa principal de los errores de tiempo de ejecución.

¿Cómo lo evitamos?

1) Trabajar directamente con los objetos relevantes.

Considera este código

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

Este código también se puede escribir como

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

2) Si es necesario, declare sus variables. El mismo código anterior se puede escribir como

Dim ws as worksheet

Set ws = Sheets("Sheet1")

With ws.Range("A1")
    .Value = "Blah"
    .NumberFormat = "@"
End With
Siddharth Rout
fuente
17
Esa es una buena respuesta, pero lo que me falta en este tema es cuando realmente necesitamos Activar. Todos dicen que es malo, pero nadie explica los casos en los que tiene sentido usarlo. Por ejemplo, estaba trabajando con 2 libros de trabajo y no podía iniciar una macro en uno de los libros de trabajo sin activarlo primero. ¿Podrías elaborar un poco quizás? Además, si, por ejemplo, no activo las hojas al copiar un rango de una hoja a otra, cuando ejecuto el programa, parece activar las hojas respectivas de todos modos, implícitamente.
user3032689
1
Creo que a veces es necesario activar primero una hoja si necesita pegar o filtrar datos en ella. Diría que es mejor evitar la activación tanto como sea posible, pero hay casos en los que debe hacerlo. Así que siga activando y seleccionando a un mínimo absoluto según la respuesta anterior.
Nick
77
Creo que el objetivo no es evitarlos por completo, sino todo lo posible. si desea guardar un libro de trabajo, de modo que cuando alguien lo abre, se selecciona una celda determinada en una hoja determinada, entonces debe seleccionar esa hoja y celda. copiar / pegar es un mal ejemplo, al menos en el caso de los valores, se puede hacer más rápido con un código comoSheets(2).[C10:D12].Value = Sheets(1).[A1:B3].Value
robotik
1
@ Nick No necesita activar hojas para pegarlas o filtrarlas. Use el objeto de hoja en sus comandos de pegar o filtrar. Se vuelve más fácil a medida que aprende el modelo de objetos de Excel a través de la práctica. Creo que la única vez que uso .Activate es cuando creo una nueva hoja, pero quiero que la hoja original aparezca cuando se termine el código.
phrebh
3
@phrebh No necesita usar .Activatepara moverse a la hoja original, solo useApplication.Goto
GMalc
88

Agregaré un pequeño punto de énfasis a todas las excelentes respuestas dadas anteriormente:

Probablemente, lo más importante que puede hacer para evitar el uso de Seleccionar es usar, en la medida de lo posible, rangos con nombre (combinados con nombres de variables significativos) en su código VBA . Este punto fue mencionado anteriormente, pero pasó por alto un poco; Sin embargo, merece especial atención.

Aquí hay un par de razones adicionales para hacer un uso liberal de los rangos con nombre, aunque estoy seguro de que podría pensar en más.

Los rangos con nombre hacen que su código sea más fácil de leer y comprender.

Ejemplo:

Dim Months As Range
Dim MonthlySales As Range

Set Months = Range("Months")
'e.g, "Months" might be a named range referring to A1:A12

Set MonthlySales = Range("MonthlySales")
'e.g, "Monthly Sales" might be a named range referring to B1:B12

Dim Month As Range
For Each Month in Months
    Debug.Print MonthlySales(Month.Row)
Next Month

Es bastante obvio lo que los rangos con nombre Monthsy MonthlySalescontiene, y lo que está haciendo el procedimiento.

¿Porque es esto importante? Parcialmente porque es más fácil para otras personas entenderlo, pero incluso si usted es la única persona que alguna vez verá o usará su código, aún debe usar rangos con nombre y buenos nombres de variables porque OLVIDARÁ lo que quiso hacer con él un año después, y perderá 30 minutos solo para descubrir qué está haciendo su código.

Los rangos con nombre aseguran que sus macros no se rompan cuando (¡no si!) La configuración de la hoja de cálculo cambia.

Considere, si el ejemplo anterior se hubiera escrito así:

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

Al principio, este código funcionará bien, es decir, hasta que usted o un futuro usuario decida "caramba, creo que voy a agregar una nueva columna con el año en Columna A", o colocar una columna de gastos entre los meses y columnas de ventas, o agregue un encabezado a cada columna. Ahora, tu código está roto. Y debido a que usó nombres de variables terribles, le llevará mucho más tiempo descubrir cómo solucionarlo de lo que debería tomar.

Si había utilizado rangos con nombre para comenzar, las columnas Monthsy Salespodrían moverse alrededor de todo lo que desee, y su código continuará funcionando bien.

Rick apoya a Monica
fuente
66
El debate sobre si los rangos con nombre son buenos o malos en el diseño de la hoja de cálculo continúa: estoy firmemente en el campo de no. En mi experiencia, aumentan los errores (para usuarios estándar que no necesitan código).
brettdj
12
Estoy de acuerdo con tu filosofía de desarrollo; Sin embargo, creo que el artículo no tiene sentido. Habla sobre cómo los nombres de rango pueden confundir a los novatos que están depurando hojas de cálculo, ¡pero cualquiera que use novatos para mirar hojas de cálculo complejas obtiene lo que se merece! Solía ​​trabajar para una empresa que revisaba hojas de cálculo financieras, y puedo decirle que no es el tipo de trabajo que le da a un novato.
DeanOC
8
No hay debate significativo. Cualquiera que discuta contra los nombres definidos no se ha tomado el tiempo para comprender completamente sus ramificaciones. Las fórmulas con nombre pueden ser la construcción más profunda y útil en todo Excel.
Excel Hero
10
@brettdj: Su cita es correcta, pero olvidó mencionar que le siguen seis frases "Excepto ...". Uno de ellos es: " Excepto como un sustituto de las referencias de celda en la codificación de macro Siempre use Excel Names como un sustituto de las referencias de celda al construir macros. Esto es para evitar errores derivados de la inserción de filas o columnas adicionales por las cuales la codificación de macro ya no apunta a la fuente de datos prevista ".
Marcus Mangelsdorf el
47

Voy a dar la respuesta corta ya que todos los demás dieron la respuesta larga.

Obtendrá .select y .activate cada vez que grabe macros y los reutilice. Cuando selecciona una celda u hoja, solo la activa. A partir de ese momento, siempre que use referencias no calificadas, como Range.Valueellos solo usan la celda y hoja activas. Esto también puede ser problemático si no mira dónde se coloca su código o si un usuario hace clic en el libro.

Por lo tanto, puede eliminar estos problemas haciendo referencia directa a sus celdas. Que 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)

O podrías

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

Existen varias combinaciones de estos métodos, pero esa sería la idea general expresada tan pronto como sea posible para personas impacientes como yo.

MattB
fuente
33

"... y descubro que mi código sería más reutilizable si pudiera usar variables en lugar de seleccionar funciones".

Si bien no puedo pensar en nada más que un puñado de situaciones aisladas en las .Selectque sería una mejor opción que la referencia directa a las celdas, me levantaría en defensa Selectiony señalaría que no debe descartarse por las mismas razones que .Selectdeberían evitarse.

Hay momentos en los que tener subrutinas macro breves y que ahorran tiempo asignadas a combinaciones de teclas de acceso rápido disponibles con solo tocar un par de teclas ahorra mucho tiempo. Ser capaz de seleccionar un grupo de celdas para promulgar el código operativo en maravillas de obras cuando se trata de datos de bolsillo que no se ajustan a un formato de datos de toda la hoja de trabajo. De la misma manera que puede seleccionar un grupo de celdas y aplicar un cambio de formato, seleccionar un grupo de celdas para ejecutar un código macro especial puede ser un gran ahorro de tiempo.

Ejemplos de subestructura basada en selección:

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

El código real para procesar podría ser cualquier cosa, desde una sola línea hasta varios módulos. He usado este método para iniciar rutinas de larga ejecución en una selección irregular de celdas que contienen los nombres de archivo de libros externos.

En resumen, no descarte Selectiondebido a su estrecha asociación con .Selecty ActiveCell. Como una propiedad de hoja de trabajo tiene muchos otros propósitos.

(Sí, sé que se trataba esta pregunta .Select, Selectionpero quería eliminar cualquier idea errónea que los codificadores principiantes de VBA pudieran inferir).


fuente
13
Selectionpuede ser cualquier cosa en la hoja de trabajo, así que también podría probar primero el tipo del objeto antes de asignarlo a una variable, ya que lo declaró explícitamente como Range.
L42
29

Tenga en cuenta que a continuación comparo el enfoque Seleccionar (el que el OP quiere evitar), con el enfoque Rango (y esta es la respuesta a la pregunta). Así que no dejes de leer cuando veas el primer Select.

Realmente depende de lo que intentes hacer. De todos modos, un simple ejemplo podría ser útil. Supongamos que desea establecer el valor de la celda activa en "foo". Usando ActiveCell escribirías algo como esto:

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

Si desea usarlo para una celda que no es la activa, por ejemplo, para "B2", debe seleccionarla primero, así:

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

Usando Rangos puede escribir una macro más genérica que se puede usar para establecer el valor de cualquier celda que desee a lo que quiera:

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

Entonces puedes reescribir Macro2 como:

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

Y Macro1 como:

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

Espero que esto ayude a aclarar un poco las cosas.

Francesco Baruchelli
fuente
1
Gracias por la excelente respuesta tan rápido. Entonces, ¿eso significa que si normalmente agregaría celdas al rango, nombrar el rango e iterar a través de él, debería saltar directamente a la creación de una matriz?
BiGXERO
No estoy seguro de entender lo que quiere decir, pero puede crear un Rango con una sola instrucción (por ejemplo, Rango ("B5: C14")) e incluso puede establecer su valor de una vez (si tiene que ser el mismo para cada celda en el rango), por ejemplo, Rango ("B5: C14"). Valor = "abc"
Francesco Baruchelli
29

Evitar Selecty Activatees el movimiento que te hace un mejor desarrollador de VBA. En general, Selecty Activatese usan cuando se graba una macro, por lo tanto, la Parenthoja de trabajo o el rango siempre se consideran activos.

Así es como puede evitar Selecty Activateen los siguientes casos:


Agregar una nueva hoja de trabajo y copiar una celda en ella:

De (código generado con la grabadora 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

A:

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

Cuando desee copiar el rango entre hojas de trabajo:

Desde:

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

A:

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

Usando elegantes rangos con nombre

Puede acceder a ellos con [], lo cual es realmente hermoso, en comparación con la otra manera. Comprueba tu mismo:

Dim Months As Range
Dim MonthlySales As Range

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

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

El ejemplo de arriba se vería así:

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

No copiando valores, sino tomándolos

Por lo general, si está dispuesto a hacerlo select, lo más probable es que esté copiando algo. Si solo está interesado en los valores, esta es una buena opción para evitar seleccionar:

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


Intente siempre hacer referencia a la Hoja de trabajo también

Este es probablemente el error más común en . Siempre que copie rangos, a veces la hoja de trabajo no está referenciada y, por lo tanto, VBA considera que la hoja incorrecta es 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

¿Realmente nunca puedo usar .Selecto .Activatepara nada?

  • Un buen ejemplo de cuándo podría justificarse el uso .Activatey .Selectcuándo quiere asegurarse de que se seleccione una Hoja de trabajo específica por razones visuales. Por ejemplo, que su Excel siempre se abriría con la hoja de cálculo de portada seleccionada primero, sin tener en cuenta cuál era la ActiveSheet cuando se cerró el archivo.

Por lo tanto, algo como el siguiente código está absolutamente bien:

Private Sub Workbook_Open()
    Worksheets("Cover").Activate
End Sub
Vityata
fuente
Puede usar Application.Goto en lugar de Worksheets.Activate. Algo menos arriesgado.
Geoff Griswald
1
La respuesta tardía para su información, un ejemplo agradable y algo inesperado para una necesidad .Select, así como mi trabajo, se puede encontrar en Cómo escribir información idéntica en todas las hojas - @Vityata :)
TM
1
@ TM: de hecho, es un ejemplo interesante y probablemente ahorre algunos milisegundos para más de 100 hojas de trabajo, pero probablemente lo desaliente si lo veo en alguna parte. De todos modos, la Selección no está escrita explícitamente, sino que es el resultado .FillAcrossSheets, por lo que esto está en algún punto intermedio (al menos en mi idea sobre la taxonomía VBA)
Vityata
17

Indique siempre el libro de trabajo, la hoja de trabajo y la celda / rango.

Por ejemplo:

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

Debido a que los usuarios finales siempre harán clic en los botones y tan pronto como el foco se salga del libro de trabajo, el código quiere trabajar, entonces las cosas van completamente mal.

Y nunca use el índice de un libro de trabajo.

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

No sabe qué otros libros de trabajo estarán abiertos cuando el usuario ejecute su código.

usuario1644564
fuente
77
Los nombres de las hojas de trabajo también pueden cambiar, ya sabes. Use nombres en código en su lugar.
Rick apoya a Monica el
Los nombres de las hojas de trabajo pueden cambiar, claro. Pero no estoy de acuerdo con que debas complicar demasiado tu código para intentar mitigarlo. Si un usuario cambia el nombre de una hoja y su macro deja de funcionar, eso es todo. Generalmente asumo que los nombres de las hojas de trabajo serán los mismos. Para macros particularmente críticas, ejecuto una pequeña verificación previa al vuelo antes de iniciar en la macro propiamente dicha, que solo verifica para asegurarse de que todas las hojas que espera encontrar estén realmente allí, y si falta alguna, notifica al usuario cuál.
Geoff Griswald
10

Estos métodos son bastante estigmatizados, por lo que tomar el liderazgo de @Vityata y @Jeeped por el simple hecho de dibujar una línea en la arena:

¿Por qué no llamar .Activate, .Select, Selection, ActiveSomethingmétodos / propiedades

Básicamente porque están llamados principalmente para manejar la entrada del usuario a través de la interfaz de usuario de la aplicación. Dado que son los métodos que se llaman cuando el usuario maneja objetos a través de la interfaz de usuario, son los que registra la macrograbadora, y por eso llamarlos es frágil o redundante para la mayoría de las situaciones: no tiene que seleccionar un objeto para realizar una acción Selectioninmediatamente después.

Sin embargo, esta definición resuelve situaciones en las que se requieren:

Cuando llamar .Activate, .Select, .Selection, .ActiveSomethingmétodos / propiedades

Básicamente cuando espera que el usuario final desempeñe un papel en la ejecución.

Si está desarrollando y espera que el usuario elija las instancias de objeto para que su código las maneje, entonces .Selectiono .ActiveObjectes apropiado.

Por otro lado, .Selecty .Activateson útiles cuando puede inferir la siguiente acción del usuario y desea que su código guíe al usuario, posiblemente ahorrándole algo de tiempo y clics del mouse. Por ejemplo, si su código acaba de crear una nueva instancia de un gráfico o una actualizada, el usuario puede desear echarle un vistazo, y puede recurrir .Activatea él o a su hoja para ahorrarle al usuario el tiempo que lo busca; o si sabe que el usuario necesitará actualizar algunos valores de rango, puede seleccionar ese rango mediante programación.

LFB
fuente
6

En mi humilde opinión, el uso .selectproviene de personas que, como yo, comenzaron a aprender VBA por necesidad a través de la grabación de macros y luego la modificación del código sin darse cuenta de eso, .selecty posteriormente selectiones solo un intermediario innecesario.

.select puede evitarse, como muchos ya se publicaron, trabajando directamente con los objetos ya existentes, lo que permite varias referencias indirectas como calcular i y j de una manera compleja y luego editar la celda (i, j), etc.

De lo contrario, no hay nada implícitamente incorrecto en .selectsí mismo y puede encontrar usos para esto fácilmente, por ejemplo, tengo una hoja de cálculo que relleno con la fecha, active la macro que hace algo de magia con ella y la exporta en un formato aceptable en una hoja separada, que , sin embargo, requiere algunas entradas manuales finales (impredecibles) en una celda adyacente. Así que aquí llega el momento en .selectque eso me ahorra ese movimiento y clic adicionales del mouse.

Eleshar
fuente
2
Si bien tiene razón, hay al menos una cosa implícitamente incorrecta con select: es lenta. Muy lento en comparación con todo lo demás que ocurre en una macro.
vacip
4

Respuesta rápida:

Para evitar el uso del .Selectmétodo, puede establecer una variable igual a la propiedad que desee.

► Por ejemplo, si desea el valor Cell A1, puede establecer una variable igual a la propiedad de valor de esa celda.

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

► Por ejemplo, si desea el nombre en clave de 'Sheet3', puede establecer una variable igual a la propiedad de nombre en clave de esa hoja de trabajo.

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

Espero que eso ayude. Hazme saber si tienes alguna pregunta.

FinPro.Online
fuente
3

Noté que ninguna de estas respuestas menciona la propiedad .Offset . Esto también se puede usar para evitar el uso de la Selectacción al manipular ciertas celdas, particularmente en referencia a una celda seleccionada (como menciona el OP ActiveCell).

Aqui hay un par de ejemplos.

También asumiré que el "ActiveCell" es J4 .

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

  • Esto cambiará la celda J6a un valor de 12
  • Un menos -2 habría hecho referencia a J2

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

  • Esto copia la celda en la k4que L4.
  • Tenga en cuenta que "0" no es necesario en el parámetro de desplazamiento si no es necesario (, 2)
  • Similar al ejemplo anterior, un menos 1 sería i4

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

  • Esto borrará los valores en todas las celdas de la columna k.

Esto no quiere decir que sean "mejores" que las opciones anteriores, sino simplemente enumerar alternativas.

PGSystemTester
fuente
0

Trabajando con .Parent característica. Este ejemplo muestra cómo configurar solo una referencia myRng permite el acceso dinámico a todo el entorno sin .Select, .Activate, .Activecell, .ActiveWorkbook, .ActiveSheet, etc. (No hay genereic. Característica infantil)

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
barneyos
fuente
Muy bien, pero no estoy seguro de qué tiene que ver esto con la pregunta de los OP. No necesita "Parent" para trabajar en VBA sin usar Select o ActiveSheet
Geoff Griswald
0

La razón principal por la que nunca debe usar Select o Activesheet es porque la mayoría de las personas tendrán al menos otro par de libros abiertos (a veces docenas) cuando ejecuten su macro, y si hacen clic fuera de su hoja mientras su macro se está ejecutando y hacen clic en otro libro que tienen abierto, luego cambia la "Hoja activa", y también cambia el libro de trabajo de destino para un comando "Seleccionar" no calificado.

En el mejor de los casos, su macro se bloqueará, en el peor de los casos, podría terminar escribiendo valores o cambiando celdas en el libro de trabajo incorrecto sin forma de "Deshacerlos".

Tengo una regla de oro simple que sigo: Agregar variables llamadas "wb" y "ws" para un objeto de libro de trabajo y un objeto de hoja de trabajo y siempre usarlas para referirme a mi macro libro. Si necesito referirme a más de un libro, o más de una hoja, agrego más variables.

por ejemplo

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

El comando "Establecer wb = ThisWorkbook" es absolutamente clave. "ThisWorkbook" es un valor especial en Excel, y significa el libro desde el cual se está ejecutando actualmente su código VBA . Un atajo muy útil para configurar la variable de su libro de trabajo.

Una vez que hayas hecho eso en la parte superior de tu Sub, usarlos no podría ser más simple, solo úsalos donde sea que uses "Selección":

Entonces, para cambiar el valor de la celda "A1" en "Salida" a "Hola", en lugar de:

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

Ahora podemos hacer esto:

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

Lo que no solo es mucho más confiable y es menos probable que se bloquee si el usuario está trabajando con varias hojas de cálculo, sino que también es mucho más corto, más rápido y más fácil de escribir.

Como beneficio adicional, si siempre nombra sus variables "wb" y "ws", puede copiar y pegar el código de un libro a otro y, por lo general, funcionará con los cambios mínimos necesarios, si corresponde.

Geoff Griswald
fuente
1
No es mi voto negativo, pero no estoy seguro de que esto agregue algo nuevo a lo que ya se ha propuesto en las respuestas existentes.
BigBen
Sí, mi respuesta es un poco redundante, pero las otras respuestas son demasiado largas, contienen demasiadas cosas superfluas y nadie ha mencionado el uso de ThisWorkbook para configurar su variable de hoja de trabajo por adelantado. Eso es algo que, si alguien me hubiera mostrado la primera vez que sumergí un dedo del pie en VBA, me habría resultado increíblemente útil. Otros han mencionado el uso de una variable de Hoja de trabajo pero realmente no explican muy bien por qué, y no ofrecen un ejemplo de código con y sin usar variables de hoja de trabajo y libro de trabajo.
Geoff Griswald
Pero la respuesta aceptada definitivamente discute ThisWorkbook... No estoy seguro de que su comentario sea exacto.
BigBen
Sí, no te equivocas. Pero no en el contexto de usarlo para establecer una variable de libro de trabajo y usar esa variable de libro de trabajo en el futuro, o usar esa variable de libro de trabajo para establecer una variable de hoja de trabajo, como sugiero. Mi respuesta es más corta, más simple y más accesible para principiantes que la respuesta aceptada.
Geoff Griswald
-3

Este es un ejemplo que borrará el contenido de la celda "A1" (o más si el tipo de selección es xllastcell, etc.). Todo hecho sin tener que seleccionar las celdas.

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

Espero que esto ayude a alguien.

marionffavp
fuente
1
No lo siento. Eso no es lo que has hecho allí en absoluto. Lo que realmente ha hecho es seleccionar la celda "A1" usando el comando "Application.GoTo", que no es diferente de usar realmente "Seleccionar", luego usó clearcontents en su selección. la forma de hacerlo sin seleccionar celdas sería Workbook(WorkbookName).Worksheets(WorksheetName).Range("A1").ClearContentsuna línea y no dos, y en realidad funciona sin seleccionar celdas.
Geoff Griswald