How to have apply Worksheet_Change to more than one cell? - excel

I implemented code to timestamp a cell whenever a condition in another cell in the same row is manually met:
Private Sub Worksheet_Change(ByVal target As Range)
Dim A As Range: Set A = Range("A2:A2800")
Dim v As String
If Intersect(target, A) Is Nothing Then Exit Sub
Application.EnableEvents = False
v = target.Value
If v = "" Then target.Offset(0, 6) = ""
If v = "Solicitud enviada" Then target.Offset(0, 6) = Date
Application.EnableEvents = True
End Sub
I need to timestamp another cell by a different criteria. I know I can't have two Worksheet_Change subs at the same time, but from what I've investigated trying to have two events at the same time goes beyond me.
Private Sub LeadTimeStamp(ByVal target As Range)
Dim D As Range: Set D = Range("D2:D2800")
Dim b As String
If Intersect(target, D) Is Nothing Then Exit Sub
Application.EnableEvents = False
b = target.Value
If b = "" Then target.Offset(0, 8) = ""
If b = "lead" Then target.Offset(0, 8) = Date
Application.EnableEvents = True
End Sub
b needs to be compared as a string array with the cell, something like b.length <= 10 if this was JavaScript.
I know that VBA uses LEN(), but I do not know how to use it here. For now I have a placeholder condition similar to the one on the original code, to make sure that the code works before I tackle the array condition part.

Your situation is that you need to check for one of several possible changes, so that means an If statement at the Target level. So in outline form it would look like this:
Private Sub Worksheet_Change(ByVal Target As Range)
'--- only deal with single-cell changes. multi-cell
' edits are skipped
If Target.CountLarge > 1 Then Exit Sub
Dim solicitudArea As Range
Dim leadArea As Range
Set solicitudArea = Range("A2:A2800")
Set leadArea = Range("D2:D2800")
Application.EnableEvents = False
If Not Intersect(target, solicitudArea) Is Nothing Then
'--- a request has changed
If Target.Value = vbNullString Then
Target.Offset(0, 6).Value = vbNullString
ElseIf Target.Value = "Solicitud enviada" Then
Target.Offset(0, 6).Value = Date()
End If
ElseIf Not Intersect(target, leadArea) Is Nothing Then
'--- a request has changed
If Target.Value = vbNullString Then
Target.Offset(0, 8).Value = vbNullString
ElseIf Target.Value = "lead" Then
Target.Offset(0, 8).Value = Date()
End If
End If
Application.EnableEvents = True
End Sub
A good practice is to use variable names that match up with what those values represent. It makes the code easier to read and maintain later on.

Related

How can I build For-Next-Loop in Change Event?

I've got a sheet with Data.
I want to calculate the difference between date now and the date which are in cells C3:C10. And the results are stored in cells D3:D10.
That part I got it so far.
But if someone manipulates the values in the result cells then the VBA should recalculate those cells and correct the results.
Private Sub Worksheet_Change(ByVal Target As Range)
For Zeile = 3 To 10
Sheets("Tabelle2").Cells(Zeile, "D") = WorksheetFunction.YearFrac(Sheets("Tabelle2").Cells(Zeile, "C"), Date)
If Sheets("Tabelle2").Cells(Zeile, "C") = 0 Then
Sheets("Tabelle2").Cells(Zeile, "D") = ""
End If
Next Zeile
End Sub
The first thing to do is check if the change has been made in C3:C10, you can use Intersect for that.
Then you should disable events to stop the code triggering itself, use Application.EnableEvents = False for that.
Next loop through Target in case more than one cell has been changed and perform the required actions/calculations.
Finally re-enable events using Application.EnableEvents = True
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rng As Range
Dim cell As Range
Dim Zeile As Long
Set rng = Intersect(Target, Range("C3:C10"))
If Not rng Is Nothing Then
Application.EnableEvents = False
For Each cell In rng.Cells
Zeile = cell.Row
If Cells(Zeile, "C") <> 0 Then
Cells(Zeile, "D") = Application.YearFrac(Cells(Zeile, "C").Value, Date)
Else
Cells(Zeile, "D") = ""
End If
Next cell
Application.EnableEvents = True
End If
End Sub
If you want the code to be triggered if a value is changed in either C3:C10 or D3:D10 change this,
Set rng = Intersect(Target, Range("C3:C10"))
to this.
Set rng = Intersect(Target, Range("C3:D10"))
You can also change the range address there if you want to further rows by changing 10.

trying to add 2 events that happen in the same column in a worksheet

Good day
I am trying to make both of the macros work in the same range since I want the drop down it that is created there to be able to check if there is a value in the column next to it get that rows values and also still be able to run the first macro. see picture attached since I don't think I am explaining properly what I want.
So in the picture in column A is set number. Column B has the dropdown where the first macro was implemented which enables it to be able to get values from column A and also be able to add a value to show 2+3 , I need it then to be able to get the values of stream C and actually add them in column C
attached is my current code and a picture of a example that doesn't necessarily work with the code just an example show what I mean.
Private Sub Worksheet_Change(ByVal Target As Range)
Dim Oldvalue As String
Dim Newvalue As String
Dim nommer As Integer
Dim finder As Range
On Error GoTo Exitsub
If Target.Column = 3 Then <------- here is macro 1
If Target.SpecialCells(xlCellTypeAllValidation) Is Nothing Then
GoTo Exitsub
Else: If Target.Value = "" Then GoTo Exitsub Else
Application.EnableEvents = False
Newvalue = Target.Value
Application.Undo
Oldvalue = Target.Value
If Oldvalue = "" Then
Target.Value = Newvalue
Else
Target.Value = Oldvalue & "+ " & Newvalue
End If
End If
End If
If Not Intersect(Target, Range("J9")) Is Nothing Then
Select Case Range("J9")
Case "A": toets_my_ws
End Select
End If
Application.EnableEvents = True
Exitsub:
Application.EnableEvents = True
If Target.Column = "3" Then <------- here is macro 2
nommer = ActiveCell.Value
Else: If Target.Value = "" Then GoTo Exitsub Else
Set finder = Range("B9:B40").Find(what:=ActiveCell.Value,LookIn:=xlValues, lookat:=xlWhole)
ActiveCell.Offset(0, 3).Value = finder.Offset(0, 4).Value
ActiveCell.Offset(0, 5).Value = finder.Offset(0, 6).Value
End If
End Sub
]1
You can only have one event of this type as it works for the entire worksheet. So if you have multiple criteria for what happens when the worksheet changes you need to include all of that logic in the event. I have combined your logic in to one big thing but there is no way for me to tell if this was done correctly. You never use the variable nommer so that does nothing. It's also not clear what toets_my_ws is.
I would advise against using Worksheet change events as is slows down the worksheet considerably.
The better way to tackle this would be to use User Defined Functions (UDF). This way you can embed VBA in to a single cell without bogging down the whole sheet with logic every time you press a key.
HERE IS THE COMBINED LOGIC:
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
Dim Oldvalue As String
Dim Newvalue As String
Dim nommer As Integer
Dim finder As Range
If Target.Value = vbNullString Then GoTo Exitsub
On Error GoTo Exitsub
If Target.Column = 3 Then
nommer = ActiveCell.Value
If Target.SpecialCells(xlCellTypeAllValidation) Is Nothing Then
GoTo Exitsub
Else
Application.EnableEvents = False
Newvalue = Target.Value
Application.Undo
Oldvalue = Target.Value
If Oldvalue = "" Then
Target.Value = Newvalue
Else
Target.Value = Oldvalue & "+ " & Newvalue
End If
End If
Else
Set finder = Range("B9:B40").Find(what:=ActiveCell.Value, LookIn:=xlValues, lookat:=xlWhole)
ActiveCell.Offset(0, 3).Value = finder.Offset(0, 4).Value
ActiveCell.Offset(0, 5).Value = finder.Offset(0, 6).Value
End If
If Not Intersect(Target, Range("J9")) Is Nothing Then
Select Case Range("J9")
Case "A"
toets_my_ws
End Select
End If
Exitsub:
Application.EnableEvents = True
End Sub
HERE IS INFORMATION ABOUT UDFS:
https://excelchamps.com/excel-user-defined-function/

Maintain count of each change to cell value

Suppose I have a value in cell A1 and everytime the cell value of A1 changes, the cell on b1 counts the change.
I have a code it works just with A1(value)cell and b1(count on change) cell. i would like to apply this function on cell E2:E709 (value) cells and F2:F709 (count on change) cell.
Private Sub Worksheet_Change(ByVal Target As Range)
Static OldVal As Variant
If Target.Address(False, False) = "A1" Then
Application.EnableEvents = False
If Target.Value <> OldVal Then
Target.Offset(, 1).Value = Target.Offset(, 1).Value + 1
OldVal = Target.Value
End If
Application.EnableEvents = True
End If
End Sub
Try code below:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
' If change was outside range we are interested in, then quit Sub
If Intersect(Target, Range("E2:E709")) Is Nothing Then Exit Sub
' store reference to cell in F column for simplicity
Dim c As Range: Set c = Target.Offset(0, 1)
' check if cell in F column has any value, if not, then assign it 1
If c.Value = "" Then
c.Value = 1
' else increment it by one
Else
c.Value = c.Value + 1
End If
End Sub
Consider:
Private Sub Worksheet_Change(ByVal Target As Range)
Static OldVal(2 To 709) As Variant
Dim E As Range, F As Range, r As Range, Intersekt As Range
Dim rw As Long
Set E = Range("E2:E709")
Set F = Range("F2:F709")
Set Intersekt = Intersect(E, Target)
If Intersekt Is Nothing Then Exit Sub
Application.EnableEvents = False
For Each r In Intersekt
rw = r.Row
If r.Value <> OldVal(rw) Then
r.Offset(0, 1).Value = r.Offset(0, 1).Value + 1
OldVal(rw) = r.Value
End If
Next r
Application.EnableEvents = True
End Sub
We use an array for OldVal rather than a single item.We use a (potentially) multi-cell IntersektRange` to facilitate changing more than one cell at a time.

Change cell color base on another cells data but keep it that way if data changes again

I have been looking for days to solve this and have only come up with half the solution.
What I can do:
I would simply like to have one cell turn green inside with an x inserted when another cells data has the word "Complete" inside it.
What I cannot do:
I would like that same cell that turned green with an x inserted into it when the word "Complete" is changed to "Rework" to stay green with an x.
So Cell A1 is blank then in cell B1 the word "Complete" is added. Then cell A1 changes to green and has an x inside it. If later B1 changes to "Rework" I would like A1 to stay green with the x inside. So I can know that at one time the status of B1 was at one time "Complete"
I have been trying Conditional Formatting with rules but cannot get it to stay. I think the "Stop If True" check box within would be part of the solution but not sure what the code would be.
I already have a different macro running on this sheet so if the answer is a macro I will need it to be added to it. Below is the macro in the sheet already. Thank you.
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("N:N,Y:Y"), Range("10:" & Rows.Count)) Is Nothing Then
If Target.Count < Columns.Count Then
On Error GoTo bm_Safe_Exit
Application.EnableEvents = False
Dim r As Range
For Each r In Intersect(Target, Range("N:N,Y:Y"), Range("10:" & Rows.Count))
With r.Offset(0, 1)
.Value = Now 'use Now to retain the time as well as the date
.NumberFormat = "mm/dd/yy" 'change to what you prefer
End With
Next r
End If
End If
bm_Safe_Exit:
Application.EnableEvents = True
End Sub
Ideally you'd split this up into separate subs to handle each of the change types, but this should give you an idea:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rng As Range, r as Range
'skip full-row changes (row insert/delete?)
If Target.Columns.Count = Columns.Count Then Exit Sub
Set rng = Intersect(Target, Range("N:N,Y:Y"), Range("10:" & Rows.Count))
If Not rng Is Nothing Then
On Error GoTo bm_Safe_Exit
Application.EnableEvents = False
For Each r In rng.Cells
With r.Offset(0, 1)
.Value = Now 'use Now to retain the time as well as the date
.NumberFormat = "mm/dd/yy" 'change to what you prefer
End With
Next r
End If
Set rng = Intersect(Target, Range("B:B"), Range("10:" & Rows.Count))
If Not rng Is Nothing Then
On Error GoTo bm_Safe_Exit
Application.EnableEvents = False
For Each r In rng.Cells
If r.Value = "Complete" Then
With r.Offset(0, -1)
.Value = "x"
.Interior.Color = vbGreen
End With '<<EDIT thanks #BruceWayne
End If
Next r
End If
bm_Safe_Exit:
Application.EnableEvents = True
End Sub
You'll need two worksheet events, and some If statements. The following should help you get started, unless I'm overlooking something.
Dim oldVal as String ' Public variable
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
'Debug.Print Target.Address
If Target.Cells.Count <> 1 Then Exit Sub
oldVal = Target.Value
End Sub
The above will make note of the oldValue.
Private Sub Worksheet_Change(ByVal Target As Range)
Dim newVal As String
newVal = Target.Value
If newVal = oldVal Then
Debug.Print "Same Values"
ElseIf oldVal = "Complete" And newVal = "Rework" Then
Debug.Print "Stay green with X"
ElseIf oldVal = "" And (newVal = "Complete" Or newVal = "complete") Then
Debug.Print "Change cell to Green, add an 'X'"
Target.Interior.ColorIndex = 10
Target.Value = Target.Value & " x"
End If
End Sub
Then, add/tweak those If statements as necessary, and add the color changing/reverting code to the appropriate block.
(There may of course be a better mousetrap, but I think this should get you going).

Macro basic multiplication calculation

I am trying to take the value inside cell I-8 Multiplied by the value inside H-8 and have this new value X, replace the contents of cell I-8.
I am trying to do this with every row starting with 8. (I-9 * H-9 etc)
I am already removing cell's will qty 0 inside column I with the following:
Private Sub Workbook_BeforePrint(Cancel As Boolean)
Dim c As Range
Dim SrchRng
Set SrchRng = Intersect(ActiveSheet.UsedRange, Range("I:I"))
Do
Set c = SrchRng.Find(0, LookIn:=xlValues)
If Not c Is Nothing Then c.EntireRow.Delete
Loop While Not c Is Nothing
End Sub
You actually need a different event for this to work:
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Column = 9 And Target.Cells.Count = 1 And Target.Row > 7 Then
Application.EnableEvents = False
If IsNumeric(Target.Value) Then Cells(Target.Row, 2).Value = Target.Value * Target.Offset(0, -1).Value
Application.EnableEvents = True
End If
End Sub
I feel it needs to pointed out however, that simply entering =H8*I8 into B8 will do the same thing.

Resources