0

I would like to get previous cells values when I copy single cell and paste it to multiple cells. The How can I determine new & previous cell value on SheetChange event in Excel? is good enough for detecting single cell previous value. However when I am trying to copy one cell (ctrl+v, dragging etc) and apply it to multiple cells, none of the previous values are detected. Instead, the array of values are equal to the first cell, which leads me to the conclusion that cells are changed before SheetSelectionChange event occures. Any idea how to handle this?

private void Application_SheetSelectionChange(object Sh, Excel.Range Target)
    {
        try
        {
            if (Target.Value2 != null)
            {
                foreach (Excel.Range range in Target)
                {
                   // Each range in Target has same value as first value instead of previous value
                }
            }
        }
        catch (Exception ex)
        {
           // Log stuff
        }
    }
Community
  • 1
  • 1
Jim
  • 2,760
  • 8
  • 42
  • 66
  • This thread will help you. ( http://stackoverflow.com/questions/35617755/excel-macro-cell-address-increase/35618080?noredirect=1#comment58965368_35618080 ) – Sixthsense Feb 29 '16 at 07:08
  • You can use Application.Undo to get the previous value(s). In VBA : http://stackoverflow.com/questions/35018093/excel-detecting-and-keeping-track-of-value-changes-in-any-worksheet/35075434#35075434 – Tim Williams Feb 29 '16 at 07:10
  • @TimWilliams Unfortunately Undo is not prefered as it has side effect of getting back to previous cell when you click enter, causing user frustration – Jim Feb 29 '16 at 07:14
  • @Sixthsense I am not sure your solution can be applied as it seems that the SheetSelectionChange doesn't detect propertly paste of values. Meaning that the event is fired after the values are changed – Jim Feb 29 '16 at 07:16
  • Then you should combine both sheet_change and selection_change events. – Sixthsense Feb 29 '16 at 07:25
  • @Sixthsense I am actually doing that because my goal is to get previous and current cell values, which is working fine for single cells. However, the issue occurs when you paste to multiple cells, where it seems that all previous values are overwritten by new values before any event ocures. – Jim Feb 29 '16 at 08:30
  • Quote:"the issue occurs when you paste to multiple cells, where it seems that all previous values are overwritten by new values before any event ocures". It's not true, since while pasting the data each cell values will be changed by the paste process which surely triggers change event for all cells so change even surely occurs on each cells. Watch it closed with target.address in debug.print window to see how change event runs when pasting the data over a range of cells. – Sixthsense Feb 29 '16 at 08:50
  • it's true for nearly all modes of writing in a cell (copy/paste, drag and drop) except for when you grab its lower-right corner and drag it in adjacent cells. that's why I posted the solution some minutes ago – user3598756 Feb 29 '16 at 08:56
  • @Sixthsense, I am checking the VS output window but no change events appear.. can you ellaborate bit more how to actually monitor events firing. – Jim Feb 29 '16 at 15:53
  • You can manage the selection as part of the tracking process - see my answer below. – Tim Williams Feb 29 '16 at 17:13
  • Does this answer your question? [How do I get the old value of a changed cell in Excel VBA?](https://stackoverflow.com/questions/4668410/how-do-i-get-the-old-value-of-a-changed-cell-in-excel-vba) – Janine White Feb 08 '22 at 21:42

2 Answers2

1

I'm afraid to achieve your goal monitoring all sheet cells you have to:

  • make a "mirror" copy of the whole "base" sheet

    each cell of which will have a reference to the corresponding cell in the "base" sheet (i.e. "mirror" sheet A1 cell will have "="baseSheetName!A1" formula", and so on)

  • set Application.Calculation = xlCalculationManual before any changing in "base" sheet (possibly set it as default configuration of your workbook at its opening)

  • use Target argument of Worksheet_SelectionChange() event handler to select corresponding "Mirror" sheet cells that, thanks to Application.Calculation = xlCalculationManual setting, will still have previous value

If your concern is about a limited number of "base" sheet cells, you can go on in a similar way but keeping "mirror" cells in the "base" sheet itself

In this latter case here's a code to handle it (NOTE: VBA code, but you can easily translate in C#")

Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim sensitiveRange As Range
Dim sensitiveRangeSelected As Range

Set sensitiveRange = Range("sensitiveRange")
Set sensitiveRangeSelected = Application.Intersect(sensitiveRange, Target)
If sensitiveRangeSelected Is Nothing Then
    ' no 'sensitive' cells  --> go ahead
Else
    ' 'sensitive' cells !! -> add code to handle thier value or store it in some array
End If

End Sub

where you have to set a named range (I called it "sensitiveRange") in your "base" sheet with all its cells that must be tracked

user3598756
  • 28,893
  • 4
  • 18
  • 28
  • Thank you for extensive response. I was hoping to avoid this approach, which will be used as last resort, mainly due to the fact that i will need to keep in memory quite huge amount of data, which at later point might have sync issues. – Jim Feb 29 '16 at 14:03
  • 1
    you could use an "array" approach: you store all sheet content in a 2D "mirror" variant array (instead of in a "mirror" sheet) and Worksheet_Change() event handler "target" parameter gives you coordinates of changed cells. The "mirror" array could be a dynamic one to be "redimmed" after every sheet's "UsedRange" address change – user3598756 Feb 29 '16 at 14:26
  • At later stage I would need to implement your solution, however for now @Tim solution works fine – Jim Feb 29 '16 at 19:50
  • Ok. I didn't elaborate on any "Undo" solution since at that stage you wouldn't use it. – user3598756 Feb 29 '16 at 20:28
1

You can grab the selection prior to running the Undo, and restore it at the end of the process.

Note: the Select at the end will fail if the sheet isn't active (in the case of a sheet being updated by code for example) so you might need to check for that.

Private Sub Worksheet_Change(ByVal Target As Range)

    Dim Where As String, OldValue As Variant, NewValue As Variant
    Dim r As Long, c As Long, tmp
    Dim sel As Object '<<< current selection: not always a Range!
    Dim rngTrack As Range

    On Error GoTo haveError
    Application.EnableEvents = False
    Set sel = Selection '<<< capture the selection
    Where = Target.Address
    NewValue = Target.Value
    Application.Undo
    OldValue = Target.Value 'get the previous values
    Target.Value = NewValue
    Application.EnableEvents = True

    Set rngTrack = Sheets("Tracking").Cells(Rows.Count, 1).End(xlUp).Offset(1, 0)

    'set some limit for the size of change you want to track
    If Target.Cells.CountLarge < 1000 Then

        'convert single-cell values to array...
        If Target.Cells.CountLarge = 1 Then
            OldValue = ToArray(OldValue)
            NewValue = ToArray(NewValue)
        End If

        'multi-cell: treat as arrays
        For r = 1 To UBound(OldValue, 1)
        For c = 1 To UBound(OldValue, 2)
            If OldValue(r, c) <> NewValue(r, c) Then
                rngTrack.Resize(1, 3).Value = _
                  Array(Target.Cells(r, c).Address, OldValue(r, c), NewValue(r, c))
                Set rngTrack = rngTrack.Offset(1, 0)
            End If
        Next c
        Next r
    End If

    sel.Select '<<< reset the selection
    Exit Sub

haveError:
    Application.EnableEvents = True

End Sub
'utility function
Private Function ToArray(v)
    Dim rv(1 To 1, 1 To 1)
    rv(1, 1) = v
    ToArray = rv
End Function
Tim Williams
  • 154,628
  • 8
  • 97
  • 125
  • While I am not fan of Undo process it seems to be the simplest solution to this problem. Thank you very much – Jim Feb 29 '16 at 19:49
  • I;ve noticed though as side effect of that method, the formatting is getting Undo as well, and its requires a lot of code to get it back. – Jim Mar 02 '16 at 20:17