跟踪工作表上的更改,复制活动单元格更改 Column Header & NewColumnValue

Track changes on worksheet, copy active cell change Column Header & NewColumnValue

REF: 跟踪工作表上的更改,复制活动单元格行中非活动单元格的单元格并记录值

我修改了我的工作表并希望深入了解如何在更改事件发生时获取单元格中的值...

非常感谢任何关于代码整合的想法。我被迫做 'long division' 方法,很高兴看到如何获得 'calculator' 方法,帮助我学习。

Option Explicit
Public OldValue, OldColumnJValue, ColumnHeaderX, ColumnJValue, ColumnHeader, OldColumnJJValue, 
OldColumnJKValue, OldColumnJLValue, OldColumnJMValue, NewColumnJJValue, NewColumnJKValue, 
NewColumnJLValue, NewColumnJMValue, OldColumnMPValue, OldColumnMQValue, OldColumnMRValue, 
OldColumnMSValue, NewColumnMPValue, NewColumnMQValue, NewColumnMRValue, NewColumnMSValue, 
OldColumnPVValue, OldColumnPWValue, OldColumnPXValue, OldColumnPYValue, NewColumnPVValue, 
NewColumnPWValue, NewColumnPXValue, NewColumnPYValue

Private Sub Worksheet_Change(ByVal Target As Range)
    Application.EnableEvents = False
        With Sheets("LogDetails").Range("A" & Rows.Count).End(xlUp)
            .Offset(1, 0) = ActiveSheet.Name
            .Offset(1, 1) = Target.Address(0, 0)
            .Offset(1, 2) = Environ("username")
            .Offset(1, 3) = Now
                'add empl name vlookup formula to this column?
            .Offset(1, 5) = ColumnJValue
            **.Offset(1, 6) = ColumnHeader**
            .Offset(1, 7) = OldValue
            .Offset(1, 8) = Target
                '2020 pre-change value below
            .Offset(1, 9) = OldColumnJJValue
            .Offset(1, 10) = OldColumnJKValue
            .Offset(1, 11) = OldColumnJLValue
            .Offset(1, 12) = OldColumnJMValue
                '2020 post-change value below
            **.Offset(1, 13) = NewColumnJJValue
            .Offset(1, 14) = NewColumnJKValue
            .Offset(1, 15) = NewColumnJLValue
            .Offset(1, 16) = NewColumnJMValue**
                '2021 pre-change value below
            .Offset(1, 18) = OldColumnMPValue
            .Offset(1, 19) = OldColumnMQValue
            .Offset(1, 20) = OldColumnMRValue
            .Offset(1, 21) = OldColumnMSValue
                '2021 post-change value below
            **.Offset(1, 22) = NewColumnMPValue
            .Offset(1, 23) = NewColumnMQValue
            .Offset(1, 24) = NewColumnMRValue
            .Offset(1, 25) = NewColumnMSValue**
                '2022 pre-change value below
            .Offset(1, 27) = OldColumnPVValue
            .Offset(1, 28) = OldColumnPWValue
            .Offset(1, 29) = OldColumnPXValue
            .Offset(1, 30) = OldColumnPYValue
                '2022 post-change value below
            **.Offset(1, 31) = NewColumnPVValue
            .Offset(1, 32) = NewColumnPWValue
            .Offset(1, 33) = NewColumnPXValue
            .Offset(1, 34) = NewColumnPYValue**
        End With
    Application.EnableEvents = True
End Sub


Private Sub Worksheet_SelectionChange(ByVal Target As Range)
    If Selection.Cells.Count = 1 Then
        OldValue = Target
           'Program name changed
        ColumnJValue = Range("A1")(Target.Row, 10)
           'Column header of changed cell
        **ColumnHeader = Range("A1")(Target.Row, (2, 0)**
           '2020 pre-change value below
        OldColumnJJValue = Range("A1")(Target.Row, 270)
        OldColumnJKValue = Range("A1")(Target.Row, 271)
        OldColumnJLValue = Range("A1")(Target.Row, 272)
        OldColumnJMValue = Range("A1")(Target.Row, 273)
           '2020 post-change value below
        **NewColumnJJValue = Range("A1")(Target.Row, 270)
        NewColumnJKValue = Range("A1")(Target.Row, 271)
        NewColumnJLValue = Range("A1")(Target.Row, 272)
        NewColumnJMValue = Range("A1")(Target.Row, 273)**
           '2021 pre-change value below
        OldColumnMPValue = Range("A1")(Target.Row, 354)
        OldColumnMQValue = Range("A1")(Target.Row, 355)
        OldColumnMRValue = Range("A1")(Target.Row, 356)
        OldColumnMSValue = Range("A1")(Target.Row, 357)
           '2021 post-change value below
        **NewColumnMPValue = Range("A1")(Target.Row, 354)
        NewColumnMQValue = Range("A1")(Target.Row, 355)
        NewColumnMRValue = Range("A1")(Target.Row, 356)
        NewColumnMSValue = Range("A1")(Target.Row, 367)**
           '2022 pre-change value below
        OldColumnPVValue = Range("A1")(Target.Row, 438)
        OldColumnPWValue = Range("A1")(Target.Row, 439)
        OldColumnPXValue = Range("A1")(Target.Row, 440)
        OldColumnPYValue = Range("A1")(Target.Row, 441)
           '2022 post-change value below
        **NewColumnPVValue = Range("A1")(Target.Row, 438)
        NewColumnPWValue = Range("A1")(Target.Row, 439)
        NewColumnPXValue = Range("A1")(Target.Row, 440)
        NewColumnPYValue = Range("A1")(Target.Row, 441)**
        Exit Sub
    End If
    MsgBox "Multiple cell selections are not allowed on this sheet", vbCritical
    ActiveCell.Select
End Sub

很高兴从您的评论中了解到您解决了问题。做得好!我确实认为你处理这项任务非常英勇,既不想丢弃我的工作,也不想剥夺你可能获得的好处。请多多关照。

Option Explicit

Private PrevVal(1)  As Variant      ' previously selected row data
                                    ' PrevVal(0) = row number, PrevVal(1) = row's data
Enum Nws                            ' data tab (ActiveSheet)
    ' 147
    NwsHeaderRow = 2                ' change to suit (data start immediately below this row)
    NwsClmJ = 10                    ' Debug.Print Columns("J").Column
    NwsClmJJ = 270
    NwsClmJK                        ' no assigned value means preceding + 1
    NwsClmJL
    NwsClmJM
    NwsClmMP = 354
    NwsClmMQ
    NwsClmMR
    NwsClmMS
    NwsClmPV = 438
    NwsClmPW
    NwsClmPX
    NwsClmPY
    NwsTop                      ' defining the last used column
End Enum

Private Sub Worksheet_Activate()
    ' 147
    SetPrevVal ActiveCell.Row
End Sub

Private Sub Worksheet_SelectionChange(ByVal Target As Range)
    ' 147
    SetPrevVal Target.Row
End Sub

Private Sub Worksheet_Change(ByVal Target As Range)
    ' 147
    
    Dim TriggerRange    As Range        ' range of relevant changes
    Dim MsgTxt()        As String       ' error message
    Dim Log(1 To 34)    As Variant      ' Log entry
    Dim Employee        As String       ' employee's name (retrieved by VLOOKUP)
    Dim i               As Long         ' index of Log()
    
    Set TriggerRange = Range(Cells(NwsHeaderRow + 1, 1), _
                             Cells(Rows.Count, "A").End(xlUp)) _
                             .Resize(, NwsTop - 1)
                             Debug.Print TriggerRange.Address
    With Target
        If Not Application.Intersect(Target, TriggerRange) Is Nothing Then
            If .Cells.CountLarge > 1 Then
                MsgTxt = Split("Please change only one cell at a time on this sheet." & _
                             "|Unsupported user action", "|")
            Else
                If IsEmpty(PrevVal) Then
                    MsgTxt = Split("?")
                ElseIf PrevVal(0) <> .Row Then
                    MsgTxt = Split("?")
                End If
                If Join(MsgTxt) = "?" Then
                    MsgTxt = Split("Sorry, I lost the previous record." & vbCr & _
                                   "Please repeat the action." & _
                                 "|Internal error", "|")
                End If
            End If
            If Len(Join(MsgTxt)) Then
                MsgBox MsgTxt(0), vbCritical, MsgTxt(1)
                Application.Undo
                .Select
                Exit Sub
            End If
            
            Employee = ""        ' add empl name vlookup formula here
            For i = 1 To 8
                Log(i) = Array(Environ("username"), Employee, Now, _
                               ActiveSheet.Name, Cells(.Row, NwsClmJ).Value, _
                               .Address(0, 0), PrevVal(1)(1, .Column), _
                               .Value)(i - 1)
            Next i
            
            For i = 9 To 12
                Log(i) = PrevVal(1)(1, NwsClmJJ + i - 9)
                Log(i + 4) = Cells(.Row, NwsClmJJ + i - 9).Value
            Next i
            ' column 17 remains blank by your design
            
            For i = 18 To 21
                Log(i) = PrevVal(1)(1, NwsClmMP + i - 18)
                Log(i + 4) = Cells(.Row, NwsClmMP + i - 18).Value
            Next i
            ' column 26 remains blank by your design
            
            For i = 27 To 30
                Log(i) = PrevVal(1)(1, NwsClmPV + i - 27)
                Log(i + 4) = Cells(.Row, NwsClmPV + i - 27).Value
            Next i
        
            With Application
                .EnableEvents = False
                .ScreenUpdating = False
            End With
            
            With Worksheets("Log")
                .Cells(.Rows.Count, "A").End(xlUp).Offset(1) _
                       .Resize(1, UBound(Log)).Value = Log
            End With
                
            With Application
                .EnableEvents = True
                .ScreenUpdating = True
            End With
        End If
    End With
End Sub

Private Function SetPrevVal(ByVal R As Long) As Range
    ' 147
    
    Dim Rl      As Long         ' last used row in column [270]
    
    ' presuming that column 1 offers a relevant measurement
    Rl = Cells(Rows.Count, 1).End(xlUp).Row
    
    ' don't record if the selection is in or above the caption row (NwsHeaderRow)
    ' or below the data range as defined by the end of 'FirstCl'
    ' you might add an exception for columns < `NwsClmJJ` ??
    If (R > NwsHeaderRow) And (R <= Rl) Then
        PrevVal(0) = R
        ' presuming that there is a header for every column in Header Row
        PrevVal(1) = DataRange(R).Value
    End If
End Function

Private Function DataRange(ByVal R As Long) As Range
    ' 147
    
    ' presuming that there is a header for every column in the Header Row
    Set DataRange = Range(Cells(R, 1), Cells(NwsHeaderRow, Columns.Count).End(xlToLeft) _
                          .Offset(R - NwsHeaderRow))
End Function

由于缺乏数据,我对这段代码做了一些非常有限的测试:它确实创建了一个日志条目,主要是按照您的代码似乎建议的行。一旦您掌握了它,它就会像您自己的一样透明,这让我希望您能够遵循它,例如,插入缺少的员工姓名。添加日期或列非常容易,而且比您拥有的要快得多。评论也不少,应该会有帮助。

关于“旧”值的一个词。我用一个数组替换了你的许多 public 变量,该数组在一行中记录了 all 数据。它在加载(激活)sheet 时以及此后每次单击时执行此操作。如果由于某种原因(这主要发生在测试期间)阵列不可用或错误的阵列可用,用户将被提示重复他的更改。比较水密