excel - 为列中的单元格值更改创建日志历史记录
问题描述
我创建了一个日志历史工作表并保存其他工作表的更改详细信息。
Dim oldValue As Variant
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
Dim sSheetName As String
sSheetName = "Data"
If ActiveSheet.Name <> "LogDetails" Then
Application.EnableEvents = False
Sheets("LogDetails").Range("A" & Rows.Count).End(xlUp).Offset(1, 0).Value = ActiveSheet.Name & "-" & Target.Address(0, 0)
Sheets("LogDetails").Range("A" & Rows.Count).End(xlUp).Offset(0, 1).Value = oldValue
Sheets("LogDetails").Range("A" & Rows.Count).End(xlUp).Offset(0, 2).Value = Target.Formula
Sheets("LogDetails").Range("A" & Rows.Count).End(xlUp).Offset(0, 3).Value = Environ("username")
Sheets("LogDetails").Range("A" & Rows.Count).End(xlUp).Offset(0, 4).Value = Now
Sheets("LogDetails").Columns("A:D").AutoFit
Application.EnableEvents = True
End If
End Sub
Private Sub Workbook_SheetSelectionChange(ByVal Sh As Object, ByVal Target As Range)
oldValue = Target.Formula
End Sub
这适用于单个单元格,例如:
如果 A1 存储“ABC”并更改为“123”,Log Detail 将保存 Cell 地址、旧值、新值、用户名和日期/时间。
最大的问题是当我选择一整列时,例如所有列(B)。它会得到错误
“类型不匹配”。
我知道问题是
oldValue = Target.Value
如何保存列的更改?
解决方案
我不完全确定这是否能解决您的问题,但请尝试对您的Workbook_SheetSelectionChange
程序进行以下修改:
Private Sub Workbook_SheetSelectionChange(ByVal Sh As Object, ByVal Target As Range)
If Target.Count > 1 Then
Target(1).Select
Exit Sub
End If
oldValue = Target.Value
oldAddress = Target.Address
End Sub
每次用户选择多个单元格时,事件过程都会更改该选择(这会引发另一个更改事件,这次是单个单元格目标)并退出而不做任何其他事情。当然,可以细化何时应该发生这种选择变化的标准,以允许更具体的行为。
这应该会使普通用户一次有意或无意地修改多个单元格变得更加困难。
要解决您评论中的问题:
excel的undo功能无法使用
这是真实的。Excel 不知道如何撤销您的代码已执行的操作。您需要自己构建此功能。看到这个问题+接受的答案。
日志表中显示的公式更改无法正确显示,它将显示
0
或#Value!
是的,这是设计使然。随着线
Sheets("LogDetails").Range("A" & Rows.Count).End(xlUp).Offset(0, 2).Value = Target.Formula
您告诉 Excel 将该单元格的值设置为公式。然后 Excel 会自动尝试评估。(导致您遇到的错误)
尝试以下操作:
' Prepend the formula with an apostrophe
Sheets("LogDetails").Range("A" & Rows.Count).End(xlUp).Offset(0, 2).Value = "'" & Target.Formula
这将强制 Excel 将单元格值视为文本,因此它将仅显示公式而不对其进行评估。
复制和粘贴一个范围只显示第一个单元格更改,它不能修复吗?
这是因为它oldValues
是一个数组,而您只能访问它的第一个值。查看我的实现:
Option Explicit
Dim oldValues As Variant
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
Const LogSheet As String = "LogDetails"
If Sh.Name = LogSheet Then Exit Sub
Application.EnableEvents = False
With Worksheets(LogSheet)
Dim idxRows As Long
For idxRows = 1 To Target.Rows.Count
Dim idxCols As Long
For idxCols = 1 To Target.Columns.Count
Dim ChangedCell As Range
Set ChangedCell = Target.Rows(idxRows).Columns(idxCols)
Dim LogRow As Long
LogRow = .Range("A" & Rows.Count).End(xlUp).Row + 1
Dim LogRange As Range
Set LogRange = .Range(.Cells(LogRow, 1), .Cells(LogRow, 5))
LogRange(1).Value = Sh.Name & "!" & ChangedCell.Address(False, False)
LogRange(2).Value = "'" & oldValues(idxRows, idxCols) ' error here when pasting a range of different size than has been selected before pasting
LogRange(3).Value = ChangedCell.Formula
LogRange(4).Value = Environ("username")
LogRange(5).Value = Now
Next idxCols
Next idxRows
.Columns("A:E").AutoFit
End With
Application.EnableEvents = True
End Sub
Private Sub Workbook_SheetSelectionChange(ByVal Sh As Object, ByVal Target As Range)
oldValues = Target.Formula
End Sub
这有一个弱点,当用户复制多个单元格然后选择一个单元格并粘贴时,由于索引不匹配,它会出错。(当您复制时有效,例如连续复制 3 个单元格,然后连续选择 3 个其他单元格并粘贴。)不知道如何避免这种情况。我们需要捕获粘贴范围的大小以进行oldValues
相应更新。由于 Excel 不公开Workbook_SheetBeforePaste
事件,这似乎相当棘手。
推荐阅读
- python - 如何编写 python 脚本以在 Window 中运行现有的任务调度程序
- javascript - Lambda@Edge 在源请求上设置 Cookie
- java - 保存 Hibernate JPA 实体后外键为空
- react-native - React Native 在两个类组件中传递和获取参数
- laravel - Laravel 8 中的后台任务
- asp.net - System.ComponentModel.Win32Exception:等待操作超时错误
- c# - .NET Core 视图在 IIS 中部署后未更新
- php - 在 HTML 表格的每个单元格中添加增加/减少按钮并更新数据库中的值
- java - Spring Cloud LoadBalancer 当前使用默认缓存。您可以通过将其添加到类路径来切换到使用 Caffeine 缓存
- sql - 在旧 SQL 中拆分字符不同