Event Handler -- Cell not Updating as Expected - excel

The code:
Private Sub Worksheet_change(ByVal target As Range)
Application.ScreenUpdating = False
Application.EnableEvents = False
Dim cell
For Each cell In Me.UsedRange.Columns("E").Cells
If cell.Text = "Cu" And cell.offset(0, -1) = "WR229" Then
MsgBox "Cu not permitted for WR229 or larger waveguide", vbOKOnly, "Cu Alert"
cell = "Al"
End If
Next cell
Application.ScreenUpdating = True
Application.EnableEvents = True
End Sub
The problem is that when the condition is met the cell does not get reset to the value "Al". Why not?

One possible reason the OP's code can fail is if the UsedRange does not start in column A. This will occur if there is no data and no formatting at all in column A.
Why? Because .Columns (and .Rows and .Cells for that matter) are relative to the specified range. For example if the UsedRange is B2:Z10 then Me.UsedRange.Columns("E") will refer to range F2:F10.
Another issue in the OP's code is that it will run for any cells change, including those in Column A. This will throw an error because Offset -1 from Column A is invalid.
So, how to fix it? As answered by jbarker2160, you should take advantage of the Target parameter, which tells you which cells have changed. However that answer leaves several problems.
We want to check for Column E = "Cu" and Column D for "WR229", but we don't know which one will be entered first
We should account for the possibility that several cells are changed at once, eg due to a copy/paste
we should handle possible errors and not leave Events disabled
The OP's code is case sensitive. Ie "CU" will be accepted when "Cu" is not. Is this the desired behaviour? (If it is, remove the UCase$()'s from the code below)
Implicit in the OP's code is that there is a range of larger waveguides that are disallowed for Cu. This remains un-addressed in the code below.
This code addresses the above issues
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rw As Range
On Error GoTo EH
Application.ScreenUpdating = False
Application.EnableEvents = False
For Each rw In Application.Intersect( _
Target.EntireRow, Me.UsedRange.EntireRow.Columns("D:E")).Rows
If UCase$(rw.Cells(1, 2)) = "CU" And UCase$(rw.Cells(1, 1)) = "WR229" Then
MsgBox "Cu not permitted for WR229 or larger waveguide", _
vbOKOnly, "Cu Alert"
rw.Cells(1, 2) = "Al"
End If
Next
EH:
Application.ScreenUpdating = True
Application.EnableEvents = True
End Sub

Code
Private Sub Worksheet_change(ByVal target As Range)
Application.ScreenUpdating = False
Application.EnableEvents = False
If target = "Cu" And target.offset(0, -1) = "WR229" Then
MsgBox "Cu not permitted for WR229 or larger waveguide", vbOKOnly, "Cu Alert"
target = "Al"
End If
Application.ScreenUpdating = True
Application.EnableEvents = True
End Sub
Explanation
Since you are doing this loop for each change, you don't need to loop through the entire column and using target will get around having to use the Value property.

Change this line:
cell = "Al"
To this:
cell.Value = "Al"

Related

Excel VBA-code to alter a cell if a line has been edited

I'm doing some data validation work and basically, whenever I alter a line in the data I also need to write "Yes" in J-column on the same line. I figured this should be automatizable, but I had some trouble with the code.
What I want it to do is to check for changes in C-H rows, and if there's a numeral (0-99) on the B-column, then replace the text on the J-column of the same line with "Yes" (without the quotation marks). The "numeral"-part can be abbreviated to having length of 1-2 (not 0 and not more) in this case.
Here's what I have thus far, but I can't seem to figure out how to do the combination of absolute and relative reference, i.e. "check B of the active row" or "alter J of the active row" (that is to say, none of the codes I've tried thus far have been valid according to VBA; I have very little VBA experience so this code feels alien to me):
Private Sub Worksheet_Change(ByVal Target As Range)
If Intersect(Target, Range("C:H")) Is Nothing Then Exit Sub
If Not Len("B" & ActiveCellRow) = "1" Then
If Not Len("B" & ActiveCellRow) = "2" Then
Exit Sub
End If
With Application
.EnableEvents = False
.ScreenUpdating = False
"J" & ActiveCellRow = "Yes"
.EnableEvents = True
.ScreenUpdating = True
End With
You can do something like this:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rng As Range, c As Range, v
'in our range of interest?
Set rng = Application.Intersect(Target, Me.Range("C:H"))
If Not rng Is Nothing Then
'get the corresponding cells in ColB
For Each c In Application.Intersect(rng.EntireRow, Me.Range("B:B")).Cells
v = c.Value
'flag required?
If Len(v) > 0 And Len(v) < 3 And IsNumeric(v) Then
c.EntireRow.Cells(1, "J").Value = "Yes"
End If
Next c
End If
End Sub

How to ensure correct character length is being input

I need to ensure that the user types in an integer with length (blank) in any cell of a certain column. If the user inputs a number that is not length (blank), the Excel freezes the user at that cell and prompts to re-enter until integer length (blank) has been inputted or cancel is hit.
I currently have most of the things I request working. However, my issue is that Excel doesn't recognize length errors until I move away from the cell and come back to it.
For example (using 3 as desired length):
If i am currently on Cell B12 and type in 15646, which is not length 3, I can still click enter and it will move to Cell B13, which I want to prevent. But if I move up to B12 again from B13, the length error is seen and Excel prompts me to input integer with correct length until its fixed.
For now, the length error is only being recognized when I come back to cell. I need it to recognize as soon as I hit enter and prevent from moving on to next cell.
Sub InputNum()
row = ActiveCell.row
col = ActiveCell.Column
If col = 2 And ActiveCell.Value <> "" Then
Dim lotTextLen As Integer
lotTextLen = Len(ActiveCell.Value)
'checks to ensure the number put in is 3 characters long
'requests an input number to be put in
If lotTextLen <> 3 Then
lotData = InputBox("Invalid Entry Length. Scan in Lot #")
If Len(lotData) <> 3 Then
'error message
Result = MsgBox("Invalid Lot # Inputed. Must be 3 Characters. Try Again?", vbOKCancel)
'if cancel is clicked, input number is made blank and sub is exited
If Result <> vbOK Then
ActiveCell.Value = ""
'if ok is clicked to try again, recurses to beginning of code again
Else
InputNum
End If
Else
ActiveCell.Value = lotData
End If
End If
End If
End Sub
InputNum is being called in the Sheet1
Public Sub Worksheet_SelectionChange(ByVal Target As Range)
If Not Application.Intersect(Target, Range("B:C")) Is Nothing Then
InputNum
End If
End Sub
In the sheet object place the following
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Me.Columns(2)) Is Nothing Then
Application.EnableEvents = False
InputNum Target
Application.EnableEvents = True
End If
End Sub
Then use this in a standard module
Public Sub InputNum(Target As Range)
Dim IoTData As String
Dim Result As String
Dim isCancel As Boolean
Do While Len(Target.Value2) <> 3
IoTData = InputBox("Invalid Entry Length. Scan in Lot #")
If Len(IoTData) = 3 Then
Target.Value2 = IoTData
Else
If IoTData <> vbNullString Then
' error message
Result = MsgBox("Invalid Lot # Inputed. Must be 3 Characters. Try Again?", vbOKCancel)
If Result <> vbOK Then isCancel = True
Else
isCancel = True
End If
End If
If isCancel Then
Target.Value2 = vbNullString
Exit Do
End If
Loop
End Sub
By placing your code in a loop it will keep pestering the user for the right length until either they enter the right format or they press cancel in which instance the cell will be cleared of it's input.
You can also add And IsNumeric(IoTData) to your If statement to test that a number has been entered.
Replace
If Len(IoTData) = 3 Then
With
If Len(IoTData) = 3 And IsNumeric(IoTData) Then
Option Explicit
Dim add As String
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("B:C")) Is Nothing And Target.Count = 1 Then
If Len(Target.Value) <> 3 Then
MsgBox "Invalid entry in cell with address " & add
Application.EnableEvents = False
Target.Activate
'Enter more code
Application.EnableEvents = True
End If
End If
End Sub
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
If Not Intersect(Target, Range("B:C")) Is Nothing And Target.Count = 1 Then
add = Target.Address
End If
End Sub

Updating value of a non-Target cell in Excel VBA

I found the attached when looking for how to due an event change to correct user data based on the values in two columns. I'm not a programmer, so I may have butchered the code as I combined two different solutions together.
Right now, it's working exactly as I want it to. Changing the offset cell value forces Excel to replace the target value with what I've specified. What I'm looking to achieve (and am not sure is possible), is to reverse the code. Basically, I want to change the offset cell, if the values are entered in the opposite order. The code will change the cell value to "Beta" if a user enters "Bravo" in column A, and then "Gamma" in column C.
What I'm trying to achieve is that if the user enters "Bravo" in column A second, that Excel still sees the combination of these cells and still replaces the value with "Beta". I know this is additional code, but I couldn't find anything to support replacing cell when the target cell isn't the value being updated.
Thanks in advance!
Dim oldCellAddress As String
Dim oldCellValue As String
Private Sub Worksheet_Change(ByVal Target As Range)
oldCellValue = "Bravo"
If Target = "Bravo" And Target.Offset(0, -2) = "Gamma" Then
Target.Value = "Beta"
Application.EnableEvents = True
End If
End Sub
This may meet your needs:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim colnum As Long, v As Variant
colnum = Target.Column
v = Target.Value
If colnum = 1 Then
If v = "Bravo" And Target.Offset(0, 2) = "Gamma" Then
Application.EnableEvents = False
Target.Value = "Beta"
Application.EnableEvents = True
End If
Exit Sub
End If
If colnum = 3 And v = "Gamma" And Target.Offset(0, -2) = "Bravo" Then
Application.EnableEvents = False
Target.Offset(0, -2).Value = "Beta"
Application.EnableEvents = True
End If
End Sub
For example if the user puts Bravo in cell A1 and C1 already contained Gamma, the code puts Beta in A1 (the code corrects the A1 entry).If the user puts Gamma in cell C1 and cell A1 already contained Bravo, the code corrects A1.
There are two possible scenarios like below...
Scenario 1:
If ANY CELL on the sheet is changed, the following code will check the content of column A and C in the corresponding row and change the content of the Target Cell.
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.CountLarge > 1 Then Exit Sub
Dim r As Long
r = Target.Row
On Error GoTo Skip:
Application.EnableEvents = False
If Cells(r, "A") = "Bravo" And Cells(r, "C") = "Gamma" Then
Target.Value = "Beta"
End If
Skip:
Application.EnableEvents = True
End Sub
Scenario 1:
If a cell in column D is changed, the change event will be triggered and check the content in column A and C in the corresponding row and change the Target Cell in Column D.
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.CountLarge > 1 Then Exit Sub
Dim r As Long
On Error GoTo Skip:
'The below line ensures that the sheet change event will be triggered when a cell in colunm D is changed
'Change it as per your requirement.
If Not Intersect(Target, Range("D:D")) Is Nothing Then
Application.EnableEvents = False
r = Target.Row
If Cells(r, "A") = "Bravo" And Cells(r, "C") = "Gamma" Then
Target.Value = "Beta"
End If
End If
Skip:
Application.EnableEvents = True
End Sub

Adding "A1,A2,A3.." to "B1,B2,B3.." Then Row "A" resets value to Zero

I am currently trying to add a script into excel. excuse my terminology, I am not that hot with programming!
I do all of my accounting on excel 2003, and I would like to be able to add the value of say cells f6 to f27 to the cells e6 to e27, respectively. The thing is, I want the value of the "f" column to reset every time.
So far I have found this code, which works if I copy and paste it into VBA. but it only allows me to use it on one row:
Private Sub Worksheet_Change(ByVal Target As Range)
Application.EnableEvents = False
If Target.Address = Range("f7").Address Then
Range("e7") = Range("e7") + Range("f7")
Range("f7").ClearContents
End If
Application.EnableEvents = True
End Sub
would somebody be kind enough to explain how I can edit this to do the same through all of my desired cells? I have tried adding Range("f7",[f8],[f9] etc.. but i am really beyond my knowledge.
First, you need to define the range which is supposed to be "caught"; that is, define the range you want to track for changes. I found an example here. Then, simply add the values to the other cell:
Private Sub Worksheet_Change(ByVal Target as Range)
Dim r as Range ' The range you'll track for changes
Set r = Range("F2:F27")
' If the changed cell is not in the tracked range, then exit the procedure
' (in other words, if the intersection between target and r is empty)
If Intersect(Target, r) Is Nothing Then
Exit Sub
Else
' Now, if the changed cell is in the range, then update the required value:
Cells(Target.Row, 5).Value = Cells(Target.Row, 5).Value + Target.Value
' ----------------^
' Column 5 =
' column "E"
' Clear the changed cell
Target.ClearContents
End if
End Sub
Hope this helps
Try this
Private Sub Worksheet_Change(ByVal Target As Range)
On Error GoTo ErrHandler
Application.ScreenUpdating = False
Application.EnableEvents = False
If Intersect(Target, Range("B1:B5,F6:F27")) Then 'U can define any other range
Target.Offset(0, -1) = Target.Offset(0, -1).Value + Target.Value ' Target.Offset(0,-1) refer to cell one column before the changed cell column.
'OR: Cells(Target.row, 5) = Cells(Target.row, 5).Value + Target.Value ' Where the 5 refer to column E
Target.ClearContents
End If
ErrHandler:
Application.EnableEvents = True
Application.ScreenUpdating = True
End Sub

Add timestamp in excel worksheet if row has changed

I need to add or update timestamp, to excel workbook, if row has been changed. I am doing data import, but I need to see which row was updated/added and on which date.
So far I have found and adjusted the following code:
Private Sub Worksheet_Change(ByVal Target As Excel.Range)
With Target
If .Count > 1 Then Exit Sub
If Not Intersect(Range("A2:BL9999"), .Cells) Is Nothing Then
Application.EnableEvents = False
With .Cells(1, 65)
.NumberFormat = "yyyy.mm.dd"
.Value = Now
End With
Application.EnableEvents = True
End If
End With
End Sub
The problem is, timestamp is always added relative to the row where the changes have been made + 65 rows, not in column BM (index 65).
Can you advise me, which function should I use or change?
As well as the fix for column BM better to
process all the rows that may have changed rather than exit withput any record
turn-off ScreenUpdating for speed
code
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rng1 As Range
Dim rng2 As Range
Set rng1 = Intersect(Target, Range("A2:BL9999"))
If rng1 Is Nothing Then Exit Sub
With Application
.ScreenUpdating = False
.EnableEvents = False
End With
For Each rng2 In rng1.Cells
With Cells(rng2.Row, 65)
.NumberFormat = "yyyy.mm.dd"
.Value = Now
End With
Next
With Application
.ScreenUpdating = True
.EnableEvents = True
End With
End Sub
To change your reference relative to the entire row, use .EntireRow.
So that line should read: With .EntireRow.Cells(1, 65)
Note that you can still use A1 style references even when working with a single row. This can keep you from having to count columns. For instance, and in this case, .EntireRow.Range("BM1") means the exact same thing as .EntireRow.Cells(1, 65).

Resources