为列中的单元格值更改创建日志历史记录

时间:2018-11-21 09:42:59

标签: excel vba

我创建一个日志历史记录工作表并保存其他工作表的更改详细信息。

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

如何保存列的更改?

1 个答案:

答案 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事件,所以这似乎很棘手。