Capítulo 15: Automação Orientada a Eventos de Planilha e Pasta de Trabalho

🎯 Objetivo da Aula

Nesta aula, você aprenderá o paradigma da Programação Orientada a Eventos (Event-Driven Programming) no VBA. Você entenderá como fazer o Excel reagir de forma autônoma a ações naturais do usuário — como abrir a pasta de trabalho (Workbook_Open), salvar o arquivo (Workbook_BeforeSave) ou editar o conteúdo de uma célula (Worksheet_Change). Além disso, aprenderá a dominar o argumento Target e a proteção crucial contra laços recursivos infinitos utilizando Application.EnableEvents = False.

Ao final desta aula, você terá transformado uma planilha comum no sistema autônomo e reativo de gestão de pátio da FastLog S.A.


🏢 Cenário Prático (FastLog S.A.)

Empresa: FastLog Transportes & Logística Integrada S.A.
Problema: No pátio de manobras, os fiscais registram caminhões o dia todo. O processo dependia de o fiscal lembrar de clicar em botões para atualizar relatórios e carimbar horários. Frequentemente, o fiscal esquecia de clicar no botão “Registrar Horário”, gerando inconsistências no tempo de permanência de carretas (Demurrage). Além disso, fiscais salvavam a planilha com campos obrigatórios em branco antes de ir embora no final do turno, prejudicando a auditoria noturna.
Sua Missão: Implementar gatilhos reativos orientados a eventos:

  1. Ao abrir o arquivo, o Excel calcula e exibe automaticamente um resumo executivo com a quantidade de veículos retidos no pátio.
  2. Ao salvar o arquivo, o VBA valida se há registros incompletos e cancela o salvamento se houver pendências graves.
  3. Ao digitar a placa na coluna B, o evento Worksheet_Change detecta a digitação, formata a placa em maiúsculas e insere o carimbo de data/hora na coluna C instantaneamente, sem nenhum clique de botão!

🧠 Fundamentos: A Teoria Traduzida

1. Onde Moram os Códigos de Eventos?

Diferente das rotinas comuns que escrevemos em módulos padrão (Módulo1), os procedimentos de eventos possuem locais sagrados dentro do VBE:

  • Eventos de Pasta de Trabalho: Devem ser digitados exclusivamente dentro do objeto EstaPasta_de_trabalho (ThisWorkbook).
  • Eventos de Planilha: Devem ser digitados exclusivamente dentro do objeto da folha correspondente (ex: Planilha1 (Operacao_Patio)).
flowchart TD
    subgraph ARQ["Arquitetura Orientada a Eventos"]
        EV_OPEN["⚡ Workbook_Open<br/>Disparado na inicialização do arquivo"]
        EV_SAVE["💾 Workbook_BeforeSave<br/>Disparado antes de gravar no disco; pode cancelar o salvamento!"]
        EV_CHANGE["✍️ Worksheet_Change (Target)<br/>Disparado a cada tecla Enter/edição de célula"]
    end

    EV_OPEN --> A1["Exibe resumo gerencial de boas-vindas"]
    EV_SAVE --> A2["Valida campos obrigatórios (Cancel = True)"]
    EV_CHANGE --> A3["Carimba data/hora e formata texto em tempo real"]

    style ARQ fill:#1a365d,stroke:#fff,stroke-width:2px,color:#fff
    style EV_OPEN fill:#2980b9,stroke:#fff,stroke-width:2px,color:#fff
    style EV_SAVE fill:#8e44ad,stroke:#fff,stroke-width:2px,color:#fff
    style EV_CHANGE fill:#27ae60,stroke:#fff,stroke-width:2px,color:#fff
    style A1 fill:#2c3e50,stroke:#fff,stroke-width:2px,color:#fff
    style A2 fill:#c0392b,stroke:#fff,stroke-width:2px,color:#fff
    style A3 fill:#d35400,stroke:#fff,stroke-width:2px,color:#fff

2. O Evento Worksheet_Change e o Argumento Target

Toda vez que uma célula é alterada pelo usuário, o Excel dispara o evento passando o parâmetro:

1
Private Sub Worksheet_Change(ByVal Target As Range)
  • O objeto Target representa exatamente a célula (ou intervalo de células) que acabou de ser editada!

3. A Função Intersect

Para evitar que a macro dispare quando o usuário editar qualquer célula aleatória, usamos a função Intersect para verificar se a célula editada pertence à nossa coluna de interesse (por exemplo, Coluna B):

1
2
3
If Not Intersect(Target, Range("B2:B1000")) Is Nothing Then
    ' O usuário acabou de editar uma célula da coluna B!
End If

4. O Perigo da Recursão Infinita e EnableEvents

Se o seu código alterar uma célula dentro do evento Worksheet_Change, o próprio Excel interpretará essa mudança como um novo evento de alteração, disparando o evento novamente sobre si mesmo em um laço infinito mortal (Stack Overflow)!
Para impedir isso, sempre desative os eventos antes de alterar células e reative-os logo após:

1
2
3
Application.EnableEvents = False   ' Desliga a escuta de eventos
Target.Offset(0, 1).Value = Now    ' Grava a data na célula vizinha
Application.EnableEvents = True    ' Religia a escuta de eventos

📖 Exemplo Guiado: Passo a Passo no Laboratório

No computador do laboratório, acesse o VBE com Alt + F11.

Passo 1: Localizando o Objeto EstaPasta_de_trabalho

  1. Na Janela de Projetos (Project Explorer), localize a pasta Objetos do Microsoft Excel.
  2. Dê um duplo clique sobre o item: EstaPasta_de_trabalho (ou ThisWorkbook).
  3. Uma folha de código vazia será aberta.

🛠️ Prática Obrigatória 1: Eventos Globais de Inicialização e Salvamento

Implementaremos o resumo de abertura de turno e a trava de segurança que bloqueia o salvamento de cadastros incompletos.

Passo a Passo no Computador:

  1. No código de EstaPasta_de_trabalho, digite os dois procedimentos de eventos globais:
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    
    Option Explicit
    
    ' GATILHO 1: Disparado automaticamente assim que a pasta de trabalho é aberta
    Private Sub Workbook_Open()
        Dim ws As Worksheet
        Dim lngTotalVeiculos As Long
    
        Set ws = ThisWorkbook.Worksheets(1)
        lngTotalVeiculos = ws.Cells(ws.Rows.Count, "B").End(xlUp).Row - 1
    
        If lngTotalVeiculos < 0 Then lngTotalVeiculos = 0
    
        MsgBox "SISTEMA DE GESTÃO DE PÁTIO FASTLOG INICIALIZADO!" & vbCrLf & vbCrLf & _
               "• Data do Turno: " & Format(Date, "dd/mm/yyyy") & vbCrLf & _
               "• Carretas Registradas no Pátio: " & lngTotalVeiculos & " veículos." & vbCrLf & vbCrLf & _
               "Atenção: A digitação na coluna de Placas possui carimbo e auditoria reativa.", _
               vbInformation, "FastLog Automação Operacional"
    End Sub
    
    ' GATILHO 2: Disparado no momento em que o usuário clica em Salvar (Ctrl + B)
    Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
        Dim ws As Worksheet
        Dim lngUltimaLinha As Long
        Dim i As Long
        Dim blnIncompleto As Boolean
    
        Set ws = ThisWorkbook.Worksheets(1)
        lngUltimaLinha = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
        blnIncompleto = False
    
        ' Se houver linhas preenchidas na tabela
        If lngUltimaLinha >= 2 Then
            For i = 2 To lngUltimaLinha
                ' Se a linha tem ID na coluna A, mas a Placa (B) está vazia
                If ws.Cells(i, 1).Value <> "" And Trim(ws.Cells(i, 2).Value) = "" Then
                    blnIncompleto = True
                    Exit For
                End If
            Next i
        End If
    
        ' Se houver inconsistência grave, BLOQUEIA O SALVAMENTO!
        If blnIncompleto Then
            MsgBox "SALVAMENTO BLOQUEADO PELA GOVERNANÇA DE TI!" & vbCrLf & vbCrLf & _
                   "Existe um ou mais registros com a placa do veículo em branco (Linha " & i & ")." & vbCrLf & _
                   "Preencha todos os dados obrigatórios antes de salvar o arquivo no servidor.", _
                   vbCritical, "Validação de Integridade"
    
            ' O parâmetro Cancel = True impede o Excel de gravar no disco!
            Cancel = True
        End If
    End Sub
  2. Teste da Trava de Salvamento:
    • Volte à planilha do Excel.
    • Na linha 1, configure os cabeçalhos:
      • A1: ID | B1: Placa_Veiculo | C1: Data_Hora_Chegada | D1: Status
    • Na célula A2, digite 1, mas deixe a célula B2 vazia.
    • Pressione o atalho de salvar: Ctrl + B (ou clique no disquete de salvar).
    • Observe o pop-up vermelho surgir bloqueando o salvamento e impedindo a gravação do arquivo incompleto!

✅ Resultado Esperado (Prática 1):

O Excel assume controle ativo sobre o processo de persistência, protegendo o banco de dados contra registros incompletos.


🛠️ Prática Obrigatória 2: O Evento Reativo Worksheet_Change com Target

Agora programaremos a planilha para reagir em tempo real: assim que o fiscal digitar uma placa na coluna B e teclar Enter, o VBA higienizará o texto e carimbará o horário imediatamente na coluna C.

Passo a Passo no Computador:

  1. No Project Explorer do VBE (Alt + F11), dê um duplo clique sobre o item: Planilha1 (Planilha1) (ou o nome da sua primeira aba).
  2. Na janela de código da planilha, digite o evento reativo:
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    
    Option Explicit
    
    ' GATILHO REATIVO: Disparado a cada alteração em qualquer célula desta aba
    Private Sub Worksheet_Change(ByVal Target As Range)
        Dim celula As Range
    
        ' 1. Ignora alterações em bloco de mais de 100 células (ex: colar planilha inteira)
        If Target.Cells.CountLarge > 100 Then Exit Sub
    
        ' 2. Verifica se a célula alterada pertence à Coluna B (Coluna das Placas, da linha 2 em diante)
        If Not Intersect(Target, Me.Range("B2:B1000")) Is Nothing Then
    
            ' BLINDAGEM OBRIGATÓRIA: Desativa escuta de eventos para evitar recursão infinita
            Application.EnableEvents = False
    
            ' Percorre cada célula editada dentro do Target
            For Each celula In Intersect(Target, Me.Range("B2:B1000"))
                If Trim(celula.Value) <> "" Then
                    ' Converte para maiúsculas e remove espaços e traços
                    celula.Value = UCase(Trim(Replace(celula.Value, "-", "")))
    
                    ' Carimba automaticamente a data e hora na Coluna C (Offset de 0 linhas, +1 coluna)
                    celula.Offset(0, 1).Value = Now
                    celula.Offset(0, 1).NumberFormat = "dd/mm/yyyy hh:mm:ss"
    
                    ' Define status automático na Coluna D (Offset de 0 linhas, +2 colunas)
                    celula.Offset(0, 2).Value = "EM PÁTIO / AGUARDANDO DOCA"
                    celula.Offset(0, 2).Font.Bold = True
    
                    ' Se o ID na coluna A estiver vazio, preenche com número sequencial
                    If celula.Offset(0, -1).Value = "" Then
                        celula.Offset(0, -1).Value = celula.Row - 1
                    End If
                Else
                    ' Se o usuário apagou a placa, limpa as colunas vizinhas
                    celula.Offset(0, 1).ClearContents
                    celula.Offset(0, 2).ClearContents
                End If
            Next celula
    
            ' Ajusta a largura das colunas alteradas
            Me.Columns("A:D").AutoFit
    
            ' RESTAURAÇÃO OBRIGATÓRIA: Religia a escuta de eventos
            Application.EnableEvents = True
        End If
    End Sub
  3. Teste Prático na Planilha:
    • Alterne para o Excel.
    • Clique na célula B2 e digite a placa em letras minúsculas: " bra-2e19 " e aperte Enter.
    • Veja a mágica do VBA em tempo real:
      • A placa se transforma instantaneamente em "BRA2E19".
      • O ID 1 surge automaticamente na coluna A.
      • A data e hora exatas com segundos são carimbadas na coluna C.
      • O status "EM PÁTIO / AGUARDANDO DOCA" surge em negrito na coluna D!
    • Digite outra placa na célula B3 (ex: "abc1234"). O mesmo comportamento autônomo se repete instantaneamente!

✅ Resultado Esperado (Prática 2):

A planilha responde com inteligência em tempo real a cada toque do operador, eliminando trabalho braçal de carimbo e digitação redundante.


📤 Instruções de Entrega (Microsoft Teams)

  1. Salve sua pasta de trabalho corporativa com a extensão .xlsm.
  2. Nomeie o arquivo como: Atividade_15_VBA_SeuNome_SeuSobrenome.xlsm.
  3. Acesse a plataforma Microsoft Teams.
  4. Abra a tarefa: “VBA - Cap 15: Automação Orientada a Eventos”.
  5. Anexe sua pasta de trabalho e entregue.

💡 Checkpoint de Gestão & TI

A programação orientada a eventos aproxima o Excel do comportamento dos modernos sistemas de gestão empresarial em tempo real (Real-Time Operations). Ao capturar ações no momento exato em que ocorrem, o sistema garante que auditorias fiscais e tempos operacionais reflitam a verdade exata dos fatos, impedindo fraudes de preenchimento retroativo de horários de chegada.


🔥 Desafio de Fixação: Alerta Visual de Cargas com Retenção

Incremente o evento Worksheet_Change:

  1. Adicione uma checagem na coluna D: se o operador alterar o status para "RETIDO SEFAZ", pinte a linha inteira daquela carga de amarelo com texto vermelho:
    1
    2
    3
    4
    
    If celula.Value = "RETIDO SEFAZ" Then
        celula.EntireRow.Interior.Color = RGB(255, 243, 205)
        celula.Font.Color = RGB(133, 100, 4)
    End If
  2. Teste digitando esse status e observe a formatação condicional reativa em tempo de execução!

🔑 Revelar Gabarito Oficial de Laboratório (Clique para expandir)
💡 Incentivo Pedagógico: Tente resolver as práticas e o desafio de fixação por conta própria antes de abrir o gabarito. Seus erros e tentativas durante o laboratório são a chave para a consolidação real do aprendizado!

Código de EstaPasta_de_trabalho:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
Option Explicit

Private Sub Workbook_Open()
    Dim ws As Worksheet
    Dim lngQtd As Long
    
    Set ws = ThisWorkbook.Worksheets(1)
    lngQtd = ws.Cells(ws.Rows.Count, "B").End(xlUp).Row - 1
    If lngQtd < 0 Then lngQtd = 0
    
    MsgBox "FastLog TMS Inicializado com Sucesso!" & vbCrLf & _
           "Veículos ativos no pátio: " & lngQtd, vbInformation, "Monitor de Pátio"
End Sub

Private Sub Workbook_BeforeSave(ByVal SaveAsUI As Boolean, Cancel As Boolean)
    Dim ws As Worksheet
    Dim i As Long
    
    Set ws = ThisWorkbook.Worksheets(1)
    For i = 2 To ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
        If ws.Cells(i, 1).Value <> "" And Trim(ws.Cells(i, 2).Value) = "" Then
            MsgBox "Não é permitido salvar com a coluna de Placas em branco!", vbCritical, "Erro de Validação"
            Cancel = True
            Exit Sub
        End If
    Next i
End Sub

Código de Planilha1:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Option Explicit

Private Sub Worksheet_Change(ByVal Target As Range)
    Dim celula As Range
    
    If Target.Cells.CountLarge > 100 Then Exit Sub
    
    If Not Intersect(Target, Me.Range("B2:B1000")) Is Nothing Then
        Application.EnableEvents = False
        
        For Each celula In Intersect(Target, Me.Range("B2:B1000"))
            If Trim(celula.Value) <> "" Then
                celula.Value = UCase(Trim(Replace(celula.Value, "-", "")))
                celula.Offset(0, 1).Value = Now
                celula.Offset(0, 1).NumberFormat = "dd/mm/yyyy hh:mm:ss"
                celula.Offset(0, 2).Value = "EM PÁTIO / AGUARDANDO DOCA"
                celula.Offset(0, 2).Font.Bold = True
                
                If celula.Offset(0, -1).Value = "" Then
                    celula.Offset(0, -1).Value = celula.Row - 1
                End If
            Else
                celula.Offset(0, 1).ClearContents
                celula.Offset(0, 2).ClearContents
            End If
        Next celula
        
        Me.Columns("A:D").AutoFit
        Application.EnableEvents = True
    End If
End Sub

📝 Atividade Extra: Questionário de Fixação (Caderno)

Instruções: Responda no caderno, de próprio punho, as 10 perguntas abaixo com base no que você estudou neste capítulo. Ao concluir, apresente o caderno ao professor para visto.

  1. O que é a programação orientada a eventos e em que ela difere da execução de macros tradicionais acionadas por botões?
  2. Em qual local do VBE devemos inserir o procedimento de evento Workbook_Open?
  3. Para que serve o argumento Cancel no evento Workbook_BeforeSave e como ele impede o salvamento da pasta?
  4. O que o objeto Target representa dentro do procedimento de evento Worksheet_Change?
  5. Qual é a finalidade da função Intersect(Target, Range("B2:B100"))?
  6. O que acontece se alterarmos o valor de uma célula dentro do evento Worksheet_Change sem utilizar Application.EnableEvents = False?
  7. Explique como funciona o método .Offset(linhas, colunas) e como ele foi usado para carimbar as colunas vizinhas.
  8. Por que a verificação If Target.Cells.CountLarge > 100 Then Exit Sub é uma boa prática de segurança em eventos de planilha?
  9. O que ocorre se uma macro apresentar erro e terminar a execução sem restaurar Application.EnableEvents = True?
  10. No Cenário Prático da FastLog, que problema operacional a inserção automática de data e hora em tempo real eliminou?