Как получить старое значение измененной ячейки в Excel VBA? - PullRequest
42 голосов
/ 12 января 2011

Я обнаруживаю изменения в значениях определенных ячеек в электронной таблице Excel, как это ...

Private Sub Worksheet_Change(ByVal Target As Range)
Dim cell As Range
Dim old_value As String
Dim new_value As String

For Each cell In Target

    If Not (Intersect(cell, Range("cell_of_interest")) Is Nothing) Then
        new_value = cell.Value
        old_value = ' what here?
        Call DoFoo (old_value, new_value)
    End If

Next cell

End Sub

Если предположить, что это не слишком плохой способ кодирования, как мне получитьзначение ячейки до изменения?

Ответы [ 15 ]

1 голос
/ 26 января 2014

попробуйте, это не будет работать для первого выбора, тогда это будет хорошо работать:)

Private Sub Worksheet_SelectionChange(ByVal Target As Range)
    On Error GoTo 10
    If Target.Count > 1 Then GoTo 10
    Target.Value = lastcel(Target.Value)
    10
End Sub


Function lastcel(lC_vAl As String) As String
    Static vlu
    lastcel = vlu
    vlu = lC_vAl
End Function
0 голосов
/ 23 октября 2017

Использование Static решит вашу проблему (с другими вещами для правильной инициализации old_value:

Private Sub Worksheet_Change(ByVal Target As Range)
    Static old_value As String
    Dim inited as Boolean 'Used to detect first call and fill old_value
    Dim new_value As String
    If Not Intersect(cell, Range("cell_of_interest")) Is Nothing Then
         new_value = Range("cell_of_interest").Value
         If Not inited Then
             inited = True
         Else
            Call DoFoo (old_value, new_value)
        End If
        old_value = new_value
    Next cell
End Sub

В коде рабочей книги вызовите Worksheet_change, чтобы заполнить old_value:

Private Sub Private Sub Workbook_Open()
     SheetX.Worksheet_Change SheetX.Range("cell_of_interest")
End Sub

Обратите внимание, однако, что ЛЮБОЕ решение, основанное на переменных VBA (включая словарь и другие более сложные методы), потерпит неудачу, если вы остановите (Сброс) выполнение кода (например, при создании новых макросов, отладке некоторого кода,..). Чтобы избежать такого, рассмотрите возможность использования альтернативных методов хранения (например, скрытого листа).

0 голосов
/ 23 октября 2017
Private Sub Worksheet_Change(ByVal Target As Range)
vNEW = Target.Value
aNEW = Target.Address
Application.EnableEvents = False
Application.Undo
vOLD = Target.Value
Target.Value = vNEW
Application.EnableEvents = True
End Sub
0 голосов
/ 16 августа 2017

Мне нужна была эта функция, и мне не понравились все вышеперечисленные решения после большинства попыток, так как они либо

  1. Медленно
  2. Имеют сложные последствия, такие как использование application.undo.
  3. Не снимать, если они не были выбраны
  4. Не фиксирует значения, если они не были изменены ранее
  5. Слишком сложный

Ну, я очень много думал об этом, и я закончил решение для полной истории UNDO, REDO.

Чтобы уловить старое значение, это на самом деле очень просто и очень быстро.

Мое решение - захватить все значения, как только пользователь откроет лист, откроет переменную, и он обновляется после каждого изменения. эта переменная будет использоваться для проверки старого значения ячейки. В вышеупомянутых решениях все они используются для цикла. На самом деле есть способ проще.

Для захвата всех значений я использовал эту простую команду

SheetStore = sh.UsedRange.Formula

Да, просто, Excel на самом деле вернет массив, если диапазон состоит из нескольких ячеек, поэтому нам не нужно использовать команду FOR EACH, и она очень быстрая

Следующий подпункт является полным кодом, который должен вызываться в Workbook_SheetActivate. Еще одна подпрограмма должна быть создана, чтобы зафиксировать изменения. Например, у меня есть подпрограмма "catchChanges", которая работает на Workbook_SheetChange. Он запишет изменения и сохранит их на другом листе истории изменений. затем запускает UpdateCache для обновления кеша новыми значениями

' should be added at the top of the module
Private SheetStore() As Variant 
Private SheetStoreName As String  ' I use this variable to make sure that the changes I captures are in the same active sheet to prevent overwrite

Sub UpdateCache(sh As Object)
      If sh.Name = ActiveSheet.Name Then ' update values only if the changed values are in the activesheet
          SheetStoreName = sh.Name
          ReDim SheetStore(1 To sh.UsedRange.Rows.count, 1 To sh.UsedRange.Columns.count) ' update the dimension of the array to match used range
          SheetStore = sh.UsedRange.Formula
      End If
End Sub

теперь получить старое значение очень просто, так как массив имеет одинаковый адрес ячеек

примеров, если нам нужна ячейка D12, мы можем использовать следующее

SheetStore(row_number,column_number)
'example
return = SheetStore(12,4)
' or the following showing how I used it. 
set cell = activecell ' the cell that we want to find the old value for
newValue = cell.value ' you can ignore this line, it is just a demonstration
oldValue = SheetStore(cell.Row, cell.Column)

это фрагмент, объясняющий метод, надеюсь, всем понравится

0 голосов
/ 06 июля 2017

Просто мысль, но вы пытались использовать application.undo

Это вернет значения обратно.Затем вы можете просто прочитать исходное значение.Не должно быть слишком сложно сначала сохранить новые значения, поэтому, если хотите, измените их снова.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...