Intersect with Target - vba

I cannot figure out how to make the code below work. Every call results in Intersection = Nothing and the ELSE statement selected and yes rngIntersect is valid and non-empty and the intersection is not empty so the problem is with the target part.
When I debug the code and ask ? target the result is zilch. However target.column, target.row, and target.address are all correct 3,9, and $C$9 respectively).
This must be something basic but after all the looking around on different sites I have yet to come up with a solution.
I can find if the target is within range by using target.column and target.row but I cannot make the more elegant code below work. For whatever reason target seems to be 'somewhat but not completely empty'.
To emphasize, in the debug window ? target returns empty but ? target.address returns $C$9 as it should because that is the cell the user has selected and rngIntersect = $C$9:$N$9, hence Intersect = $C$9 definitely <> Nothing.
I have tried ? range(target) and ? target.range but that generates an error.
In essence, how do I make target return the range as it is supposed to, after all it is defined as a range to begin with right?
Sub OnSelectionChange(ByVal target As Range, wksChange As Worksheet)
On Error GoTo ErrorHandler
Dim rngIntersect As Range, rngTarget As Range
Set rngIntersect = wksChange.Range("RNGINTERSECT")
Set rngTarget = target
If Not Application.Intersect(rngTarget, rngIntersect) Is Nothing Then
Call WorksheetSelectionChange(target.Column, wksChange)
Else
Call MAIN.ZoomAdjust(wksChange, False)
End If
ErrorExit:
Exit Sub
ErrorHandler:
Call LogError(Err.Number, Err.Description, C_MODULE, "OnSelectionChange")
Resume ErrorExit
End Sub

I think your problem isn't from your code but from your expectation, to wit that ? Target should print something. Target is a range, and when you don't specify which property you want to print the default property will be printed which is .Value. The fact that the cell may be empty says nothing about the existence of it as a range object.
Target is a range object. Therefore you don't need to convert it into a range object, like rngTarget. As a range object, one of its properties is .Worksheet which, therefore, you don't need to pass along as an additional argument. Bear I mind that Application.Intersect also returns a range object.
Below is working code. I have stripped out anything that might confuse you, your error handlers in particular. If this kind of code ever creates an error you want to know about it immediately and not only after 3 hours of futile search.
Option Explicit
Private Sub TestIntersect()
OnSelectionChange Range("C9")
OnSelectionChange Range("C3")
End Sub
Sub OnSelectionChange(ByVal Target As Range)
Dim WksChange As Worksheet
Dim RngIntersect As Range
Set WksChange = Target.Worksheet
' Set RngIntersect = WksChange.Range("RNGINTERSECT")
Set RngIntersect = WksChange.Range("C9:N9")
If Not Application.Intersect(Target, RngIntersect) Is Nothing Then
WorksheetSelectionChange Target.Column, WksChange
Else
MAINZoomAdjust WksChange, False
End If
End Sub
Private Sub WorksheetSelectionChange(ByVal C As Long, _
Ws As Worksheet)
MsgBox "Here is WorksheetSelectionChange" & vbCr & _
"Column " & C
End Sub
Private Sub MAINZoomAdjust(Ws As Worksheet, _
ByVal TooBig As Boolean)
MsgBox "Here is MAINZoomAdjust" & vbCr & _
"TooBig = " & TooBig
End Sub

I did find the problem. The correct code should be
If Not Application.Intersect(target, wksChange.Range(wksChange.Range("RNGINTERSECT"))) Is Nothing Then
instead of
If Not Application.Intersect(target, wksChange.Range("RNGINTERSECT")) Is Nothing Then
The reason is that wksChange.Range("RNGINTERSECT") returns the string value for the range of interest, not the range itself

Related

Fire 'Worksheet_Change' event only when cell value is actually different

I wrote code to extract data from the URL a particular page.
First time I run the code it extracts data from URL to cell C1.
I am at a point where I want to display a MsgBox whenever cell value changes.
For example:
First time I run the code "Happy" gets extracted to cell C1.
(Cell value changes, so msgbox "value changed")
The second time I run the code then Also "Happy" gets extracted to the cell C1.
(means no change, Noting happens)
The third time I run the code and "Sad" gets extracted to cell C1,
so at this point, I want a msgbox of the cell change.
I tried the below code but it shows the msgbox even when same values are changed in the cell.
For example - Cell contains text "Happy". I rewrite "Happy" in cell and press enter, so it displays msgbox of cell changed despite being same text in the cell.
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."
End If
End Sub
This uses Undo to check what the previous value of the cell was, and then compare it to the new value.
This will also not be case-sensitive, so HAPPY = HAPpy. If you want it to be case sensitive then remove the strconv functions.
Note that (any) of these procedures (including yours) will not react properly to multiple cells changing at once (like pasting in a range of cells), but you could add code to handle that however you needed to as demonstrated in the commented out lines.
But for single cells, this will do the trick:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim KeyCells As Range, vNew, vOld
If Target.Cells.Count > 1 Then
MsgBox ("multiple cells changed: " & vbLf & Target.Address)
'to handle multiple cells changing at omce you'll need to loop like:
' dim c as cell
' for each c in Target.Cells
' ... etc
Exit Sub
End If
Set KeyCells = Range("A1:C10") ' cells to watch
If Not Application.Intersect(KeyCells, Target) Is Nothing Then
vNew = Target.Value
Application.EnableEvents = False
Application.Undo
vOld = Target.Value
Target.Value = vNew
Application.EnableEvents = True
'make sure value is different (NOT case sensitive)
If StrConv(vNew, vbLowerCase) <> StrConv(vOld, vbLowerCase) Then
'do something here
MsgBox "Cell " & Target.Address & " changed" & vblf & _
"From: " & vOld & vblf & _
"To: " & vNew
End If
End If
End Sub
More Information:
MSDN : Application.Undo Method (Excel)
MSDN : Application.EnableEvents Property (Excel)
MSDN : Worksheet.Change Event (Excel)
Stack Overflow : How do I get the old value of a changed cell in Excel VBA? (Ronnie Dickson's answer)
Replace:
If Not Application.Intersect(KeyCells, Range(Target.Address))
with:
If Not Application.Intersect(KeyCells, Target)
Try it like this:
Public PrevValue
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
If Target.Value <> PrevValue Then
MsgBox ("value changed")
PrevValue = Target.Value
End If
End Sub
The previous value is now stored in the global variable. When the value changes, it first checks if the value is the same as the previous value.
Edit:
If you change different cells each time, you can also use
Private Sub Workbook_SheetSelectionChange(ByVal Sh As Object, ByVal Target As Range)
PrevValue = Target.Value
End Sub
To set the value of the currenctly selected cell before the change.

Type Mismatch error when range of data is changed in Excel

I have written a macro to color my cells green if the input is TRUE and red if the input to cell is FALSE.
Private Sub Worksheet_Change(ByVal Target As Range)
If ActiveSheet.Name = "Ribs" Then
If Not Intersect(Target, Range("G2:K200")) Is Nothing Then
If Target = "False" Then
Sheets("Ribs").Range(Target.Address).Style = "Bad"
ElseIf IsNumeric(Target) Then
Sheets("Ribs").Range(Target.Address).Style = "Good"
End If
ElseIf Not Intersect(Target, Range("D2:D200")) Is Nothing Then
RotateRib (Target.Address)
End If
End If
End Sub
Now the problem is that if I change the range value (for example typing TRUE in cell G2 and than drag mouse pointer from bottom right corner of G2 to G10 should copy value TRUE to range G2:G10) raises Type Mismatch error in my macro.
Debugger says the problematic line is If Target = "False" Then.
Is there a workaround the given error? Ignoring the error would probably do the job, but it's not something I'd like to do.
The problem is that you're trying to do an illegal operation. You're asking the compiler to see if the contents of G2:G10 is equal to False - you can see this by adding Debug.Print Target.Address to the top of your code and then making another attempt.
It is possible to do what you want, but you'll need more code. When comparing values, you have to do it cell by cell - you can't compare an entire range at once. Here's a rudimentary example:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim c As Range
If ActiveSheet.Name = "Ribs" Then
For Each c In Target
If Not Intersect(c, Range("G2:K200")) Is Nothing Then
If c.Value = "False" Then
Sheets("Ribs").Range(c.Address).Style = "Bad"
ElseIf IsNumeric(c.Value) Then
Sheets("Ribs").Range(c.Address).Style = "Good"
End If
ElseIf Not Intersect(c, Range("D2:D200")) Is Nothing Then
RotateRib (c.Address)
End If
Next c
End If
End Sub
The principal change is that we're no longer comparing against Target, we're looping through all the individual cell contents (Range objects denoted as c) of Target and comparing against those.
Again, you can verify that this works by trying this code and filling down some values:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim c As Range
For Each c In Target
Debug.Print c.Address
Next c
End Sub
There's absolutely no need to check the name of active sheet, since Worksheet_Change event fires on the sheet where it's defined.
Rather iterating over each cell in the Target, you could receive the intersection and apply your settings directly.
Don't forget about that Target can contain non-contiguous ranges (accessed by Areas property). My code handles this situation, but can't say the same about RotateRib.
To sum up:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rngIntersect As Range, rngArea As Range, cell As Range
Set rngIntersect = Intersect(Target, Range("G2:K200"))
If Not rngIntersect Is Nothing Then
For Each rngArea In rngIntersect.Areas
For Each cell In rngArea
cell.Style = IIf(cell, "Good", "Bad")
Next
Next
End If
Set rngIntersect = Intersect(Target, Range("D2:D200"))
If Not rngIntersect Is Nothing Then RotateRib (rngIntersect)
End Sub

Worksheet Change Event

I have the below code which I effectively want to rename worksheets based on the Value of I16. However if the target address is blank/ Nothing I wish to exit the sub. (this part of the code is not working).
If anyone could advise how I could resolve this issue it would be greatly appreciated.
Private Sub Worksheet_Change(ByVal Target As Range)
Dim KeyCells As Range
Set KeyCells = Range("I16")
Dim WSname As String
WSname = Range("I16").Value
If KeyCells Is Nothing Then Exit Sub
Sheet23.Name = "BISSB"
Sheet25.Name = "RMIB"
Sheet26.Name = "MORIB"
Worksheets(WSname).Name = "Stage 3 V1"
End Sub
Replace:
If KeyCells Is Nothing Then Exit Sub
With:
If Trim(WSname) = "" Then Exit Sub
Explanation: you already use Set KeyCells = Range("I16") in your code, so you set your KeyCells Range, therefore it will never be Nothing.
You want to check the value of KeyCells range, and you have your WSname String variable.
Instead of
If KeyCells Is Nothing Then Exit Sub
use
If IsEmpty(KeyCells) Then Exit Sub
The ISEMPTY function can be used to check for blank cells. If cell is blank it will return TRUE else FALSE.
You are already declare and set KeyCells to "I16". This is why if condition doesnt work - because KeyCells already contains cell. Ask if WSname = "" or check other way if it contains value or no.
I think the correct way to use Change Event code is to tell the code when to be automatically triggered and perform some actions.
Right now, your code will be triggered and perform the actions defined in the code each time when any cell on the sheet gets changed.
I assume, you want to trigger the Change Event Code and perform some predefined actions only when the cell I16 gets changed and then rename the sheets as per the code. Right?
If so, you may try something like this...
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.CountLarge > 1 Then Exit Sub
Dim KeyCells As Range
Set KeyCells = Range("I16")
Dim WSname As String
WSname = Range("I16").Value
If Not Intersect(Target, KeyCells) Is Nothing Then
If Target <> "" Then
Sheet23.Name = "BISSB"
Sheet25.Name = "RMIB"
Sheet26.Name = "MORIB"
Worksheets(WSname).Name = "Stage 3 V1"
End If
End If
End Sub

use inputbox as excel range

I'd like the user to input a range of cells such as A1:Z26. I've tried adding quotations, I've tried have 2 inputboxes, one for beginning and end of the range. But it errors out everytime with: 'method range of object_global failed'
I know it's a simple syntax issue (I think) so can anyone point me in the right direction in terms of how to have the user input a range that works in the set rng = range(msg)
Sub iterationLoop()
Dim rng As Range, iteration As Range
msg = "What is the range you'd like to look at: (e.g. A1:B2)"
InputBox (msg)
Set rng = Range(msg)
For Each iteration In rng
iteration.Select
If iteration = vbNullString Then
iteration = "add value"
MsgBox ("Cell: " & Selection.Address & " has no value")
End If
Next
End Sub
Application.InputBox allows you to specify the input type. Type 8 corresponds to a range. This will allow the user to either select the range with a mouse or type it in manually:
Sub test()
Dim rng As Range
Set rng = Application.InputBox("Select by mouse or enter (e.g. A1:B2) the range you'd like to look at:", Type:=8)
MsgBox rng.Address
End Sub
If you intend your code to be used by others, you should probably wrap the Application.InputBox call in some error-handling code since the above code raises a run-time error if the user presses Cancel. Something like:
On Error Resume Next
Set rng = Application.InputBox("Select by mouse or enter (e.g. A1:B2) the range you'd like to look at:", Type:=8)
If Err.Number > 0 Then
MsgBox "No Range Selected"
Exit Sub
End If
On Error GoTo 0
(though you might want to do something more useful than just quitting the sub)
aAdd
Dim rngstr as string
Then with the inputbox use this:
rngstr = inputbox(msg)
set rng = Range(rngstr)

Run-Time error '1004' with my VBA for hiding and unhiding rows

I have code for hiding and unhiding rows in my sheet based on changing the value in my dropdown. Every time I change the dropdown I get Run-Time error of '1004'. I had a private Sub before and changed it to a Sub but that doesn't seem to be the solution.
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rng As Range
Set rng = Target.Parent.Range("L6")
If Target.Count > 1 Then Exit Sub
If Intersect(Target, rng) Is Nothing Then Exit Sub
Application.Run "dynamic_hide"
End Sub
Sub dynamic_hide()
If Target.Range = "$S$9:$S$51" Then
If Target.Range = 0 Then Rows("F9:T51").EntireRow.Hidden = True
If Target.Value <> 0 Then Rows("F9:T51").EntireRow.Hidden = False
End If
End Sub
You have a few problems going on here:
First, the default property of a Range object is Value, so Target.Range = "$S$9:$S$51" will always be false. Use Target.Address instead.
Second, don't use Application.Run to call Subs from the same VBProject. Use Call instead.
Third, you've not let the sub dynamic_hide know what Target is since Target is only a parameter of the Worksheet_Change event subroutine. You can solve this by declaring your sub like Sub dynamic_hide(ByVal Target As Range) And then you can use it: Call dynamic_hide(Target)
Lastly, since Target is a range you don't need to use Target.Range since Target is a range so you can simply omit every .Range from Target.Range Target.Parent.Range is fine.