2

Is there something equivalent to Cell_Change Event in VBA or even VSTO that would fire BEFORE the user input is committed into the cell? I need to add some checks just before a new value is entered in a cell.

By the time the WorkSheet_Change event fires, Excel has already modified the user input and converted it into a different value. I need to get the original input that user typed.

EDIT

To be more specific, I want to prevent Excel from converting values like 3E9, which happens to be a valid (non-numeric/non-scientific) input in my case, to scientific notation.

These values can be input by the user in any cells in the entire worksheet, so I cannot pre-apply Text formatting because that will stop Excel from doing its normal functionality on valid numeric data too.

So I'm planning to:

  • somehow grab the input just before it goes into the cell and gets converted.
  • then check if it fits the exact pattern I'm watching for and apply Text format on that specific cell on-the-fly.
Community
  • 1
  • 1
dotNET
  • 33,414
  • 24
  • 162
  • 251
  • From VBA point of view: There is nothing like `before_change` event. However, depending on the way you enter values into cells you could use `Worksheet_SelectionChange` event. If user wants to change cell he usually enter it first. Than `SelectionChange` fires first and `Change` is later. This would be useless if you change cells with any code. – Kazimierz Jawor Apr 09 '13 at 09:55
  • Is there a select set of cells of interest to you? – glh Apr 09 '13 at 10:56
  • For a single cell, many cells, or all cells? – brettdj Apr 09 '13 at 11:05
  • I want to monitor all cells on the worksheet, for a particular input. – dotNET Apr 09 '13 at 11:07
  • @KazJaw: I'm not sure how can `SelectionChange` be of any use in this. Can you explain plz? – dotNET Apr 09 '13 at 11:07
  • @dotNET, which way will user input cell data- directly in Excel App? In single cell or few cells at once? I could extend suggestion only for VBA, not VSTO, is that ok? – Kazimierz Jawor Apr 09 '13 at 11:12
  • User will be typing values directly into the cells. See my edit above. – dotNET Apr 09 '13 at 11:16
  • Is this really all cells? Both my solution, and Dicks, are getting e little expensive scope wise to cater for everything – brettdj Apr 10 '13 at 04:53

3 Answers3

3

You could trap the scientific notation being applied instead and reverse it into a text string

Updated with a RegExp to handle the parsing

  • Reformat valid values back to the initial text (i.e. 120E3 or 99E13)
  • Replaces mangled values with a message to renter them (with the cell already formatted as text so next time 12E14 stays as text 12E14

sheet event code

Private Sub Worksheet_Change(ByVal Target As Range)
Dim rng1 As Range
Dim strTmp As String
Dim objRegex As Object
Set objRegex = CreateObject("vbscript.regexp")
With objRegex
    .Pattern = "([1-9]+)(0+)"
    Application.EnableEvents = False
    For Each rng1 In Target.Cells
        If rng1.NumberFormat = "0.00E+00" Then
        rng1.NumberFormat = "@"
        strTmp = Len(.Replace(rng1.Value, "$2"))
            If .Test(rng1.Value) Then
            rng1.Value = "'" & .Replace(rng1.Value, "$1E") & strTmp
                Else
            rng1.Value = "Pls re-enter as text"
            End If
        End If
    Next
End With
Application.EnableEvents = True
End Sub
brettdj
  • 54,857
  • 16
  • 114
  • 177
  • That would have worked, but Excel goes one step forward and truncates user input if it is longer than a few characters, before converting it to scientific notation, so I'll lose some of user's input that way. +1 for the idea though. – dotNET Apr 09 '13 at 13:17
  • How long are your strings? – brettdj Apr 09 '13 at 13:19
  • I'm allowing for up to 255 chars. – dotNET Apr 09 '13 at 13:20
  • .... yes once 16 characters are exceeeded there is truncation. so `12E13` is fine but `12E14` fails ..... but we can handle this to ran the user. Updating now – brettdj Apr 09 '13 at 13:22
  • You shouldn't need to format as @ if you're putting the apostrophe in front and it may be better to format as General in case a number gets entered later. Also, what about InputBox for re-entry with a nice message? – Dick Kusleika Apr 09 '13 at 15:42
  • @DickKusleika the reason for the text formatting is for the *invalid* re-engineered cells, so that the next time 12E14 is entered, it is fine first time. But given I have applied this to both the inavlid and valid cells (prior to the `IF`) you are correct that I am doubling up on formatting of the valid cells. I did consider a `InputBox` but if say 20 cells were changed at once needing re-entry, I thought 20 individual boxes might be irritating. – brettdj Apr 09 '13 at 21:33
  • + 1Nice one as usual. @dotNET: A quick word of caution since this code uses `Worksheet_Change`. See this http://stackoverflow.com/questions/13860894/ms-excel-crashes-when-vba-code-runs/13861640#13861640 – Siddharth Rout Apr 10 '13 at 06:05
  • 1
    Oh yeah, multi-cell changes. That one always gets me. – Dick Kusleika Apr 10 '13 at 13:07
1

What about the undo function?

Private Sub Worksheet_Change(ByVal Target As Range) 
    Dim OldValue As String, NewValue as string
    If Intersect(Target, Range("B:B")) Is Nothing Then Exit Sub 
    With Application 

        .EnableEvents = False 

        .Undo 
        'this is before
        OldValue = Target.Cells(1).Value 
        .Redo 
        'this is after 
        Newvalue = Target.Cells(1).value

        'do some check to see if you need to reverse the change 
        If oldvalue > newvalue then 
            .undo
            Msgbox "Not valid"
        end if

        .EnableEvents = True 

    End With

End Sub
glh
  • 4,900
  • 3
  • 23
  • 40
  • Haven't tested it, but I can see that doing an `Undo` will simply wash out the newly entered data in the cell, so .Value will be blank. – dotNET Apr 09 '13 at 11:22
  • Not if there was something there before and this is why the `oldvalue` variable is there followed by the `redo`. This will give you a before (`oldvalue`) and after (`target.cells(1).value`) if you check after the `end with`. – glh Apr 09 '13 at 11:34
  • 1
    You don't understand the problem correctly. I need the value that user just typed (immediately before it was committed into the cell), not the value that was there BEFORE user started typing. – dotNET Apr 09 '13 at 11:41
  • This will be the new value but the problem you have is its already committed. Undo will help you reverse this if you don't want to keep it. I've given you the ability to see what was there and what is currently there. Isn't that the same? There can only be one of 2 values your after. – glh Apr 09 '13 at 11:43
  • 1
    @dotNET this looks like it should be what you're after. Just modify the conditional `If oldvalue > newvalue...` block to check against your pattern. – David Zemens Apr 09 '13 at 11:59
  • 1
    The code failed for me on `Redo` which was remedied by making it a second `Undo`. But it still doesn't answer the question - `dotNet` wants to capture `1E3` before it gets converted to `1000` - not the value that existed in the cell before the user types `1E3` – brettdj Apr 09 '13 at 12:20
  • 1
    @brettdj typed all that I wanted to type. – dotNET Apr 09 '13 at 12:24
  • I know, it can't be captured, as I stated in my comment and thers have said. There's no need to repeat this. Also, what is the point of capturing 1e3 if **no one** not even excel sees this, 1000 is the end result no matter how it was achieved. – glh Apr 09 '13 at 12:39
  • @brettdj, what do you mean failed? Should i alter something? May be the new value could be saved and no redo but change the value back if its acceptable? – glh Apr 09 '13 at 12:48
  • 1
    @glh the code produced an error for me on `.Redo`. And while `1000` is the end result it can be reverse engineered back to a text string of `1E3` - see my post. – brettdj Apr 09 '13 at 13:01
  • @brettdj: nice! But it then seems the issue is not the number typed but the format the cell inherits as a result? – glh Apr 09 '13 at 13:11
  • The cell automatically formats to scientic-notation when it converts 1E9 to 1000, so detecting this format can be used as the trigger for the reverse engineering to text. – brettdj Apr 09 '13 at 13:18
  • how many of these unique entries can you reverse engineer? I doubt all of them? ;) – glh Apr 09 '13 at 13:20
1

I wonder if you can use Data Validation to help with this. Let's say you have valid inputs like 3E9 and 4E7, but you don't have valid inputs like 5E2 or 7E1. Then let's further assume you only rarely have valid numeric inputs above 10m. If this very specific situation is true, you could warn the user when they enter a number > 10m. If it's legitimate, they dismiss the warning and move on. If they intended to enter a literal string, they can cancel the input and follow your instructions to precede with an apostrophe. You could create DV like this:

Allow: Custom
Formula: =OR(ISTEXT(A1),A1<10^7)
Style: Warning
Title: Scientific Notation
Msg: If you enter a string like '9E99' Excel will convert to a number in scientific notation format. If you want the literal string, precede it with an apostrophe.

It may be asking a lot that your situation is that specific. Maybe there are some other characteristics that you could use DV to avoid. Like if it's extremely unlikely that someone will enter a whole number, you could DV to check for that.

Dick Kusleika
  • 32,673
  • 4
  • 52
  • 73
  • glh - your vote didn't appear? Dick, from the OP question, this would need to be applied to every cell in the worksheet. – brettdj Apr 10 '13 at 04:46
  • Thanks @brettdj, neither did your @. ;) – glh Apr 10 '13 at 12:09
  • Well, every cell in a reasonable range. If you have no other DV, it's not a problem. If some other cells have DV, then it's too hard to manage IMO. – Dick Kusleika Apr 10 '13 at 13:10