我创建一个日志历史记录工作表并保存其他工作表的更改详细信息。
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”,则日志详细信息将保存单元格地址,旧值,新值,用户名和日期/时间。
最大的问题是当我选择整个列,例如所有列(B)时。它将得到错误
“类型不匹配”。
我知道问题是
oldValue = Target.Value
如何保存列的更改?
答案 0 :(得分:0)
我不确定是否能解决您的问题,但是请尝试对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的撤消功能
是真的。 Excel不知道如何撤销您的代码已采取的操作。您需要自己构建此功能。 See this question + accepted answer。
日志表中显示的公式更改无法正确显示,它将显示
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
事件,所以这似乎很棘手。