0

In my Excel sheet, I have a range "plot" that triggers a sub-routine upon change. I used the following code for that:

Private Sub Worksheet_Change(ByVal Target As Range)
    If Target.Address = Range("plot").Address Then
        auto_save_data (current_plot) ' <- doesn't work
        restore_data
    End If
End Sub

This code has to first save the data from the current worksheet to a specific range, that defined by current_plot in another worksheet (let's call it "DATA"), by calling auto_save_data (current_plot).
Then it restores the data from a specific range in "DATA" that is defined by Range("plot"), by calling restore_data.

The restore_data sub-routine above work as expected, but auto_save_data doesn't. The problem is that when the user change the value of "plot" I need to somehow know what was the value before the change, so I can save the data to the correct place before restoring the data from "DATA" for the value after update, and by that deleting the data in the current sheet.

I tried to use the Worksheet_SelectionChange event, as described here:

Private Sub Worksheet_SelectionChange(ByVal Target As Range)
    Dim current_plot As Variant
    If Target.Address = Range("plot").Address Then
        current_plot = Target.Value
    End If
End Sub

But it has 2 problems:

  1. It didn't work. The sub-routine Worksheet_Change didn't seem to recognize the value of the variable current_plot, albeit, it didn't throw an error.
  2. I tried another method from the question above, that save the old value to a hidden sheet. That worked, except when the user changes the value in "plot" without selecting another range first (then the value in the hidden sheet does not update).

So my question is: What is the simplest method (I'm very new to VBA) to use the value that was in Target before the routine Worksheet_Change was triggered?

EDIT: I changed "plot" to be a single cell range ($P$2), to focus the question on the real problem.

Community
  • 1
  • 1
EBH
  • 10,350
  • 3
  • 34
  • 59
  • I think you simply failed to give `current_plot` module-level scope, because you placed `Dim current_plot As Variant` inside the `SelectionChange` subroutine instead of at the beginning of the module. But try using [the `Undo` method from that page](https://stackoverflow.com/a/7035579/6535336) as well - it should be robust enough to do what you want. – YowE3K May 28 '17 at 19:48
  • Is `Range("plot")` a single cell? You are comparing it to Target in a way that infers it is a single cell. –  May 28 '17 at 20:02
  • 1
    `' "$P$2:$R$2" is the range of "plot"` – Siddharth Rout May 28 '17 at 20:03
  • Without an `application.enableevents = false` it seems like you would be in an infinite loop. –  May 28 '17 at 20:05
  • 1
    `If Target.Address = Range("plot").Address Then` This should be `If not intersect(target,Range("plot")) then`. You may also want to see [This](https://stackoverflow.com/questions/13860894/ms-excel-crashes-when-vba-code-runs/13861640#13861640) – Siddharth Rout May 28 '17 at 20:07
  • @YowE3K Both `SelectionChange` and `Cange` are in the same worksheet. – EBH May 28 '17 at 20:54
  • 1
    @EBH With `Dim current_plot As Variant` inside one of the subroutines, the variable is local to that subroutine. Moving the declaration to the beginning of the module (prior to the first `Sub`) makes it module-level in scope and therefore accessible to both subroutines. – YowE3K May 28 '17 at 20:57
  • @YowE3K Thanks for that! Now problem 1 is solved. – EBH May 28 '17 at 21:03
  • @Jeeped I have edited the question to resolve the range issue. As for the infinite loop - well, that didn't happen. – EBH May 28 '17 at 21:06
  • So what is problem two? (And why haven't we closed the question as "too broad" if there is more than one problem per question?!? ;) ) – YowE3K May 28 '17 at 21:06
  • Problem 2 says: _"That worked, except when the user changes the value in "plot" without selecting another range first."_ It's all part of the same problem... – EBH May 28 '17 at 21:08

1 Answers1

2

Assuming "Plot" is a single-cell range in the worksheet which has Name of "DATA", place the following code in the code module of Worksheets("DATA"):

'Declare a variable with global scope, i.e. accessible in this worksheet
'and in other code modules.
Public current_plot As Variant

Private Sub Worksheet_Change(ByVal Target As Range)
    If Target.Address = Range("plot").Address Then
        'Use "current_plot" whenever the cell is changed
        MsgBox "Old value: " & current_plot & vbCrLf & _
               "New value: " & Target.Value
    End If
End Sub

Private Sub Worksheet_SelectionChange(ByVal Target As Range)
    If Target.Address = Range("plot").Address Then
        'Update "current_plot" whenever the cell is selected
        current_plot = Target.Value
    End If
End Sub

and place this code in the ThisWorkbook code module:

Private Sub Workbook_Open()
    'Initialise "current_plot" when the workbook is opened
    Worksheets("DATA").current_plot = Worksheets("DATA").Range("Plot").Value
End Sub

Assuming you don't want to know what used to be in the cell, but instead you actually want to know what was in the cell the last time you made use of the cell's value, you can simplify this a bit by using the following:

'Declare a variable with global scope, i.e. accessible in this worksheet
'and in other code modules.
Public current_plot As Variant

Private Sub Worksheet_Change(ByVal Target As Range)
    If Target.Address = Range("plot").Address Then
        'Use "current_plot" whenever the cell is changed
        MsgBox "Old value: " & current_plot & vbCrLf & _
               "New value: " & Target.Value

        '...
        '... process whatever needs to be processed
        '...

        'Save the value we used this time
        current_plot = Target.Value
    End If
End Sub

and place this code in the ThisWorkbook code module:

Private Sub Workbook_Open()
    'Initialise "current_plot" when the workbook is opened
    Worksheets("DATA").current_plot = Worksheets("DATA").Range("Plot").Value
End Sub
YowE3K
  • 23,852
  • 7
  • 26
  • 40
  • Thanks for your answer! I'm not sure what the MsgBox is for, but this works good as long as you leave the rage "plot" between changes, otherwise `current_plot` does not update. – EBH May 28 '17 at 21:54
  • (a) The message box was only to show that you could access the old and new values. (b) If you don't leave the cell between "changes" what do you want the "old" value to be - the value before they started making changes, or the value after they made a change to an incorrect value but before correcting it to a valid value? E.g. If the value was `5`, then they changed it to `"xyz"`, then changed it to `6`, what value do you want as the value stored in `current_plot` - the `5` or the `"xyz"`? (If you want `"xyz"` then just set `current_plot` as part of the `Change` event.) – YowE3K May 28 '17 at 21:58
  • It's not about incorrect values. "plot" is a cell that limited to a list of values, that the user choose from. Let's say he is now in plot `100`, and want to change to plot `200`, and then go back to `100`. While doing that all the data that he entered in other places in the sheet ("Input") has to be saved (in some other sheet - "DATA") and replaced by the data that is already in "DATA" for plot `200`, and then again to be reversed to the data saved for `100`. ... – EBH May 28 '17 at 22:13
  • ... If he selects some other cell then "plot" in between, (i.e. after the last change to `200` he leaves the cell, then choose it again and change it to `100`) that's work fine. But, if **immediately after changing to `200` he changes it back to `100`, then the value of `current_plot` stays `100`, and all the data for plot `200` is saved in `100` area. I hope this is clearer now. – EBH May 28 '17 at 22:18
  • @EBH - if the user is changing the cell from `100` to `200` and then perhaps to `300` (or `100` again) and you want to save the `200` once they have set it to that, then just set `current_plot` as part of the `Change` event - i.e. `current_plot = Target.Value`. I.e. every time you believe that the cell contains something you want to save for future use, save it for future use. – YowE3K May 28 '17 at 22:19
  • WOW! that was what I'm looking for, so simple and clear. I am so confused by the VBA syntax that I couldn't see this simple solution before. Thanks a million for the patience and all the help! – EBH May 28 '17 at 22:36