乐于分享
好东西不私藏

从0学Excel VBA编程 第12篇:事件编程——让代码"自动触发"

从0学Excel VBA编程 第12篇:事件编程——让代码"自动触发"

从0学Excel VBA编程 第12篇:事件编程——让代码"自动触发"

学习目标

  1. 理解什么是事件,知道事件代码写在哪里
  2. 掌握 Workbook_Open 和 Worksheet_Change 两个最常用的事件
  3. 学会用 EnableEvents 防止事件"无限循环"

知识点精讲

从第1篇到第11篇,我们写的所有代码都有一个共同点——必须手动按 F5 才能运行

这一篇,我们要学一种全新的方式:事件(Event)。

事件就是——当某个操作发生时,代码自动运行。不需要你按 F5,不需要你点任何按钮,Excel 会自己"感应"到操作并执行对应的代码。

生活中的事件类比

想象一下自动门:你走过去,门自动开了。你不需要按开关,门上有个传感器,检测到人靠近就自动开门。事件就是这个"传感器"。

在 Excel 里:

  • 打开文件 → 自动触发 Workbook_Open 事件
  • 修改单元格 → 自动触发 Worksheet_Change 事件
  • 关闭文件 → 自动触发 Workbook_BeforeClose 事件

事件代码写在哪里?(关键!)

这是本篇最重要的知识点。事件代码不能写在普通模块里,必须写在特定位置:

事件类型
代码位置
怎么找
工作簿事件(打开/关闭文件)
ThisWorkbook
VBA 编辑器左侧工程管理器 → 双击 ThisWorkbook
工作表事件(编辑单元格等)
对应的工作表
VBA 编辑器左侧工程管理器 → 双击 Sheet1(或其他表名)

注意: 如果你把事件代码写在了普通模块(Module1)里,它不会自动触发,写了等于白写。

事件过程的样子

普通过程长这样:

Sub 我的宏()    ' 代码内容End Sub

事件过程长这样:

Private Sub Worksheet_Change(ByVal Target As Range)    ' 代码内容End Sub

区别有两点:

  1. 多了 Private——表示这个过程只能在当前模块里用,不会出现在宏列表里
  2. 过程名是固定的——Worksheet_Change 是 Excel 规定的名字,一个字都不能改,改了就不触发了
  3. 括号里有 ByVal Target As Range——这是 Excel 传给你的参数,告诉你是哪个单元格被改了

Target 参数详解

当用户修改了某个单元格,Excel 会自动把那个单元格作为 Target 传给事件过程。

Target.Column   ' 被修改的单元格在第几列(1=A列,2=B列,3=C列)Target.Row      ' 被修改的单元格在第几行Target.Value    ' 被修改后的新值Target.Offset(0, 1)   ' 同一行、右边一列的单元格(Offset 表示偏移)

Offset(行偏移, 列偏移) 是一个很方便的用法:

  • Offset(0, 1) = 同一行,往右1列
  • Offset(1, 0) = 往下1行,同一列
  • Offset(-1, 0) = 往上1行,同一列

EnableEvents:防止无限循环(必学!)

Worksheet_Change 会在单元格被修改时触发。但如果你的事件代码本身也在修改单元格,会怎样?

修改单元格 → 触发事件 → 事件代码修改单元格 → 再次触发事件 → 再次修改……

这就是"无限循环",Excel 会卡死。

解决办法:在修改单元格之前,用 Application.EnableEvents = False 暂时关闭事件感应,改完之后再 = True 恢复。

Application.EnableEvents = False   ' 关闭事件感应Target.Offset(0, 1).Value = Now    ' 修改单元格(不会触发事件)Application.EnableEvents = True    ' 恢复事件感应

记住这个三步走:关闭 → 操作 → 恢复,缺一不可。

实战案例

案例 1(简单):打开文件自动显示欢迎信息

功能说明: 当用户打开这个 Excel 文件时,自动弹出一个欢迎框,显示今天的日期和文件名。

Private Sub Workbook_Open()    MsgBox "欢迎使用本工作簿!" & vbCrLf & _           "今天是 " & Format(Date, "yyyy年mm月dd日") & vbCrLf & _           "文件名:" & ThisWorkbook.Name, _           vbInformation, "欢迎"End Sub

操作步骤:

  1. 按 Alt + F11 打开 VBA 编辑器
  2. 在左侧工程管理器中,双击 ThisWorkbook(不是右键插入模块!)
  3. 在右侧代码窗口顶部,确认左下拉框显示 (General),右下拉框显示 (Declarations)
  4. 从左下拉框选择 Workbook,Excel 会自动帮你写好 Private Sub Workbook_Open() 的框架
  5. 在框架内填入上面的代码
  6. 保存文件(必须保存为 .xlsm 格式,即"Excel 启用宏的工作簿")
  7. 关闭文件,重新打开——欢迎信息会自动弹出!

代码解释:

  • Private Sub Workbook_Open():这是工作簿"打开时"的事件,名字固定不能改
  • Format(Date, "yyyy年mm月dd日"):把今天日期格式化成"2026年08月14日"这种中文格式
  • ThisWorkbook.Name:当前文件的文件名
  • vbInformation:消息框上显示一个蓝色"i"信息图标
  • vbCrLf:换行符,让消息框里分多行显示

案例 2(中等):编辑 A 列自动在 B 列记录时间

功能说明: 当你在 A 列(从第2行开始)输入任何内容时,B 列自动写入当前时间,省去手动打时间的麻烦。

Private Sub Worksheet_Change(ByVal Target As Range)    ' 只关心 A 列(第1列),且从第2行开始(跳过第1行标题)    If Target.Column = 1 And Target.Row > 1 Then        ' 暂时关闭事件,防止改 B 列时再次触发        Application.EnableEvents = False        ' 在同行 B 列写入当前时间        Target.Offset(0, 1).Value = Format(Now, "hh:nn:ss")        ' 重新开启事件        Application.EnableEvents = True    End IfEnd Sub

操作步骤:

  1. 按 Alt + F11 打开 VBA 编辑器
  2. 在左侧工程管理器中,双击 Sheet1(不是插入模块!)
  3. 从左下拉框选择 Worksheet,Excel 会自动帮你写好事件框架
  4. 从右下拉框选择 Change,Excel 会生成 Private Sub Worksheet_Change 的框架
  5. 在框架内填入上面的代码
  6. 回到 Excel,在 A1 单元格写上"任务名称"作为标题
  7. 在 A2 单元格输入任意内容(比如"写报告"),按回车
  8. 看 B2 单元格——自动出现了当前时间!

代码解释:

  • ByVal Target As Range:Excel 自动传进来的参数,就是被修改的那个单元格
  • Target.Column = 1:判断被修改的单元格是否在 A 列(第1列)
  • Target.Row > 1:跳过第1行(标题行)
  • Application.EnableEvents = False:关闭事件感应,这样写 B 列时不会再触发事件
  • Target.Offset(0, 1).Value = ...:Offset(0, 1) 表示同一行往右1列,即 B 列,写入当前时间
  • Format(Now, "hh:nn:ss"):把当前时间格式化为"14:30:25"这样的格式
  • Application.EnableEvents = True:恢复事件感应,别忘了这一步!

如果把 EnableEvents 那两行去掉会怎样? 你在 A 列输入内容 → 触发事件 → 事件往 B 列写时间 → B 列被修改又触发事件 → 事件又往 B 列写时间 → 又触发……Excel 直接卡死。这就是为什么 EnableEvents 必须要加。

案例 3(实用小案例):输入状态自动变色

功能说明: 在 C 列输入任务状态时,单元格自动变色——"完成"变绿色、"进行中"变黄色、"待处理"变红色,其他内容清除颜色。一目了然,不用手动设置格式。

Private Sub Worksheet_Change(ByVal Target As Range)    ' 只关心 C 列(第3列),且从第2行开始    If Target.Column = 3 And Target.Row > 1 Then        Application.EnableEvents = False        Dim 状态 As String        状态 = Target.Value        If 状态 = "完成" Then            Target.Interior.Color = RGB(200, 255, 200)        ElseIf 状态 = "进行中" Then            Target.Interior.Color = RGB(255, 255, 200)        ElseIf 状态 = "待处理" Then            Target.Interior.Color = RGB(255, 200, 200)        Else            Target.Interior.ColorIndex = xlNone        End If        Application.EnableEvents = True    End IfEnd Sub

操作步骤:

  1. 按 Alt + F11 打开 VBA 编辑器
  2. 双击左侧的 Sheet1(如果案例2已经写在这了,就把代码接在下面——一个工作表可以有多个事件过程)
  3. 如果案例2已经用了 Worksheet_Change,你需要把案例3的逻辑合并到同一个Worksheet_Change 里(Excel 只认一个)
  4. 合并后的代码如下方"合并版"所示
  5. 回到 Excel,在 C1 写上"状态"做标题
  6. 在 C2 输入"完成"→ 变绿;C3 输入"进行中"→ 变黄;C4 输入"待处理"→ 变红
  7. 输入其他内容 → 颜色自动清除

如果案例2和案例3都要用,合并版代码:

Private Sub Worksheet_Change(ByVal Target As Range)    Application.EnableEvents = False    ' A 列:自动记录时间到 B 列    If Target.Column = 1 And Target.Row > 1 Then        Target.Offset(0, 1).Value = Format(Now, "hh:nn:ss")    End If    ' C 列:自动变色    If Target.Column = 3 And Target.Row > 1 Then        Dim 状态 As String        状态 = Target.Value        If 状态 = "完成" Then            Target.Interior.Color = RGB(200, 255, 200)        ElseIf 状态 = "进行中" Then            Target.Interior.Color = RGB(255, 255, 200)        ElseIf 状态 = "待处理" Then            Target.Interior.Color = RGB(255, 200, 200)        Else            Target.Interior.ColorIndex = xlNone        End If    End If    Application.EnableEvents = TrueEnd Sub

代码解释:

  • Target.Interior.Color = RGB(200, 255, 200):用 RGB 函数设置背景色,三个数字分别是红、绿、蓝,范围 0-255
  • RGB(200, 255, 200):红色少、绿色多 → 浅绿色
  • RGB(255, 255, 200):红绿都多、蓝少 → 浅黄色
  • RGB(255, 200, 200):红色多、绿蓝少 → 浅红色
  • Target.Interior.ColorIndex = xlNone:清除背景色,xlNone 是 Excel 的内置常量,表示"无颜色"

本篇小结

今天我们学的是 VBA 的一个重要进阶能力——事件编程

  • 事件 = 自动触发:不需要按 F5,操作发生时代码自动运行
  • 代码位置很重要:工作簿事件写在 ThisWorkbook,工作表事件写在对应工作表,写错地方就不触发
  • Workbook_Open:文件打开时自动执行,适合做欢迎提示、初始化设置
  • Worksheet_Change:单元格被修改时自动执行,适合做自动填表、数据验证、自动变色
  • Target 参数:告诉你哪个单元格被改了,用 .Column.Row.Value 获取信息
  • EnableEvents 三步走:关闭 → 操作 → 恢复,防止事件无限循环

重要提醒: 事件代码需要把文件保存为 .xlsm 格式(Excel 启用宏的工作簿)。如果保存为普通 .xlsx 格式,所有宏代码都会丢失。

下一篇内容预告

第 13 篇:自定义函数——打造你专属的公式

到目前为止,我们写的都是 Sub 过程——"做事型"代码,运行完就结束了。下一篇我们要学 Function——"计算型"代码,它能像 Excel 公式一样返回一个结果,甚至可以在单元格里直接使用 =我的函数() 来调用。掌握 Function,你就能自己造公式了,敬请期待。