【vba】Excel 记录/修改 记录的更新时间(使用Worksheet_Change、Workbook_SheetChange事件)

功能概述:

  用指定列来记录对应行数据是否有更新,如果更新(增删改),则将当前日期记录到对应单元格中。

一、运行前准备

运行前准备二选一,由于Excel2007版本以上,基于安全考虑微软将Excel数据文件与脚本文件分离,数据文件xlsx,脚本文件xlsm。但是2003以下版本数据文件和脚本文件是可以同时保存的。

所以如果是2007以上版本,要么另存后xlsm后添加运行脚本;要么另存为xls后添加运行脚本。

以下是两种方案

1、基于2007以上版本 -xlsx

  1)修改“保存时从文件属性中删除个人信息(R)”,取消√

设置调整后保存vba脚本才不会报错

 

  2)Excel文件另存为.xlsm文件,因为从2003版本以后为了安全,Excel文件跟脚本文件分开。

在.xlsx下保存vba脚本时报错信息

  

2、基于2003以下版本-xls

将文件另存为2003以下版本.xls 

 

二、添加脚本代码

1、鼠标右键单击工作表——“查看代码”   或通过快捷键“Alt+F11”

2、将代码粘贴进去,并保存关闭即可。

 

三、代码内容1-基于工作表Worksheet

'功能概述:用指定列来记录对应行数据是否有更新,如果更新(增删改),则将当前日期记录到对应单元格中。
'实现逻辑
'1.通过Worksheet_SelectionChange事件获取修改前的值
'2.通过变量tagCol设置要记录修改记录的列
'3.循环判断修改后的单元格所在的行(除用来记录修改记录的单元格tagCol外)是非空nulFlag=True并直接退出循环
'4.判断修改的列为非tagCol列,且该行有记录非空nulFlag=True,且修改前和修改后的值不相等,则将tagCol的值修改为当前日期date()
'5.如果tagCol为空nulFlag=False,则清空tagCol

Option Explicit
Dim oldValue
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
'用此事件获取修改前的值
    oldValue = Target.Value
End Sub

Private Sub Worksheet_Change(ByVal Target As Range)
'On Error Resume Next  '跳过异常
On Error GoTo MyErr
'用此事件获取修改后的值
    Dim row '事件所在的行
    Dim tagCol, col '记录时间的列
    Dim nulFlag '对应行数据单元格是否非空
    Dim i
    row = Target.row
    col = Target.Column
    tagCol = 8 ' 第8列 "H",即
    nulFlag = False '用来判断光标所在的行是否为非空,非空为True,空为False
    
    '启用事件
    'Application.EnableEvents = True
    
    '判断从该行从1-8内容是否非空
    For i = 1 To tagCol - 1 '最后一列除外
        If Application.WorksheetFunction.CountA(Cells(row, i)) <> 0 Then
            nulFlag = True
            Exit For '如果判断目标行有非空单元格,则退出循环
        End If
    Next
    
    
    '非tagCol值发生变化,更新tagCol列
    If col <> tagCol And nulFlag = True And oldValue <> Target.Value Then
        Cells(row, tagCol) = Date
    End If
    If nulFlag = False Then
        Cells(row, tagCol) = ""
    End If
    
    '禁用事件
    'Application.EnableEvents = False
MyErr:
    'MsgBox " 错误 " & Err.Number & " : " & Err.Description
    Resume Next
End Sub

四、代码逻辑2-基于工作簿Workbook

基于“三、代码内容1-基于工作表Worksheet” 的代码逻辑存在性能和逻辑上的瓶颈,说明如下:

1、以上基于工作表Worksheet的事件,每个工作表都要copy一份代码,而基于工作簿Workbook的事件只需要一份代码即可。

2、以上判断整列是否为空,为空则不处理需要循环逐条判断,判断次数较多,效率低;本次直接对整行使用CountA函数,仅进行一次判断,统计为0则退出事件

3、怎么使用?打开VBE(方式之一:鼠标右键单击工作表名称,例如Shee1——查看代码——将代码复制到ThisWorkbook中),如图:

4、代码逻辑如下:

Dim oldvalue '全局变量,用来记录单元格修改前的值

Private Sub Workbook_SheetSelectionChange(ByVal Sh As Object, ByVal Target As Range)
    '记录单元格修改前的值
    oldvalue = Target.Value
End Sub

Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
    On Error Resume Next  '跳过异常
    
    Application.ScreenUpdating = False '关闭屏幕更新
'排除特定工作表,如果工作表名称时如下名称,则退出事件 If Sh.Name = "转置" Or Sh.Name = "工资条" Or Sh.Name = "商品名称" Then Exit Sub End If Dim tagCol tagCol = 8 '“修改日期”字段所在工作表的列数,此表为第8列,用逻辑来识别该参数逻辑较为复杂(效率比较低),直接写死即可 '判断第1列到“修改日期”列中是否有值,没有值则退出事件 If Application.WorksheetFunction.CountA(Range(Cells(Target.row, 1), Cells(Target.row, tagCol))) = 0 Then Exit Sub End If '如果同时选择操作多个单元格,仅处理选中区域内的第一个单元格 If Target.Column > 1 Then Set Target = Target.Cells(1) End If '判断新旧值不相等,且修改的单元格的列要在“修改日期”的列之前(不包含) If Target.Value <> oldvalue And Target.Column < tagCol Then Cells(Target.row, tagCol) = Date End If
Application.ScreenUpdating = True '恢复屏幕更新 End Sub

五、测试

修改其中一个值,观察“修改日期”单元格内容是否变化

 

posted @ 2018-09-25 23:19  航松先生  阅读(14957)  评论(0编辑  收藏  举报