Run macro if any cell in a non-contiguous range are changed - vba

i have a non-contiguous range and i need to run a privatesub, if any of this cells change, but only if all cells in this range aren't empty i've tried this but isn't working, since it's running the macro iven if the cells are empty, here:
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("D3")) Is Nothing Then
If (ActiveSheet.Range("D3") <> """") And (ActiveSheet.Range("D5") <> """") And _
(ActiveSheet.Range("I3") <> """") And (ActiveSheet.Range("O3") <> """") And _
(ActiveSheet.Range("O5") <> """") And (ActiveSheet.Range("O7") <> """") And _
(ActiveSheet.Range("X3") <> """") And (ActiveSheet.Range("X5") <> """") _
Then
Create
End If
End If
End Sub
Thank you for your time =]

I think this should meet your requirements:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rng As Range
Set rng = Range("D3,D5,I3,O3,O5,O7,X3,X5")
If Intersect(Target, rng) Is Nothing Then Exit Sub
For Each r In rng
If r.Value = "" Then
Exit Sub
End If
Next r
MsgBox "I am going to do something"
End Sub

Related

IF Null exit sub code

I'm trying to check that if cells are empty or null, that it would display a message and exit sub. Here's my code:
With Worksheets(1).[D3:D4, D6:D14]
If WorksheetFunction.CountA(.Cells) = 0 Then
MsgBox "Nothing to generate" & vbNewLine & _
"Set parameters and click generate."
Exit Sub
End If
End With
But the code only works if the entire [D3:D4, D6:D14] are empty. I want it to exit sub even just one of the cells are empty. All cells needs have content for it to continue sub. Please help.
You need to seach inside the Range, try the code below:
Sub CheckEmptyCellsinRange()
Dim Rng As Range
Dim cell As Range
Set Rng = Worksheets(1).[D3:D4, D6:D14]
For Each cell In Rng
If IsEmpty(cell) Or IsNull(cell) Then
MsgBox "Nothing to generate" & vbNewLine & _
"Set parameters and click generate."
Exit Sub
End If
Next cell
End Sub
You can also use SpecialCells:
Sub Check()
Dim rng1 As Range
On Error Resume Next
With Worksheets(1).[D3:D4, D6:D14]
Set rng1 = .SpecialCells(xlBlanks)
If Not rng1 Is Nothing Then
MsgBox "Nothing to generate" & vbNewLine & _
"Set parameters and click generate."
Exit Sub
End If
End With
End Sub
Follow-up question
Sub Check2()
Dim rng1 As Range
Dim rng2 As Range
With Worksheets(1)
Set rng1 = .Range(.[D3:D4], .[D6:D14])
End With
On Error Resume Next
Set rng2 = rng1.SpecialCells(xlBlanks)
On Error GoTo 0
If Not rng1 Is Nothing Then
MsgBox rng1.Cells.Count - rng2.Cells.Count & " were used"
Else
MsgBox "all cells used"
End If
End Sub

Worksheet_BeforeDoubleClick for a selection

i have the below code that doesn't work for a selection:
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
Dim Rng1 As Range
Dim I As Integer
If Selection.Count > 1 Then
For Each cell In Selection
If I = 0 Then
Set Rng1 = Range(cell.Address)
Else
Set Rng1 = Union(Range(cell.Address), Rng1)
End If
I = I + 1
Next cell
MsgBox "You have selected the range " & Rng1.Address(False, False)
Rng1.End(xlDown).Offset(0, 1).Activate
Else
MsgBox "you are in " & ActiveCell.Address(False, False)
ActiveCell.Offset(1, 0).Activate
End If
End Sub
I tried selecting a range of cells and then double clicking, does anybody know if that is even possible?
Because double-clicking collapses any multi-cell selection you have previously made, you'd need to keep track of it, and then check whether Target is within it.
Something like:
Dim rng As Range
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
If Not rng Is Nothing Then
If Not Application.Intersect(Target, rng) Is Nothing Then
Debug.Print "Clicked in selected range: " & rng.Address()
Else
Debug.Print "Cell: " & Target.Address()
End If
Set rng = Nothing
Else
Debug.Print "No previous range: clicked in " & Target.Address()
End If
End Sub
'keeping track of the last multi-cell range selected....
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
If Target.Count > 1 Then Set rng = Target
End Sub

Excel issue with integrating string text splitter function and worksheet_change

I'm trying to write a piece of code that automatically separates the data scanned in from a plug&play scanner from a 2D bar-code. The data is in this format "SN1234567 7654321 PA01234-5 A B C" and I need each block of text/numbers into each own cell. Now I was successful in finding a macro online to split this text(shown below), and also a macro to automatically run A (not my macro) macro when entering data into A1. the problem is I cant get the worksheet_change sub to work with my splittext macro. Code shown below
Private Sub Worksheet_Change(ByVal Target As Range)
Dim KeyCells As Range
' The variable KeyCells contains the cells that will
' cause an alert when they are changed.
Set KeyCells = Range("A1:C10")
If Not Application.Intersect(KeyCells, Range(Target.Address)) _
Is Nothing Then
' Display a message when one of the designated cells has been
' changed.
' Place your code here.
MsgBox "Cell " & Target.Address & " has changed."
Call textsplit
End If
End Sub
Sub textsplit()
Dim text As String
Dim a As Integer
Dim name As Variant
text = ActiveCell.Value
name = Split(text, " ")
For a = 0 To UBound(name)
Cells(1, a + 1).Value = name(a)
Next a
End Sub
It's not really clear where you want the split values to go, but something along these lines works:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim KeyCells As Range, rng As Range
' The variable KeyCells contains the cells that will
' cause an alert when they are changed.
Set KeyCells = Range("A1:C10")
'Target can be a multi-cell range, so you need to account
' for that possibility
Set rng = Application.Intersect(KeyCells, Target)
If Not rng Is Nothing Then
' Display a message when one of the designated cells has been
' changed.
' Place your code here.
Debug.Print "Cell " & Target.Address & " has changed."
'prevent re-activating this sub when splitting text...
Application.EnableEvents = False
textsplit Target
Application.EnableEvents = True
End If
Exit Sub
haveError:
Application.EnableEvents = True
End Sub
Sub textsplit(rng As Range)
Dim c As Range, arr
For Each c In rng.Cells
If Len(c.Value) > 0 Then
arr = Split(c.Value, " ")
c.Offset(0, 1).Resize(1, UBound(arr) + 1).Value = arr
End If
Next c
End Sub
I modified some of your code to use TextToColumns instead of textsplit() which works.
Private Sub Worksheet_Change(ByVal Target As Range)
Dim KeyCells As Range
Set KeyCells = Range("A1:C10")
If Not Application.Intersect(KeyCells, Range(Target.Address)) _
Is Nothing Then
MsgBox "Cell " & Target.Address & " has changed."
Target.TextToColumns Destination:=Range(Target.Address), DataType:=xlDelimited, Space:=True
End If
End Sub
Once the cell was changed, the ActiveCell is no longer the target. Send the Sub the target, see below:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim KeyCells As Range
' The variable KeyCells contains the cells that will
' cause an alert when they are changed.
Set KeyCells = Range("A1:C10")
If Not Application.Intersect(KeyCells, Range(Target.Address)) _
Is Nothing Then
' Display a message when one of the designated cells has been
' changed.
' Place your code here.
MsgBox "Cell " & Target.Address & " has changed."
Call textsplit(Target)
End If
End Sub
Sub textsplit(Target)
Dim text As String
Dim a As Integer
Dim name As Variant
text = Target.Value
name = Split(text, " ")
For a = 0 To UBound(name)
Cells(1, a + 1).Value = name(a)
Next a
End Sub

Combine two subs with changing ranges

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

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