Café y Código

7. Eventos de Hoja y Libro

En esta lección aprenderás a:

  • Entender qué es un evento en VBA y para qué sirve.
  • Usar Worksheet_Change para reaccionar cuando el usuario edita una celda.
  • Usar Workbook_Open para ejecutar código al abrir el archivo.
  • Prevenir la recursividad con Application.EnableEvents.

¿Qué es un evento?

Un evento es una acción del usuario o del sistema (editar una celda, abrir el libro, hacer clic) que Excel detecta y a la que VBA puede responder automáticamente, sin necesidad de llamar el procedimiento manualmente.

Vista tipo Excel: la primera fila muestra las letras de columna; la primera columna muestra los números de fila. En las celdas con fórmula (=...), cada referencia lleva un color distinto. Clic en la referencia: resalta solo esa celda. Clic en el resto de la celda (por ejemplo el = o los operadores): resalta todas las celdas citadas. Repetir el mismo gesto sobre lo ya resaltado lo oculta.

Eventos más usadosResumen

Para abrir el módulo de una hoja, haz doble clic en su nombre en el Explorador de proyectos del editor VBA.

ABC
1 EventoCuándo se disparaDónde se escribe
2Worksheet_ChangeAl cambiar el valor de cualquier celda de la hojaMódulo de la hoja (ej. Hoja1)
3Worksheet_ActivateAl activar (hacer clic en) esa hojaMódulo de la hoja
4Workbook_OpenAl abrir el libro de ExcelThisWorkbook
5Workbook_BeforeSaveAntes de guardar el libroThisWorkbook
6Workbook_BeforeCloseAntes de cerrar el libroThisWorkbook

Worksheet_Change — reaccionar al editar celdas

Este evento recibe el parámetro Target que representa la celda modificada. Se escribe en el módulo de la hoja (doble clic en Hoja1 en el explorador):

Hoja1 — módulo de hoja
VBA
1 Private Sub Worksheet_Change(ByVal Target As Range)
2 ' Solo reaccionar si se edita la columna B
3 If Target.Column = 2 Then
4 If Target.Value < 0 Then
5 Target.Interior.Color = RGB(255, 200, 200)
6 MsgBox "¡Valor negativo detectado en " & Target.Address & "!"
7 Else
8 Target.Interior.Color = xlNone
9 End If
10 End If
11 End Sub

Target.Column devuelve el número de columna (1=A, 2=B…). Target.Address devuelve la dirección como texto (ej. "$B$5").

Prevenir recursividad con EnableEvents

Si el evento modifica una celda, eso puede disparar de nuevo el mismo evento, creando un bucle infinito. La solución: desactivar los eventos mientras se ejecuta el código:

Hoja1 — evitar recursividad
VBA
1 Private Sub Worksheet_Change(ByVal Target As Range)
2 Application.EnableEvents = False
3 If Target.Column = 1 Then
4 Target.Offset(0, 1).Value = UCase(Target.Value) ' mayúsculas en col B
5 End If
6 Application.EnableEvents = True
7 End Sub

Siempre restaura Application.EnableEvents = True al final. De lo contrario ningún evento funcionará durante el resto de la sesión.

Workbook_Open — código al abrir el archivo

Se escribe en el módulo de ThisWorkbook (doble clic en ThisWorkbook en el explorador):

ThisWorkbook — Workbook_Open
VBA
1 Private Sub Workbook_Open()
2 Dim usuario As String
3 usuario = Environ("USERNAME")
4 MsgBox "Bienvenido al sistema, " & usuario
5
6 ' Ir directamente a la hoja 'Panel'
7 Sheets("Panel").Activate
8 End Sub

Environ("USERNAME") lee el nombre del usuario de Windows.

Practica en Excel

Abre Excel (o copia los datos con el botón de la tabla), sigue los pasos y comprueba el resultado en tu hoja.

  1. Abre el editor VBA, haz doble clic en "Hoja1" en el Explorador de proyectos.
  2. Elige "Worksheet" y "Change" en los desplegables superiores del módulo.
  3. Escribe el código de Worksheet_Change para colorear negativos en columna B.
  4. Vuelve a Excel, escribe un número negativo en B3 y verifica el color rojo.
  5. Haz doble clic en "ThisWorkbook" y escribe el evento Workbook_Open con un mensaje de bienvenida.
  6. Guarda el archivo como .xlsm (habilitado para macros) y ciérralo. Al reabrirlo, el mensaje debe aparecer.

Ejercicios prácticos

15 ejercicios organizados por dificultad para consolidar el manejo de eventos VBA.

Básico

1. Saludo al abrir

Escribe en ThisWorkbook un evento Workbook_Open que muestre el mensaje "¡Bienvenido! Hoy es: " seguido de la fecha actual (Date).

Ver solución
ThisWorkbook
VBA
1 Private Sub Workbook_Open()
2 MsgBox "¡Bienvenido! Hoy es: " & Date
3 End Sub

2. Detectar cambio en celda A1

En el módulo de Hoja1, escribe un Worksheet_Change que muestre un MsgBox solo cuando se edite la celda A1.

Ver solución
Hoja1
VBA
1 Private Sub Worksheet_Change(ByVal Target As Range)
2 If Target.Address = "$A$1" Then
3 MsgBox "Cambiaste A1 a: " & Target.Value
4 End If
5 End Sub

3. Activar hoja con mensaje

Escribe un evento Worksheet_Activate que muestre el nombre de la hoja activa cuando el usuario haga clic en ella.

Ver solución
Hoja1
VBA
1 Private Sub Worksheet_Activate()
2 MsgBox "Activaste la hoja: " & Me.Name
3 End Sub

4. Timestamp automático

Cuando se edite cualquier celda de la columna B, escribe en la columna C de la misma fila la hora actual (Now).

Ver solución
Hoja1
VBA
1 Private Sub Worksheet_Change(ByVal Target As Range)
2 Application.EnableEvents = False
3 If Target.Column = 2 Then
4 Target.Offset(0, 1).Value = Now
5 End If
6 Application.EnableEvents = True
7 End Sub

5. Aviso antes de guardar

En ThisWorkbook, escribe un evento Workbook_BeforeSave que muestre "¿Seguro que quieres guardar?" antes de proceder.

Ver solución
ThisWorkbook
VBA
1 Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
2 MsgBox "¿Seguro que quieres guardar?"
3 End Sub

Intermedio

6. Colorear celda editada

Cuando se edite cualquier celda de la columna A, colorea el fondo de esa celda en amarillo (RGB(255, 255, 0)). Usa EnableEvents para evitar recursividad.

Ver solución
Hoja1
VBA
1 Private Sub Worksheet_Change(ByVal Target As Range)
2 Application.EnableEvents = False
3 If Target.Column = 1 Then
4 Target.Interior.Color = RGB(255, 255, 0)
5 End If
6 Application.EnableEvents = True
7 End Sub

7. Validar celda no vacía

Si el usuario deja vacía la celda B2, cancela el cambio restaurando el valor anterior y muestra un aviso. (Pista: guarda el valor anterior en una variable de módulo antes del cambio.)

Ver solución
Hoja1
VBA
1 Dim valorAnterior As String
2
3 Private Sub Worksheet_SelectionChange(ByVal Target As Range)
4 If Target.Address = "$B$2" Then
5 valorAnterior = Target.Value
6 End If
7 End Sub
8
9 Private Sub Worksheet_Change(ByVal Target As Range)
10 If Target.Address = "$B$2" Then
11 If Target.Value = "" Then
12 Application.EnableEvents = False
13 Target.Value = valorAnterior
14 Application.EnableEvents = True
15 MsgBox "B2 no puede quedar vacía."
16 End If
17 End If
18 End Sub

8. Convertir a mayúsculas automáticamente

Cada vez que el usuario edite una celda de la columna C, convierte automáticamente el texto a mayúsculas usando UCase.

Ver solución
Hoja1
VBA
1 Private Sub Worksheet_Change(ByVal Target As Range)
2 Application.EnableEvents = False
3 If Target.Column = 3 Then
4 Target.Value = UCase(Target.Value)
5 End If
6 Application.EnableEvents = True
7 End Sub

9. Redirigir al usuario al abrir

En Workbook_Open, activa la hoja llamada "Panel" y selecciona la celda A1. Si la hoja no existe, muestra un aviso y no hagas nada.

Ver solución
ThisWorkbook
VBA
1 Private Sub Workbook_Open()
2 Dim ws As Worksheet
3 On Error Resume Next
4 Set ws = ThisWorkbook.Sheets("Panel")
5 On Error GoTo 0
6 If ws Is Nothing Then
7 MsgBox "La hoja 'Panel' no existe."
8 Else
9 ws.Activate
10 ws.Range("A1").Select
11 End If
12 End Sub

10. Contador de cambios

Declara una variable de módulo contadorCambios. Con cada edición de cualquier celda, incrementa el contador y muestra en la celda F1 cuántas veces se ha editado la hoja.

Ver solución
Hoja1
VBA
1 Dim contadorCambios As Long
2
3 Private Sub Worksheet_Change(ByVal Target As Range)
4 Application.EnableEvents = False
5 contadorCambios = contadorCambios + 1
6 Me.Range("F1").Value = contadorCambios
7 Application.EnableEvents = True
8 End Sub

Avanzado

11. Rango de intersección

Usa Application.Intersect para detectar si el usuario editó alguna celda dentro del rango B2:D10. Solo en ese caso, muestra en una MsgBox la dirección y el nuevo valor.

Ver solución
Hoja1
VBA
1 Private Sub Worksheet_Change(ByVal Target As Range)
2 Dim zonaVigilada As Range
3 Set zonaVigilada = Me.Range("B2:D10")
4 If Not Application.Intersect(Target, zonaVigilada) Is Nothing Then
5 MsgBox "Celda editada: " & Target.Address & " → " & Target.Value
6 End If
7 End Sub

12. Bloquear cierre sin contraseña

En Workbook_BeforeClose, pide al usuario una contraseña con InputBox. Si no escribe "admin123", cancela el cierre poniendo Cancel = True.

Ver solución
ThisWorkbook
VBA
1 Private Sub Workbook_BeforeClose(Cancel As Boolean)
2 Dim clave As String
3 clave = InputBox("Introduce la contraseña para cerrar:", "Acceso")
4 If clave <> "admin123" Then
5 MsgBox "Contraseña incorrecta. No se puede cerrar."
6 Cancel = True
7 End If
8 End Sub

13. Evento en múltiples hojas

Desde ThisWorkbook, usa el evento Workbook_SheetChange (que se dispara en cualquier hoja) para registrar en la hoja "Log" (créala) cada cambio: fila, columna, hoja y valor nuevo.

Ver solución
ThisWorkbook
VBA
1 Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
2 Dim wsLog As Worksheet
3 Dim sig As Long
4 Application.EnableEvents = False
5 On Error Resume Next
6 Set wsLog = ThisWorkbook.Sheets("Log")
7 On Error GoTo 0
8 If wsLog Is Nothing Then
9 Set wsLog = ThisWorkbook.Sheets.Add
10 wsLog.Name = "Log"
11 End If
12 sig = wsLog.Cells(wsLog.Rows.Count, 1).End(xlUp).Row + 1
13 wsLog.Cells(sig, 1).Value = Sh.Name
14 wsLog.Cells(sig, 2).Value = Target.Address
15 wsLog.Cells(sig, 3).Value = Target.Value
16 wsLog.Cells(sig, 4).Value = Now
17 Application.EnableEvents = True
18 End Sub

14. Semáforo de stock

La columna D tiene cantidades de stock. Cuando el usuario edite un valor en D, colorea la celda: rojo si es menor a 5, amarillo si está entre 5 y 10, verde si es mayor a 10.

Ver solución
Hoja1
VBA
1 Private Sub Worksheet_Change(ByVal Target As Range)
2 Application.EnableEvents = False
3 If Target.Column = 4 And IsNumeric(Target.Value) Then
4 Select Case CDbl(Target.Value)
5 Case Is < 5
6 Target.Interior.Color = RGB(255, 100, 100)
7 Case 5 To 10
8 Target.Interior.Color = RGB(255, 255, 0)
9 Case Else
10 Target.Interior.Color = RGB(100, 220, 100)
11 End Select
12 End If
13 Application.EnableEvents = True
14 End Sub

15. Desactivar eventos temporalmente desde otro módulo

Crea una macro CargarDatos en un módulo estándar que escriba 50 valores en la columna A desactivando los eventos antes de empezar y reactivándolos al terminar, evitando que el evento Worksheet_Change se dispare 50 veces.

Ver solución
Modulo1
VBA
1 Sub CargarDatos()
2 Dim i As Long
3 Application.EnableEvents = False
4 Application.ScreenUpdating = False
5
6 With ThisWorkbook.Sheets("Hoja1")
7 For i = 1 To 50
8 .Cells(i, 1).Value = i * 10
9 Next i
10 End With
11
12 Application.ScreenUpdating = True
13 Application.EnableEvents = True
14 MsgBox "50 valores cargados sin disparar eventos."
15 End Sub

Pon a prueba tus conocimientos

Ko-fi
Donaciones
Apoyá cafeycodigo con un café en Ko-fi. Colaboradores: insignia, muro y zona exclusiva.