Changing Specific Cell Condition to a Range Condition - vba

I would like to run a macro automatically when a cell changed(its value is equal or greater than "1") on G1:G500 Range.
I partially succeeded but;
It only works for one specific cell.
I could not write which tells it is equal or greater than 1. It only works if cell value is equal to 1.
I am truly sorry since I am a beginner at VBA. Helps appreciated.
Please see the whole code below;
Private Sub Worksheet_Change(ByVal Target As Range)
If Intersect(Target, Range("G12")) Is Nothing Then Exit Sub
If Target = "" Then Exit Sub
With Application
.EnableEvents = False
.ScreenUpdating = False
If UCase(Me.Range("G12").Value) = "1" Then
Call K999
End If
.ScreenUpdating = True
.EnableEvents = True
End With
End Sub

Try:
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("G1:G500")) Is Nothing Then
If Target = "" Then Exit Sub
On Error GoTo GetOut
With Application
.EnableEvents = False
.ScreenUpdating = False
If Target.Value >= 1 Then
Call K999
End If
End With
End If
GetOut:
Application.ScreenUpdating = True
Application.EnableEvents = True
End Sub

Private Sub Worksheet_Change(ByVal Target As Range)
'[1]
If Intersect(Target, Range("G1:G500")) Is Nothing Then Exit Sub
If Target = "" Then Exit Sub
'[2]
If Not IsNumeric(Target.Value) Then Exit Sub
With Application
.EnableEvents = False
.ScreenUpdating = False
'[3]
If Target.Value >= 1 Then
Call K999
End If
.ScreenUpdating = True
.EnableEvents = True
End With
End Sub
Intersect(Target, Range("G12")) checks the target is G12 or not, according to your statement, it should be Range("G1:G500")
Since you cannot force the user type a number into the cell, it is safer to check the value is number or not first.
"1" is a string, 1 is a number. No need to transtype into string then compare two string.

Related

Keeping cell unchanged under specified conditions

I am looking for way to keep a formula in a cell every time another cell is active.
Private Sub Worksheet_Change(ByVal Target As Range)
If ActiveSheet.Range("AL2").Value = 1 Then
ActiveSheet.Range("AK14").Value = ActiveSheet.Range("AL8").Value
Else
End If
End Sub
So if Cell AL2 is equal to 1 (so my desired active cell) I want to have a certain value in cell AK14.
If cell AL2 is NOT equal to 1 I want to just keep value in AK14 unchanged (so for example somebody can overwrite it).
At the moment Excel seems to get lost with the second part: if AL2 = 0 and I am getting an error.
If I need two conditions, do I just put another If?
Private Sub Worksheet_Change(ByVal Target As Range)
Application.EnableEvents = False
If Range("AL2").Value = 1 Then Range("F11").Value = Range("AK7").Value
Application.EnableEvents = True
End Sub
Private Sub Worksheet_Change(ByVal Target As Range)
Application.EnableEvents = False
If Range("AL2").Value = 2 Then Range("J11").Value = Range("AL7").Value
Application.EnableEvents = True
End Sub
so I want to have those two macros..
When you change value in a cell, in a Worksheet_Change event, you should disable the events. Otherwise, it starts calling itself:
Private Sub Worksheet_Change(ByVal Target As Range)
Application.EnableEvents = False
If Range("AL2").Value = 1 Then Range("AK14").Value = Range("AL8").Value
Application.EnableEvents = True
End Sub
Then, as a next step it is really a good practice to use Error-catcher here with the .EnableEvents:
Private Sub Worksheet_Change(ByVal Target As Range)
On Error GoTo Worksheet_Change_Error
Application.EnableEvents = False
If Range("AL2").Value = 1 Then Range("AK14").Value = Range("AL8").Value
Application.EnableEvents = True
On Error GoTo 0
Exit Sub
Worksheet_Change_Error:
Debug.Print "Error " & Err.Number & " (" & Err.Description & ") "
Application.EnableEvents = True
End Sub

Excel VBA Issue

Using the following code to auto upper two columns,
Private Sub Worksheet_Change(ByVal Target As Range)
If Not (Application.Intersect(Target, Range("C2:C5000", "P3:P5000")) _
Is Nothing) Then
With Target
Application.EnableEvents = False
.Value = UCase(.Value)
Application.EnableEvents = True
End With
End If
End Sub
Works perfectly, the problem is, if a user selects multiple cells, and hits delete, it errors, then the user hits End and the function no longer works. protected. Run-time error 13, type mismatch.
Doesn't matter if the cell is empty or not, still get the error.
Thanks in advance.
The answer of #ScottHoltzman solves the issue of the current problem, where an error is raised when you apply UCASE to an Array. When the Target range has more than one cell its .Value is an array, and UCase does not accept an array parameter.
Your routine will exit this line (.Value = UCase(.Value)) and will miss the next line that resets Application.EnableEvents = True. After that, you end up working with events disabled, so all your event handling routines will stop working, not only this one (in case you had other such routines).
To avoid these situations the good approach is to implement proper error handling in event handlers, following this structure
Sub my_Handler()
On Error Goto Cleanup
Application.EnableEvents = False: Application.ScreenUpdating = False ' etc..
''''''''''''''''''''''''''''''''''
'
' normal code of the routine here
'
''''''''''''''''''''''''''''''''''
Cleanup:
if Err.Number <> 0 Then MsgBox Err.Description
Application.EnableEvents = True, Application.ScreenUpdating = True ' etc..
End Sub
To apply it to your routine:
Private Sub Worksheet_Change(ByVal Target As Range)
On Error GoTo Cleanup
Application.EnableEvents = False: Application.ScreenUpdating = False ' etc..
If Not (Application.Intersect(Target, Range("C2:P5000")) Is Nothing) Then
Target.value = UCase(Target.value)
End If
Cleanup:
If Err.Number <> 0 Then msgBox Err.Description
Application.EnableEvents = True: Application.ScreenUpdating = True ' etc..
End Sub
Importantly, don't use this structure automatically for all you routines, only Event handlers or eventually macros ythat you would invoke from the GUI. Other routines are usually called from these handlers or macros, so you can write them normally.
I tried putting this in a comment to the answer, but was too long, so sorry..
#a-s-h #a.s.h
This one worked the best, with a slight modification. Thanks!
Private Sub Worksheet_Change(ByVal Target As Range)
On Error GoTo Cleanup
Application.EnableEvents = False: Application.ScreenUpdating = False ' etc..
If Not (Application.Intersect(Target, Range("C2:C5000", "P3:P5000")) Is Nothing) Then
Target.Value = UCase(Target.Value)
End If
Cleanup:
If Err.Number <> 0 Then GoTo EndLine
EndLine:
Application.EnableEvents = True: Application.ScreenUpdating = True ' etc..
End Sub
Performs uppercase, and deletes multiples at once without any errors, or MsgBox's.
If they are selecting multiple cells then my thinking is that you would want to use SelectionChange macro instead, like this
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim StartTime As Double
Dim SecondsElapsed As Double
StartTime = Timer
If ((Target.Address = Target.EntireRow.Address Or _
Target.Address = Target.EntireColumn.Address)) Then Exit Sub
Application.EnableEvents = False
If Not (Application.Intersect(Target, Range("C2:C5000", "P3:P5000")) _
Is Nothing) Then
On Error GoTo endItAll
For Each aCell In Target.Cells
Range(aCell.Address) = UCase(Range(aCell.Address))
Next aCell
End If
endItAll:
Application.EnableEvents = True
SecondsElapsed = Round(Timer - StartTime, 2)
Debug.Print SecondsElapsed
End Sub
Or you could change it back to the worksheet_Change macro like below and it will not error if the user selects multiple cells or deletes cells without causing an error. The error handler is there - Like in A.S.H. 's solution, but I haven't yet seen it needed in my testing.
Private Sub Worksheet_Change(ByVal Target As Range)
Application.EnableEvents = False
If Not (Application.Intersect(Target, Range("C1:C5000", "D1:D5000")) _
Is Nothing) Then
On Error GoTo endItAll
For Each aCell In Target.Cells
Range(aCell.Address) = UCase(Range(aCell.Address))
Next aCell
End If
endItAll:
Application.EnableEvents = True
End Sub
Account for multiple cells this way:
Private Sub Worksheet_Change(ByVal Target As Range)
If Not (Application.Intersect(Target, Range("C2:C5000", "P3:P5000")) _
Is Nothing) Then
Dim rCell as Range
Application.EnableEvents = False
For each rCell in Target
rCell.Value = UCase(rCell.Value)
Next
Application.EnableEvents = True
End If
End Sub

VBA on change event ruins the ListObjectTable on extending rows

My code works with a listobject.table and is intended to allow editing prices and calculating discounts or vice versa...
On entering the cell for editing it turns the formula therein into a value and pastes the formula in another column.
It works like a charm when the user is editing cells. But if the user tries to add rows to the listobject.table, the macro ruins the table. It adds a couple of columns and some headers are replaced.
Is it possible to make the macro somehow to disregard the operation of adding new rows or extending the range of data.table?
Here is the macro, thank you my friends for advice:
Private oListObj As ListObject
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
Set oListObj = Worksheets("Quotation").ListObjects("tblProForma")
Application.EnableEvents = True
If Not Intersect(Target, oListObj.ListColumns("Price").DataBodyRange) Is Nothing Then
Application.EnableEvents = False
Application.AutoCorrect.AutoFillFormulasInLists = False
Target.Formula = Target.Value
Application.EnableEvents = True
End If
If Not Intersect(Target, oListObj.ListColumns("Discount").DataBodyRange) Is Nothing Then
Application.EnableEvents = False
Application.AutoCorrect.AutoFillFormulasInLists = False
Target.Formula = Round(Target.Value, 5)
Application.EnableEvents = True
End If
End Sub
Private Sub Worksheet_Change(ByVal Target As Range)
Dim PriceDiscountOffset As Integer: PriceDiscountOffset = ActiveSheet.Range("tblProForma[[#All],[Price]:[Discount]]").Columns.Count - 1
Set oListObj = Worksheets("Quotation").ListObjects("tblProForma")
Application.EnableEvents = True
If Not Intersect(Target, oListObj.ListColumns("Price").DataBodyRange) Is Nothing Then
Application.EnableEvents = False
Application.AutoCorrect.AutoFillFormulasInLists = False
Target.Offset(0, PriceDiscountOffset).Formula = "=IF([#[Price]]<>"""", -([#[Price]]-[#[Pricelist]])/[#[Price]],"""")"
Application.EnableEvents = True
End If
If Not Intersect(Target, oListObj.ListColumns("Discount").DataBodyRange) Is Nothing Then
Application.EnableEvents = False
Application.AutoCorrect.AutoFillFormulasInLists = False
Target.Offset(0, -PriceDiscountOffset).Formula = "=[#[Pricelist]]-([#[Pricelist]]*[#[Discount]])"
Application.EnableEvents = True
End If
End Sub

How to end infinite "change" loop in VBA

I have a problem with visual basic. I want to make a macro/function that will multiply a number I enter by 3 and give a result in the same cell. I tried something like this:
Sub Worksheet_Change(ByVal Target As Range)
If Target.Address = "$Q$21" Then
Target.Value = Target.Value * 3
End If
End Sub
but it doesn't work - I'm getting results like "xE25" because it keeps multiplying.
I'd like it to stop after first iteration or work only when I press "enter" not with every change in the cell.
It's quite easy to put a result in different cell, but it's not my point.-----Edit:
I edited "If" line to :
If (Target.Column = 5 Or Target.Column = 11 Or Target.Column = 17 Or Target.Column = 23) And (Target.Row >= 19 And Target.Row <= 24) And Target.Value <> "" Then so it would work on all cells that I need. After that, the best solution is the way given by #Chrismas007, because it doesn't prompt an error when trying to delete data in few cells at once.
With error handling to ensure .EnableEvents goes back to True:
Sub Worksheet_Change(ByVal Target As Range)
On Error GoTo CleanExit
If Target.Address = "$Q$21" Then
Application.EnableEvents = False
Target.Value = Target.Value * 3
End If
CleanExit:
Application.EnableEvents = True
On Error GoTo 0
End Sub
When a Worksheet_Change event macro changes a value, you need to set Application.EnableEvents to false or risk triggering another event and having the macro run on top of itself.
Sub Worksheet_Change(ByVal Target As Range)
If Target.Address = "$Q$21" Then
Application.EnableEvents = False
Target.Value = Target.Value * 3
Application.EnableEvents = True
End If
End Sub
While I would usually involve some error control in order that the .EnableEvents was always reset to true, the above should get you started.
You need to disable events to prevent recursion:
Sub Worksheet_Change(ByVal Target As Range)
If Target.Address = "$Q$21" Then
application.enableevents = false
Target.Value = Target.Value * 3
application.enableevents = true
End If
End Sub
Just as an alternative, you can also use your own variable rather than tinkering with EnableEvents, though it will mean your code responds twice even though it doesn't actually do anything the second time round:
Dim bSkipEvents as Boolean
Sub Worksheet_Change(ByVal Target As Range)
If bSkipEvents then exit sub
If Target.Address = "$Q$21" Then
bSkipEvents = True
Target.Value = Target.Value * 3
bSkipEvents = False
End If
End Sub
This is also the sort of approach you need with userforms and most events for activex controls on worksheets.

macros still running even if the cell/ range value changed

I want to select a range (B2) resulting from the dropdown referring from another range (source from F2: F5). I mean I do not need to change the values in the macro code if one day I had to change the data in the reference range (F2: F5). If the value in the range B2 is equal to the value of the text on one of the list range F2: F5 then the macro will run.
I want something like this:
Private Sub Worksheet_Calculate(ByVal Target As Range)
If Target.Address = "$B$2" Then
If Range("B2").Value = Range("F3").Value Then
Rows("10:20").EntireRow.Hidden = False
Rows("11:21").EntireRow.Hidden = True
ElseIf Range("B2").Value = Range("F4").Value Then
Rows("10:20").EntireRow.Hidden = True
Rows("11:21").EntireRow.Hidden = False
..............
..............
..............
End If
End If
End Sub
How can I re-write this logic in a way that is VBA friendly? Thanks for your help
You'll want to use a Worksheet_Change... This activates on any changes to the sheet. (Put this code in the SHEET's CODE). First it checks to make sure the cell is B2 then it checks to make sure B2's Value is found in your reference cells. Then it does a Select Case for each possible option.
Private Sub Worksheet_Change(ByVal Target As Range)
If Intersect(Target, Range("B2")) is Nothing Then
Exit Sub
End If
Select Case Range("B2").Value
Case Range("F2").Value
'Do Something
Case Range("F3").Value
'Do Something
Case Range("F4").Value
'Do Something
Case Range("F5").Value
'Do Something
Case Else
Exit Sub
End Select
End Sub
Private Sub Worksheet_Change(ByVal Target As Range)
With Target
If .Address = "$B$2" Then
Application.EnableEvents = False
Select Case .Value
Case Range("F3").Value
Rows("10:20").EntireRow.Hidden = False
Rows("11:21").EntireRow.Hidden = True
Case Range("F4").Value
Rows("10:20").EntireRow.Hidden = True
Rows("11:21").EntireRow.Hidden = False
End Select
Application.EnableEvents = True
End If
End With
End Sub
Why do you not use the Select case structure:
Private Sub Worksheet_Calculate(ByVal Target As Range)
If Target.Address = "$B$2" Then
Select Case Range("B2").Value
case Range("F3").Value
Rows("10:20").EntireRow.Hidden = False
Rows("11:21").EntireRow.Hidden = True
case Range("B2").Value = Range("F4").Value Then
Rows("10:20").EntireRow.Hidden = True
Rows("11:21").EntireRow.Hidden = False
..............
..............
..............
End Select
End If
End Sub