VBA en Excel: ejecutar el código de una hoja desde un módulo

En Excel con VBA es muy común tener un procedimiento escrito dentro del módulo de una hoja (por ejemplo, Hoja1) y necesitar ejecutarlo desde un módulo estándar, desde un botón o desde otra macro. En este post te muestro cómo hacerlo con un ejemplo real, por qué a veces aparece el error "No se ha definido Sub o Function" y qué buenas prácticas conviene seguir.

El caso: el código está en la Hoja1

En la siguiente imagen se ve el editor de VBA (se abre con Alt + F11). El procedimiento VALOR está escrito en el módulo de la Hoja1:

Ese procedimiento recorre las filas 20 a 34 y, según el valor de la columna I, copia el dato en la columna H o en la G. Este es el mismo código escrito de forma un poco más ordenada:

' Código en el módulo de la Hoja1
Public Sub VALOR()
    Dim i As Long
    For i = 20 To 34
        If Me.Range("I" & i).Value >= 0.7 Then
            Me.Range("H" & i).Value = Me.Range("I" & i).Value
            Me.Range("G" & i).Value = ""
        Else
            Me.Range("G" & i).Value = Me.Range("I" & i).Value
            Me.Range("H" & i).Value = ""
        End If
    Next i
End Sub

Dentro del módulo de una hoja, Me hace referencia a esa misma hoja. Así el código siempre trabaja sobre la Hoja1, aunque en ese momento el usuario esté viendo otra hoja.

La solución: llamar al procedimiento desde un módulo

En un módulo estándar (Insertar → Módulo) basta con escribir el nombre de la hoja, un punto y el nombre del procedimiento:

' Código en Módulo3
Sub ACTUALIZARGRAFICAS()
    Call Hoja1.VALOR
End Sub

Al ejecutar ACTUALIZARGRAFICAS (con F5, desde un botón o desde Programador → Macros), VBA busca el objeto Hoja1 y ejecuta su procedimiento VALOR.

Paso a paso

  1. Abre el editor de VBA con Alt + F11.
  2. En el Explorador de proyectos, haz doble clic sobre la hoja donde está el código y verifica que el procedimiento sea Public Sub (si no dice nada, por defecto también es público).
  3. Inserta un módulo: menú Insertar → Módulo.
  4. Escribe una macro que llame a Hoja1.NombreDelProcedimiento.
  5. Guarda el libro como .xlsm (libro habilitado para macros); si lo guardas como .xlsx, las macros se pierden.

Hoja1 no es lo mismo que "el nombre de la pestaña"

Este es el detalle que más confunde. Cada hoja tiene dos nombres:

  • Nombre de código (CodeName): el que aparece primero en el Explorador de proyectos, por ejemplo Hoja1. Es el que usamos en Call Hoja1.VALOR. No cambia aunque el usuario renombre la pestaña.
  • Nombre de la pestaña: el que aparece entre paréntesis, por ejemplo "(COMITE DE VIGILANCIA)". Es el que se usa con Worksheets("COMITE DE VIGILANCIA").

Por eso es más robusto llamar por el nombre de código. Si tu Excel está en inglés, el nombre de código por defecto será Sheet1 en lugar de Hoja1. Puedes cambiarlo en la ventana Propiedades (F4), en el campo (Name).

Otras formas de ejecutar el código de una hoja

' 1) Llamada directa por nombre de código (la recomendada)
Call Hoja1.VALOR

' 2) Sin la palabra Call (equivalente)
Hoja1.VALOR

' 3) Usando el nombre de la pestaña
Worksheets("COMITE DE VIGILANCIA").VALOR

' 4) Con Application.Run, útil si el nombre viene en una variable
Application.Run "Hoja1.VALOR"

La opción 3 depende de que nadie cambie el nombre de la pestaña. La opción 4 es útil cuando el nombre de la macro se arma dinámicamente, pero si escribes mal el nombre el error solo aparece al ejecutar.

Buena práctica: ¿código en la hoja o en un módulo?

El módulo de una hoja está pensado para los eventos de esa hoja: Worksheet_Change, Worksheet_Activate, Worksheet_SelectionChange, etc. La lógica que quieres reutilizar desde varios lugares suele quedar mejor en un módulo estándar, recibiendo la hoja como parámetro:

' En un módulo estándar
Public Sub ClasificarValores(ws As Worksheet, filaIni As Long, filaFin As Long)
    Dim i As Long
    For i = filaIni To filaFin
        If ws.Range("I" & i).Value >= 0.7 Then
            ws.Range("H" & i).Value = ws.Range("I" & i).Value
            ws.Range("G" & i).ClearContents
        Else
            ws.Range("G" & i).Value = ws.Range("I" & i).Value
            ws.Range("H" & i).ClearContents
        End If
    Next i
End Sub

Sub Ejemplo()
    ClasificarValores Hoja1, 20, 34
End Sub

Así el mismo procedimiento funciona para cualquier hoja y rango, y el módulo de la hoja queda solo con sus eventos.

Errores comunes

  • "No se ha definido Sub o Function": escribiste solo Call VALOR. Como el procedimiento está en una hoja, debes anteponer el objeto: Call Hoja1.VALOR.
  • "Se requiere un objeto": el nombre de código no es Hoja1 (puede ser Sheet1 u otro). Revísalo en el Explorador de proyectos.
  • El procedimiento no aparece en la lista de macros: los procedimientos de las hojas sí aparecen, pero con el prefijo de la hoja; los Private Sub no aparecen.
  • Las macros no se ejecutan: el libro está guardado como .xlsx o las macros están deshabilitadas en el Centro de confianza.

Preguntas frecuentes

¿Puedo llamar a un procedimiento de una hoja que está en otro libro?

Sí, con Application.Run "'Otro libro.xlsm'!Hoja1.VALOR", siempre que ese libro esté abierto.

¿Puedo pasarle parámetros?

Sí. Si el procedimiento es Public Sub VALOR(filaFin As Long), lo llamas con Hoja1.VALOR 34.

¿Funciona igual en Excel 365?

Sí. Esta forma de llamar procedimientos funciona igual en las versiones de escritorio de Excel para Windows, incluida Microsoft 365.

Si te interesa automatizar tareas con VBA, mira también la macro para crear y borrar carpetas desde Excel, y si estás empezando con fórmulas, el post BUSCARV vs BUSCARX en Excel.

Conclusión

Para ejecutar desde un módulo el código que está en una hoja, usa Call Hoja1.NombreProcedimiento, verifica el nombre de código de la hoja y guarda el libro como .xlsm. Y como buena práctica, deja en las hojas solo los eventos y lleva la lógica reutilizable a módulos estándar.

No hay comentarios