Combine two subs with changing ranges - vba

I want to combine these two lines of code but cannot figure out how to get it to work. They both work separately, but I want the first line of code to be the first operation and then the second sub to be the second operation. These should execute whenever there is a change to the worksheet. The first routine should only cause a msg box when the corresponding cell in the "S" range updates in the same row as the cell that was updated in column A or B.
The second operation should look for any change in range "T7:T26" and prompt a msg box.
Code is below:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim myRng As Range
Dim lRow As Long
If Target.CountLarge > 1 Then Exit Sub
On Error GoTo Whoa
Set myRng = Range("A7:B26")
Application.EnableEvents = False
If Not Intersect(Target, myRng) Is Nothing Then
lRow = Target.Row
If Range("S" & lRow).Value >= 16 Then sVar = _
MsgBox("Will Enough Pre-Wave Resources be Available?", 4, "Attention!")
If sVar = 7 Then Application.Undo
End If
Letscontinue:
Application.EnableEvents = True
Exit Sub
Whoa:
MsgBox Err.Description
Resume Letscontinue
End Sub
and
Private Sub Worksheet_Change(ByVal Target As Range)
Dim myRng As Range
Set myRng = ThisWorkbook.Sheets("SMT 5").Range("T7:T26")
For Each mycell In myRng
If mycell.Value = "ISSUE" Then sVar = MsgBox("Possible Pre-Wave Manpower Issue on 2nd or 3rd Shift. Will Enough Resources be Available?", 4, "Attention!")
If sVar = 7 Then
Application.Undo
End If
Exit For
Next
End Sub

If both of them work on their own, you can copy the code into a module and give them two distinct names.
Then, in the Worksheet_Change sub you just use Call to run both subs.

Is this what you are trying?
Const sMsg1 As String = "Will Enough Pre-Wave Resources be Available?"
Const sMsg2 As String = "Possible Pre-Wave Manpower Issue on " & _
"2nd or 3rd Shift. Will Enough Resources be Available?"
Private Sub Worksheet_Change(ByVal Target As Range)
Dim myRng As Range, othrRng As Range, aCell As Range
Dim lRow As Long
Dim sVar
If Target.CountLarge > 1 Then Exit Sub
On Error GoTo Whoa
Set myRng = Range("A7:A26")
Set othrRng = Range("T7:T26")
Application.EnableEvents = False
If Not Intersect(Target, myRng) Is Nothing Then
lRow = Target.Row
If Range("S" & lRow).Value >= 16 Then sVar = _
MsgBox(sMsg1, 4, "Attention!")
If sVar = 7 Then Application.Undo
End If
For Each aCell In othrRng
If aCell.Value = "ISSUE" Then _
sVar = MsgBox(sMsg2, 4, "Attention!")
If sVar = 7 Then
Application.Undo
Exit For
End If
Next
Letscontinue:
Application.EnableEvents = True
Exit Sub
Whoa:
MsgBox Err.Description
Resume Letscontinue
End Sub

Private Sub Worksheet_Change(ByVal Target As Range)
Dim myRng As Range
Dim lRow As Long
If Target.CountLarge > 1 Then Exit Sub
On Error GoTo Whoa
Set myRng = Range("A7:B26")
Application.EnableEvents = False
If Not Intersect(Target, myRng) Is Nothing Then
lRow = Target.Row
If Range("S" & lRow).Value >= 16 Then sVar = _
MsgBox("Will Enough Pre-Wave Resources be Available?", 4, "Attention!")
If sVar = 7 Then Application.Undo
End If
Set othrRng = Range("T7:T26")
For Each aCell In othrRng
If aCell.Value = "ISSUE" Then sVar = MsgBox("Possible Pre-Wave Manpower Issue on 2nd or 3rd Shift. Will Enough Resources be Available?", 4, "Attention!")
If sVar = 7 Then
Application.Undo
Exit For
End If
Next
Letscontinue:
Application.EnableEvents = True
Exit Sub
Whoa:
MsgBox Err.Description
Resume Letscontinue
End Sub

Related

I am trying to update a previously entered date in the past to the current date, if a condition is met

I have a worksheet that I use to help forecast cash flow.
Column V is a date that is initially the anticipated invoice date, and upon issuance of an invoice, becomes the actual invoice date. Upon issuing a Purchase Order("D:D") or Invoice("F:F"), I manually enter the associated number and the current date is entered in the appropriate cells of that row, Purchase Order(With rCell.Offset(0, 11)){Column O} Invoice(With rCell.Offset(0, 16)){Column V}, to be referenced in other worksheets.
I've discovered the need to automatically make sure that the date in Column V is never in the past, which does occasionally occur. I am having difficulty with this. I need to only update Column V dates in the past, to the current date, IF Column F is empty.
Any assistance would be greatly appreciated!
Chris
This is what I have. The first two routines run as desired. The third is the current date issue, and does not function as expected.
Private Sub Worksheet_Change(ByVal Target As Excel.Range)
Dim rCell As Range
Dim rChange As Range
On Error GoTo ErrHandler
Set rChange = Intersect(Target, Range("D:D"))
If Not rChange Is Nothing Then
Application.EnableEvents = False
For Each rCell In rChange
If rCell > "" Then
With rCell.Offset(0, 11)
.Value = Now
.NumberFormat = "mm/dd/yy"
End With
End If
Next
End If
On Error GoTo ErrHandler
Set rChange = Intersect(Target, Range("F:F"))
If Not rChange Is Nothing Then
Application.EnableEvents = False
For Each rCell In rChange
If rCell > "" Then
With rCell.Offset(0, 16)
.Value = Now
.NumberFormat = "mm/dd/yy"
End With
End If
Next
End If
On Error GoTo ErrHandler
Set rChange = Intersect(Target, Range("F:F"))
If Not rChange Is Nothing Then
Application.EnableEvents = False
For Each rCell In rChange
If rCell = "" Then
With rCell.Offset(0, 16)
If .Value < Now - 1 Then
.Value = Now
.NumberFormat = "mm/dd/yy"
End If
End With
End If
Next
End If
ExitHandler:
Set rCell = Nothing
Set rChange = Nothing
Application.EnableEvents = True
Exit Sub
ErrHandler:
MsgBox Err.Description
Resume ExitHandler
End Sub
This seems to work for me:
Private Sub Worksheet_Change(ByVal Target As Excel.Range)
Dim rCell As Range
Dim rChange As Range
On Error GoTo ErrHandler
Set rChange = Intersect(Target, Range("D:D"))
If Not rChange Is Nothing Then
Application.EnableEvents = False
For Each rCell In rChange
If rCell > "" Then
With rCell.Offset(0, 11)
.Value = Now
.NumberFormat = "mm/dd/yy"
End With
End If
Next
End If
'## combining your two loops into a single block ##
Set rChange = Intersect(Target, Range("F:F"))
If Not rChange Is Nothing Then
Application.EnableEvents = False
For Each rCell In rChange
With rCell.Offset(0, 16)
.NumberFormat = "mm/dd/yy"
If Len(rCell) > 0 Then
.Value = Now
Else
If .Value < Now - 1 Then .Value = Now
End If
End With
Next
End If
ExitHandler:
Set rCell = Nothing
Set rChange = Nothing
Application.EnableEvents = True
Exit Sub
ErrHandler:
MsgBox Err.Description
Resume ExitHandler
End Sub

Running VBA caps/replace script on multiple lines of pasted data

I am running a VBA script to auto-capitalize and remove hyphens from pasted data into Excel. This script works great on single-line pastes (single-cell), but will not run (does nothing to change the data) if multiple lines of data are pasted in. The following is my code:
Private Sub worksheet_change(ByVal target As Range)
Application.EnableEvents = False
With target
On Error Resume Next
Dim rng As Range
Set rng = Range("A:U")
If Not Intersect(target, rng) Is Nothing Then
If Not .HasFormula Then
.Value = UCase(.Value)
.Value = Replace(.Value, "-", "")
End If
End If
End With
Application.EnableEvents = True
End Sub
Try this
Private Sub worksheet_change(ByVal target As Range)
Application.EnableEvents = False
With target
On Error Resume Next
Dim rng As Range
Dim cell As Range
Set rng = Range("A:U")
If Not Intersect(target, rng) Is Nothing Then
For Each cell in target
If Not cell.HasFormula Then
cell.Value = UCase(cell.Value)
cell.Value = Replace(cell.Value, "-", "")
End If
next cell
End If
End With
Application.EnableEvents = True
End Sub

Excel Macro - How to merge 2 Different Function of Macros

I am new to here and excel macro beginner. I need help for how to..join below macros into 1.
- The function of 1st macro is to move the cell to next row once specific cell is entered
- The function of second sub-macro is enter the timestamp when last specific cell of row is entered.
Thank you...Yanto
The macros:
1st Macro(main)
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
On Error GoTo Whoa
Application.EnableEvents = False
If Not Target.Cells.CountLarge > 1 Then
If Not Intersect(Target, Columns(1)) Is Nothing Then
Target.Offset(, 1).Select
ElseIf Not Intersect(Target, Columns(2)) Is Nothing Then
Target.Offset(, 1).Select
ElseIf Not Intersect(Target, Columns(3)) Is Nothing Then
Target.Offset(, 1).Select
ElseIf Not Intersect(Target, Columns(4)) Is Nothing Then
Target.Offset(1, -3).Select
End If
End If
Letscontinue:
Application.EnableEvents = True
Exit Sub
Whoa:
MsgBox Err.Description
Resume Letscontinue
End Sub
2nd Macro(sub)
Private Sub Worksheet_Change1(ByVal Target As Range)
If Intersect(Target, Range("D2:D3000")) Is Nothing Then Exit Sub
If Target.Count > 1 Then Exit Sub
If Target = "" Then Exit Sub
Dim lc As Long
With Application
.EnableEvents = False
.ScreenUpdating = False
lc = Cells(Target.Row, Columns.Count).End(xlToLeft).Column
If lc = 1 Then
Cells(Target.Row, lc + 2) = Now()
ElseIf lc > 1 Then
Cells(Target.Row, lc + 1) = Now()
End If
.EnableEvents = True
.ScreenUpdating = True
End With
End Sub
Just call the sub macro name in your main macro like:
Private Sub Worksheet_Change(ByVal Target As Range)
'''''''''''''''''some code''''''''''''''''''''
call Worksheet_Change1(Target)
'''''''''''''''''some code''''''''''''''''''''
End Sub
Friend,
Please ignore my comment. I managed to get the codes merged with exec output as expected. Thanks again
The codes:
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
On Error GoTo Whoa
Application.EnableEvents = False
If Not Target.Cells.CountLarge > 1 Then
If Not Intersect(Target, Columns(1)) Is Nothing Then
Target.Offset(, 1).Select
ElseIf Not Intersect(Target, Columns(2)) Is Nothing Then
Target.Offset(, 1).Select
ElseIf Not Intersect(Target, Columns(3)) Is Nothing Then
Target.Offset(, 1).Select
ElseIf Not Intersect(Target, Columns(4)) Is Nothing Then
Target.Offset(1, -3).Select
End If
End If
Call Worksheet_Change1(Target)
Letscontinue:
Application.EnableEvents = True
Exit Sub
Whoa:
MsgBox Err.Description
Resume Letscontinue
End Sub
Private Sub Worksheet_Change1(ByVal Target As Range)
If Intersect(Target, Range("D2:D3000")) Is Nothing Then Exit Sub
If Target.Count > 1 Then Exit Sub
If Target = "" Then Exit Sub
Dim lc As Long
With Application
.EnableEvents = False
.ScreenUpdating = False
lc = Cells(Target.Row, Columns.Count).End(xlToLeft).Column
If lc = 1 Then
Cells(Target.Row, lc + 2) = Now()
ElseIf lc > 1 Then
Cells(Target.Row, lc + 1) = Now()
End If
.EnableEvents = True
.ScreenUpdating = True
End With
End Sub
The Image link:

Create new sheets based on a list

When I create new sheets based on the below VBA Code, it works as I want, but there is a small problem. The issue is that when creating all the sheets based on the list given in Column ("A"), it create one more sheet with the same name of the original one and also show an error in the code in this section
ActiveSheet.Name = c.Value
Any assistant to correct.
Private Sub CommandButton1_Click()
On Error Resume Next
Application.EnableEvents = False
Dim bottomA As Integer
bottomA = Range("A" & Rows.Count).End(xlUp).Row
Dim c As Range
Dim ws As Worksheet
For Each c In Range("A2:A" & bottomA)
Set ws = Nothing
On Error Resume Next
Set ws = Worksheets(c.Value)
On Error GoTo 0
If ws Is Nothing Then
Sheets("Format").Select
Sheets("Format").Copy After:=Sheets(Sheets.Count)
ActiveSheet.Name = c.Value
End If
Next
Application.EnableEvents = True
End Sub
I think you forgot in your For statement to state which worksheet the range will be on. So that line should be something like this:
For Each c in worksheet(1).Range("A2:A" & bottomA)
Also there other issue in your code, I just made quick re-write..
Private Sub CommandButton1_Click()
Dim c As Range
Dim ws As Worksheet
Dim bottomA As Integer
On Error GoTo eh
Application.EnableEvents = False
bottomA = Range("A" & Rows.Count).End(xlUp).Row
For Each c In Worksheets(1).Range("A2:A" & bottomA)
'Set ws = Nothing
'On Error Resume Next
'Set ws = Worksheets(c.Value)
'On Error GoTo 0
'If ws Is Nothing Then
Sheets("Format").Select
Sheets("Format").Copy After:=Sheets(Sheets.Count)
ActiveSheet.Name = c.Value
'End If
Next
Application.EnableEvents = True
Exit Sub
eh:
Debug.Print ""
Debug.Print Err.Description
MsgBox (Err.Description)
End Sub
Try to be explicit as much as possible.
Private Sub CommandButton1_Click()
On Error GoTo halt ' Do not use OERN, that ignores the error
Application.EnableEvents = False
Dim bottomA As Long
' explicitly work on the target sheet
With Sheets("SheetName")
bottomA = .Range("A" & .Rows.Count).End(xlUp).Row
Dim c As Range, ws As Worksheet, wb As Workbook
' explicitly define which workbook your working on
Set wb = ThisWorkbook
For Each c In .Range("A2:A" & bottomA)
On Error Resume Next
Set ws = wb.Sheets(c.Value)
On Error GoTo 0
If ws Is Nothing Then
wb.Sheets("Sheet1").Copy _
After:=wb.Sheets(wb.Sheets.Count)
ActiveSheet.Name = c.Value
End If
Next
End With
forward:
Application.EnableEvents = True
Exit Sub
halt:
MsgBox Err.Number
Resume forward
End Sub
I don't know why you need to turn events On/Off (I don't see it required at least for your example). Nonetheless, I replaced the On Error Resume Next with a more flexible error handling routine because what you did is simply ignoring any errors. Check this out as well to improve how you work with objects and avoid unnecessary use of Active[object] and Select.

Excel VBA Event Handlers

1) Can someone please tell me what the issue could be with this code?
2) I need this code actually to run on a worksheet update, but # first-time load of the workbook, i'm running an update using the Workbook_Open event handler. Won't this trigger my Worksheet_Change event as well? Is there any way to avoid this
Private Sub Worksheet_Change(ByVal Target As Range)
Dim ws As Worksheet
Dim rowCount As Integer
Set Worksheet = "datasheet"
Set rowCount = ws.Cells(Rows.Count, "A").End(xlUp)
If Not Intersect(Target, Range("M3:M" & CStr(rowCount))) Is Nothing Then
MsgBox ("Hi")
End If
Else
If Not Intersect(Target, Range("T3:T" & CStr(rowCount))) Is Nothing Then
MsgBox ("Hi")
End If
Else
If Not Intersect(Target, Range("X3:X" & CStr(rowCount))) Is Nothing Then
MsgBox ("Hi")
End If
Else
If Not Intersect(Target, Range("AB3:AB" & CStr(rowCount))) Is Nothing Then
MsgBox ("Hi")
End If
Else
If Not Intersect(Target, Range("AI3:AI" & CStr(rowCount))) Is Nothing Then
MsgBox ("Hi")
End If
End Sub
I'm getting a Compiler error saying "object required" when i change my data with this handler. On the other hand, if i give values instead of taking a rowcount, I get no issues.
I always recommend this when using Worksheet_Change
You do not need the sheet name. It is understood that the code is to be run on current sheet unless you are trying to use another sheet row as a reference as correctly mentioned by brettdj in the comments below.
Whenever you are working with Worksheet_Change event. Always switch Off events if you are writing data to the cell. This is required so that the code doesn't go into a possible endless loop
Whenever you are switching off events, use error handling else if you get an error, the code will not run the next time.
Here is an example
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
On Error GoTo Whoa
Application.EnableEvents = False
'
'~~> Rest of the code
'
LetsContinue:
Application.EnableEvents = True
Exit Sub
Whoa:
MsgBox Err.Description
Resume LetsContinue
End Sub
So using the above, your code becomes (UNTESTED)
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rowCount As Long
On Error GoTo Whoa
Application.EnableEvents = False
With ActiveSheet
rowCount = .Cells(.Rows.Count, "A").End(xlUp).Row
End With
If Not Intersect(Target, Range("M3:M" & rowCount)) Is Nothing Then
MsgBox ("Hi")
ElseIf Not Intersect(Target, Range("T3:T" & rowCount)) Is Nothing Then
MsgBox ("Hi")
ElseIf Not Intersect(Target, Range("X3:X" & rowCount)) Is Nothing Then
MsgBox ("Hi")
ElseIf Not Intersect(Target, Range("AB3:AB" & rowCount)) Is Nothing Then
MsgBox ("Hi")
ElseIf Not Intersect(Target, Range("AI3:AI" & rowCount)) Is Nothing Then
MsgBox ("Hi")
End If
LetsContinue:
Application.EnableEvents = True
Exit Sub
Whoa:
MsgBox Err.Description
Resume LetsContinue
End Sub
EDIT:
Regarding your 2nd question. As I mentioned in the comment above, you can use a Public variable to check if the worksheet change event is being caused by the workbook open.
Place this code in a module.
Public WasWbOpened As Boolean
Place this code in the workbook code area
Option Explicit
Private Sub Workbook_Open()
WasWbOpened = True
'
'~~> Rest of the code
'
WasWbOpened = False
End Sub
And change your worksheet change event to
Private Sub Worksheet_Change(ByVal Target As Range)
If WasWbOpened = True Then Exit Sub
Dim rowCount As Long
On Error GoTo Whoa
Application.EnableEvents = False
With ActiveSheet
rowCount = .Cells(.Rows.Count, "A").End(xlUp).Row
End With
If Not Intersect(Target, Range("M3:M" & rowCount)) Is Nothing Then
MsgBox ("Hi")
ElseIf Not Intersect(Target, Range("T3:T" & rowCount)) Is Nothing Then
MsgBox ("Hi")
ElseIf Not Intersect(Target, Range("X3:X" & rowCount)) Is Nothing Then
MsgBox ("Hi")
ElseIf Not Intersect(Target, Range("AB3:AB" & rowCount)) Is Nothing Then
MsgBox ("Hi")
ElseIf Not Intersect(Target, Range("AI3:AI" & rowCount)) Is Nothing Then
MsgBox ("Hi")
End If
LetsContinue:
Application.EnableEvents = True
Exit Sub
Whoa:
MsgBox Err.Description
Resume LetsContinue
End Sub
You have several issues
As per Sid's comment you should use rowCount = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
You should be using Set ws = Sheets("datasheet") not Set Worksheet = "datasheet"
Your Else statements are causing errors as they are orphaned. If you want to exit on the first "Hi" rather than continuing to test you should try something like this
Sid has covered your other point. You can use a Boolean variable for this
updated code
this tests the intersect on columns M, T, X, AB and AI from row 3 to row rowCount in a single line
Private Sub Worksheet_Change(ByVal Target As Range)
Dim ws As Worksheet
Dim rowCount As Long
Set ws = Sheets("datasheet")
rowCount = ws.Cells(Rows.Count, "A").End(xlUp).Row
If Not Intersect(Target, Range("M3:M" & CStr(rowCount) & ",T3:T" & CStr(rowCount) & ",X3:X" & CStr(rowCount) & ",AB3:AB" & CStr(rowCount) & ",AI3:AI" & CStr(rowCount))) Is Nothing Then MsgBox ("Hi")
End Sub