Combining Worksheet_Change events in vba code - excel

I require some help in combining two Worksheet_Change events. Event 1 will reformat the cell to the correct postcode format & event 2 will apply the proper function. How can i combine event 1 & 2 in order for both to work at the same time?
Any help would be greatly appreciated :)
Event 1
Private Sub Worksheet_Change(ByVal Target As Range)
On Error Resume Next
If Intersect(Target, Range("K17")) Is Nothing Then Exit Sub
With CreateObject("vbscript.regexp")
.Global = True
.Pattern = "[A-Z]{1,2}[0-9]{1,2}\s[0-9][A-Z]{2}"
If Not .test(Cells(17, 11)) Then
Cells(17, 11) = UCase(Left(Cells(17, 11), Len(Cells(17, 11)) - 3) & " " & Right(Cells(17, 11), 3))
End If
End With
End Sub
Event 2
Private Sub Worksheet_Change2(ByVal Target As Range)
If Target.Cells.Count > 1 Or Target.HasFormula Then Exit Sub
On Error Resume Next
If Not Intersect(Target, Range("F7;K7")) Is Nothing Then
Application.EnableEvents = False
Target = StrConv(Target, vbProperCase)
Application.EnableEvents = True
End If
End Sub

Why not just split into two independent macros that each run during the Change event? The below might work. I'd be careful about doing .cells.count as that can be a lot if you delete an entire column or a wide range of data.
Private Sub Worksheet_Change(ByVal Target As Range)
Call macroFirst(Target)
Call macroSecond(Target)
End Sub
Private Sub macroFirst(ByVal Target As Range)
On Error Resume Next
If Intersect(Target, Range("K17")) Is Nothing Then Exit Sub
With CreateObject("vbscript.regexp")
.Global = True
.Pattern = "[A-Z]{1,2}[0-9]{1,2}\s[0-9][A-Z]{2}"
If Not .test(Cells(17, 11)) Then
Cells(17, 11) = UCase(Left(Cells(17, 11), Len(Cells(17, 11)) - 3) & " " & Right(Cells(17, 11), 3))
End If
End With
End Sub
Private Sub macroSecond(ByVal Target As Range)
If Target.Cells.Count > 1 Or Target.HasFormula Then Exit Sub
On Error Resume Next
If Not Intersect(Target, Range("F7;K7")) Is Nothing Then
Application.EnableEvents = False
Target = StrConv(Target, vbProperCase)
Application.EnableEvents = True
End If
End Sub

Related

Worksheet_Change executed multiple times

I am using a MsgBox in an if else condition. When I use other conditions along with MsgBox, the MsgBox pops up multiple times and I have to end the program.
Code in a module:
Sub CheckValue(Target)
If Target.Offset(0, 12) < 1 Then
MsgBox "This is a sample box"
Range(Target.Offset(0, -12), Cells(Target.MergeArea(1, 1).Row, Target.MergeArea(1, 1).Offset(1, -2).Column)).ClearContents
Target.Offset(0, 0).ClearContents
Target.Offset(-4, 0).Select
End If
I activate this sub through worksheet change:
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Address = "$N$16" Then
Call CheckValue(Target)
End If
End Sub
As you clear contents in your CheckValue sub, you are triggering the change-event.
You have to add Application.EnableEvents
Sub CheckValue(Target)
If Target.Offset(0, 12) < 1 Then
MsgBox "This is a sample box"
Application.EnableEvents = false '--> disable event
Range(Target.Offset(0, -12), Cells(Target.MergeArea(1, 1).Row, Target.MergeArea(1, 1).Offset(1, -2).Column)).ClearContents
Target.Offset(0, 0).ClearContents
Application.EnableEvents = true '--> enable events
Target.Offset(-4, 0).Select
End If
end sub

VBA Worksheet_Change Only Working For One Cell

I'd like to preface by saying I am a novice to VBA, so hopefully this is an easy fix.
I am trying to get the following VBA code to work for multiple cells with formulas. The effect is that there is a ghost value in the cell a user can overwrite then see again if they delete their value. I can get one cell to work how I want it to, but the second (and third and fourth etc.) do not work. How can I repeat this same line of code so that the effect repeats itself in multiple cells with different formulas?
Working:
Private Sub Worksheet_Change(ByVal Target As Excel.Range)
With Target
If .Address(False, False) = "F7" Then
If IsEmpty(.Value) Then
Application.EnableEvents = False
.Formula = "=IFERROR(INDEX(DATABASE!$D$2:$AG$3222,MATCH('Pricing Grid'!$B$11,DATABASE!$E$2:$E$3222,0),10),0)"
Application.EnableEvents = True
End If
End If
End With
End Sub
My attempt (Top working, bottom not):
Private Sub Worksheet_Change(ByVal Target As Excel.Range)
With Target
If .Address(False, False) = "F7" Then
If IsEmpty(.Value) Then
Application.EnableEvents = False
.Formula = "=IFERROR(INDEX(DATABASE!$D$2:$AG$3222,MATCH('Pricing Grid'!$B$11,DATABASE!$E$2:$E$3222,0),10),0)"
Application.EnableEvents = True
End If
End If
End With
End Sub
Private Sub Worksheet_Change1(ByVal Target As Excel.Range)
With Target
If .Address(False, False) = "F8" Then
If IsEmpty(.Value) Then
Application.EnableEvents = False
.Formula = "=IFERROR(INDEX(DATABASE!$D$2:$AG$3222,MATCH('Pricing Grid'!$B$11,DATABASE!$E$2:$E$3222,0),9),0)"
Application.EnableEvents = True
End If
End If
End With
End Sub
Try this...
Private Sub Worksheet_Change(ByVal Target As Range)
Dim i&, j&, v, t
v = Target.Value2
If Not IsArray(v) Then t = v: ReDim v(1 To 1, 1 To 1): v(1, 1) = t
Application.EnableEvents = False
For i = 1 To UBound(v)
For j = 1 To UBound(v, 2)
If Len(v(i, j)) = 0 Then
With Target(i, j)
Select Case .Address(0, 0)
Case "A1": .Formula = "=""Excel"""
Case "A2": .Formula = "=""Hero"""
End Select
End With
End If
Next
Next
Application.EnableEvents = True
End Sub
Use your formulas and ranges instead of mine, of course.
Update
The above works well, but this is faster/better...
Private Sub Worksheet_Change(ByVal Target As Range)
Dim i&, v
DoEvents
ReDim v(1 To 3, 1 To 2)
v(1, 1) = "A1": v(1, 2) = "=""This"""
v(2, 1) = "A2": v(2, 2) = "=""Works"""
v(3, 1) = "A2": v(3, 2) = "=""Great!"""
Application.EnableEvents = False
For i = 1 To UBound(v)
With Range(v(i, 1))
If Not Intersect(Target, .Cells) Is Nothing Then
If Len(.Value2) = 0 Then
.Formula = v(i, 2)
End If
End If
End With
Next
Application.EnableEvents = True
End Sub
Both of the above methods work for single-cell deletes AND also for clearing and deleting large ranges, including whole columns and whole rows and the second method is particularly quick in all these scenarios.
You can do something like this:
Private Sub Worksheet_Change(ByVal Target As Excel.Range)
'only handle single cells
If Target.Cells.CountLarge > 1 Then Exit Sub
If IsError(Target.Value) Then Exit Sub '<< edit: added
'only handle empty cells
If Len(Target.Value) > 0 Or Len(Target.Formula) > 0 Then Exit Sub
On Error Goto haveError
Application.EnableEvents = False
Select Case Target.Address(False, False)
Case "F7": Target.Formula = "=IFERROR(INDEX(DATABASE!$D$2:$AG$3222,MATCH('Pricing Grid'!$B$11,DATABASE!$E$2:$E$3222,0),10),0)"
Case "F8": Target.Formula = "=IFERROR(INDEX(DATABASE!$D$2:$AG$3222,MATCH('Pricing Grid'!$B$11,DATABASE!$E$2:$E$3222,0),9),0)"
End Select
haveError:
'ensure events are re-enabled
Application.EnableEvents = True
End Sub

Clear the cell of column B if cell A is empty - RANGE

Got a problem and looking for some advice. I've been using the below code for a while now in Excel, it clears the contents of column B if cell A is empty. It works great, but I now need it to work for a specific range (A6:B35). Any ideas?
Private Sub Worksheet_Change(ByVal Target As Range)
On Error Resume Next
If Target.Column = 1 Then
If Target.Validation.Type = 3 Then
Application.EnableEvents = False
Target.Offset(0, 1).ClearContents
End If
End If
exitHandler:
Application.EnableEvents = True
Exit Sub
End Sub
You need to test if the active cell (target) falls in the range A6:A35. Like this:
Private Sub Worksheet_Change(ByVal Target As Range)
On Error Resume Next
If not intersect(target, range("A6:A35")) is nothing then
If Target.Validation.Type = 3 Then
Application.EnableEvents = False
Target.Offset(0, 1).ClearContents
Application.EnableEvents = True
End If
End if
exitHandler:
End Sub
You should also indent your code so it is more readable. It will help with loops and IF statements.
something like
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rng1 As Range
Dim rng2 As Range
Set rng1 = Intersect(Target, Range("A6:B35"))
If rng1 Is Nothing Then Exit Sub
Application.EnableEvents = False
For Each rng2 In rng1
If rng2.Validation.Type = 3 Then rng2.Offset(0, 1).ClearContents
Next
Application.EnableEvents = True
End Sub

Calling one function from the other function in VBA (Excel)

I have two VBA functions, but i am unable to call the other from the first function.
Function 1:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim lastRow As Long
With ActiveSheet
lastRow = .Cells(.Rows.count, "A").End(xlUp).Row
Dim I, J As Integer
For I = 1 To lastRow
If Cells(I, "C").Value = "" Then
MsgBox "Please Enter Business Type Value", vbOKOnly
Exit Sub
End If
Next I
End With
End Sub
And the 2nd function:
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Cells.Count > 1 Then Exit Sub
Application.EnableEvents = False
If Not Application.Intersect(Target, Me.Columns(3)) Is Nothing Then
ActiveSheet.Unprotect
Select Case Target.Value
Case Is = "CNS"
Target.Offset(0, 4).Locked = True
Case Is = "cns"
Target.Offset(0, 4).Locked = True
Case Is = "APL"
Target.Offset(0, 4).Locked = False
Case Is = "apl"
Target.Offset(0, 4).Locked = False
Case Else
MsgBox "Value not covered by the program", vbInformation + vbOKOnly
End Select
ActiveSheet.Protect
Else
End If
Application.EnableEvents = True
End Sub
please help somebody..
thanks in advance..
In the same module you just call nameoffunction
You can make function public
public sub function
But it's a poor (sometime good) solution. You should structure your code
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
call modul1.function1 ( Target ) ' As Range)
End sub
Private Sub Worksheet_Change(ByVal Target As Range)
call modul1.function1 ( Target ) ' as range
call modul1.function2 ( Target )
end sub
edit ok ugly way
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
....
call Worksheet_change ( Target)
End sub

Clear the contents of columns B to F if cell A is empty

I have a worksheet with values depending on Cell A. If a row in column A contains a value then cells from Columns B through H will be changed accordingly.
If Cell of Column A is empty I want to reset the cells from columns D through F.
I wrote down the following VBA Code
Private Sub Worksheet_Change(ByVal Target As Range)
Dim n As Integer
For n = 5 To 75
Application.EnableEvents = False
If VarType(Cells(n, 1)) = vbEmpty Then
Cells(n, 4).ClearContents
Cells(n, 5).ClearContents
Cells(n, 6).ClearContents
Application.EnableEvents = True
End If
Next n
End Sub
The "FOR" Loop is annoying, and making the Excel to pause for 1 second or more after any entry to any Cell, can anyone help me correct the above code to do what I need to do without the "FOR" loop.
You are using a Worksheet_Change event and you iterating through 70 rows each time something changes.. this is a bad approach for this kind of problem and that's why there is a delay.
Instead, try
Private Sub Worksheet_Change(ByVal Target As Range)
Dim n As Long
If Target.Column = 1 Then
If IsEmpty(Cells(Target.Row, 1)) Then
Range("B" & Target.Row & ":F" & Target.Row).ClearContents
End If
End If
End Sub
this will only clear the cells if you remove a value from column A => when cell in column A is empty
Try this:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim i As Integer
If Mid(Target.Address(1, 1), 1, 2) = "$A" Then
If Target.Cells(1, 1).Value = "" Then
For i = 4 To 6
Target.Cells(1, i).Value = ""
Next i
End If
End If
End Sub
Give this a try:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rLook As Range, r As Range, Intr As Range
Set rLook = Range("A5:A75")
Set Intr = Intersect(rLook, Target)
If Intr Is Nothing Then Exit Sub
Application.EnableEvents = False
For Each r In Intr
If r.Value = "" Then
rw = r.Row
Range("D" & rw & ":F" & rw).ClearContents
End If
Next r
Application.EnableEvents = True
End Sub
It should have minimal impact on timing.
Use a range object.
The following line of code will print the address of the Range we'll use to clear the contents. The first cells call gets the upper left corner of the range, the second cells call gets the lower right corner of the range.
Private Sub test()
Debug.Print Range(Cells(5, 4), Cells(75, 6)).Address
End Sub
We apply this to your code like this:
Private Sub Worksheet_Change(ByVal Target As Range)
If VarType(Cells(Target.Row, 1)) = vbEmpty Then
Application.EnableEvents = False
Range(Cells(Target.Row, 4), Cells(Target.Row, 6)).ClearContents
Application.EnableEvents = True
End If
End Sub
One final sidenote: You should use an error handler to make sure events are always enabled when the sub exits, even if an error occurs.
Private Sub Worksheet_Change(ByVal Target As Range)
On Error GoTo ErrHandler
If VarType(Cells(Target.Row, 1)) = vbEmpty Then
Application.EnableEvents = False
Range(Cells(Target.Row, 4), Cells(Target.Row, 6)).ClearContents
End If
ExitSub:
Application.EnableEvents = True
Exit Sub
ErrHandler:
MsgBox "Oh Noes!", vbCritical
Resume ExitSub
End Sub
You should disable events and cater for multiple cells when using the Change event.
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rng1 As Range
Dim rng2 As Range
Set rng1 = Intersect(Columns("A"), Target)
If rng1 Is Nothing Then Exit Sub
With Application
.EnableEvents = False
.ScreenUpdating = False
End With
For Each rng2 In rng1.Cells
If IsEmpty(rng2.Value) Then rng2.Offset(0, 1).Resize(1, 5).ClearContents
Next
With Application
.EnableEvents = True
.ScreenUpdating = True
End With
End Sub
For those that need to have data entered in one cell cleared (in a column) when there's a change in another column use this, which is a modification of Gary's Student.
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rLook As Range, r As Range, Intr As Range
Set rLook = Range("D:D")
Set Intr = Intersect(rLook, Target)
If Intr Is Nothing Then Exit Sub
Application.EnableEvents = False
For Each r In Intr
If r.Value = "" Then
rw = r.Row
Range("L:L").ClearContents
End If
Next r
Application.EnableEvents = True
End Sub

Resources