VBA beginner here.
I have project where I have specified input cells for the user. When one of these input cells is changed, it needs to run a few lines of code that are specific to only that one cell. If the user clears the contents of the cell, I want the code to replace the blank cell with the value "0".
The code below simulates what I am trying to achieve. It is written in the same form as my project but is more succinct.
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Column = 1 And Target.Row = 1 Then
Range("B1").Value = "Changed 1" 'Just something specific to this cell. Not important
If IsEmpty(Sheet1.Range("A1")) Then Sheet1.Range("A1").Value = 0
End If
If Target.Column = 1 And Target.Row = 2 Then
Range("B2").Value = "Changed 2" 'Just something specific to this cell. Not important
If IsEmpty(Sheet1.Range("A2")) Then Sheet1.Range("A2").Value = 0
End If
If Target.Column = 1 And Target.Row = 3 Then
Range("B3").Value = "Changed 3" 'Just something specific to this cell. Not important
If IsEmpty(Sheet1.Range("A3")) Then Sheet1.Range("A3").Value = 0
End If
End Sub
Everything above works fine when the changes are performed on single cells. If the user selects all the cells and presses the delete key, it only runs the code for the first cell. I want it to run for all the selected (deleted) cells.
Any advice on how to simultaneously run the Worksheet_Change across multiple cells?
Thanks
When you have a change event that works just fine on a single cell, you can make a few adjustments to ensure that it also works when you change a range of cells in one go, like when you paste three cells into A1 to A3
You may want to apply an approach similar to this:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim ws As Worksheet
Dim cel As Range
Dim myRow As Long
Set ws = ThisWorkbook.Sheets("Sheet1")
If Not Intersect(Target, Range("A1:A3")) Is Nothing Then ' watch all the cells in this range
For Each cel In Target ' do the next steps for each cell that was changed
myRow = cel.Row
Range("B" & myRow).Value = "Changed " & Target.Row 'Just something specific to this cell. Not important
Application.EnableEvents = False
If IsEmpty(ws.Range("A" & myRow)) Then Sheet1.Range("A" & myRow).Value = 0
Application.EnableEvents = True
Next cel
End If
End Sub
Explanation:
If Not Intersect(Target, Range("A1:A3")) Is Nothing Then -- only act on changes to cells A1 to A3
For Each cel In Target - do the same thing for all cells that have been changed
Range("B" & myRow).Value = "Changed " & Target.Row - enter some value into column B of the current row
In the next step of the macro we will possibly enter some data into the cells we are monitoring for a change, i.e. A1 to A3. A change in these cells will trigger this macro. This macro will write into the cells. A change in the cells will trigger this macro ....
You see where this is going. In order to avoid an endless loop, we turn off any event triggered macros, e.g. macros that fire when a cell is changed. So we turn off event monitoring with this statement.
Application.EnableEvents = False - Now any events like "a cell has been changed" will be ignored.
We can now write a value into column A and that will not trigger the macro again. Great. We do whatever we need to do to cells A1 to A3 and then we turn event monitoring back on.
Application.EnableEvents = True
Then we go to the next cell (if any) in the range that triggered this macro.
Let me know if that helps or if you need a bit more detail. These things take a little learning curve.
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Application.Intersect(Target, Cells(1, 1)) Is Nothing Then
Range("B1").Value = "Changed 1" 'Just something specific to this cell. Not important
If IsEmpty(Sheet1.Range("A1")) Then Sheet1.Range("A1").Value = 0
End If
If Not Application.Intersect(Target, Cells(1, 2)) Is Nothing Then
Range("B2").Value = "Changed 2" 'Just something specific to this cell. Not important
If IsEmpty(Sheet1.Range("A2")) Then Sheet1.Range("A2").Value = 0
End If
If Not Application.Intersect(Target, Cells(1, 3)) Then
Range("B3").Value = "Changed 3" 'Just something specific to this cell. Not important
If IsEmpty(Sheet1.Range("A3")) Then Sheet1.Range("A3").Value = 0
End If
End Sub
Related
This is a simple answer but one I cannot find.
I have two columns of data.
Column A (1) has yes/no data validation list options in every cell.
Column B also has data validation with say 6 strings of text options in every cell.
But I only want each the corresponding cell (column B) to update in the same row as column A
e.g A20 toggled, then B20 is updated. Like so
A20 is selected “Yes” from the dropdown option and B20 is updated with the string “complete” which is one of the states you can select in the dropdown boxes manually in every cell in column B.
I had some code but I would have to write an argument for every cell and then two macros for every yes / no.
This is code that works for one cell only but this is not ideal for many cells but it works
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("A20")) Is Nothing Then
Select Case Range("A20")
Case "Yes": Macro_001
Case "No": Macro_002
End Select
End If
End Sub
Sub Macro_001()
Application.Calculation = xlManual
Application.ScreenUpdating = False
Sheets("August 2020").Select
Sheets("August 2020").Range("B20").Select
ActiveCell.FormulaR1C1 = "Complete"
Application.ScreenUpdating = True
Application.Calculation = xlAutomatic
End Sub
Sub Macro_002()
Application.Calculation = xlManual
Application.ScreenUpdating = False
Sheets("August 2020").Select
Sheets("August 2020").Range("B20").Select
ActiveCell.FormulaR1C1 = ""
Application.ScreenUpdating = True
Application.Calculation = xlAutomatic
End Sub
There much be an easier way with .range perhaps
Thanks in advance
In the developer tab click view code, choose the sheet you want the macro to run on, make sure the upper left drop down says worksheet and the upper right says Change (I'll assume your sheet has headers):
Private Sub Worksheet_Change(ByVal Target As Range)
Dim KeyCells As Range
Dim ChangeCell As Range
Dim numrows As Long
numrows = Cells(Rows.Count, 1).End(xlUp).Row
Set TriggerCells = Range("A1")
Set KeyCells = Range("B2:B" & numrows)
If Application.Intersect(TriggerCells, Range("A1")) = "[Column Header Text]" Then
If Not Application.Intersect(KeyCells, Range(Target.Address)) Is Nothing Then
If Target.Value = "Yes" Then
Range("B" & Target.Row).Value = "Completed"
End If
End If
End If
End Sub
Try that, see if it works for you.
I am trying to track data in a laboratory.
Goal. When the value in cell P3 changes to "yes", then cells Q3:AE3 are locked. However, if "yes" does not appear in cell P3, then cells Q3:AE3 are unlocked.
I need to loop through column P locking/unlocking cells with respect to the row each P value is located.
For example if P36 = "Yes", the Q36:AE36 would become locked.
Edit: This code works with line P3. How can I make this loop through P3:P500?
Private Sub Worksheet_Change(ByVal Target As Range)
If Range("P3") = "Yes" Then
Range("Q3:AE3").Locked = True
ElseIf Range("P3") = "No" Then
Range("Q3:AE3").Locked = False
ElseIf Range("P3") = "" Then
Range("Q3:AE3").Locked = False
End If
End Sub
This will handle any number of values being changed in column P. Provide your password as needed.
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
'did any cells in column P change
If Not Intersect(Target, Me.Range("P1").EntireColumn) Is Nothing Then
Me.Unprotect "password"
Dim cell As Range
For Each cell In Intersect(Target, Me.Range("P1").EntireColumn)
Me.Range("Q" & cell.Row & ":AE" & cell.Row).Locked = UCase$(cell.Value) = "YES"
Next
Me.Protect "password"
End If
End Sub
Use Case: For each cell in Column A (RangeK5:K700) that has the specific text ("Week 1" in this example) in cell B2, the row in the table that contains that specific text will be hidden.
I have code that works, but it takes a long time since the table I am currently 'filtering' is large and will get more rows as time goes on. I'm wondering if there is a way to speed up the code. I'm not much of an object oriented expert so I do tend to code the 'long' way. I think (my terminology will be incorrect so excuse me) that if I could somehow keep track of all of the rows to hide and then hide them at the end instead of at each loop, it would be faster. Is that possible?
I 'figured' this out by google searches & prayer so if there are any changes that can be made I'd appreciate hearing them. Thanks so much in advance!!
Note: B2 can have up to 13 different options (Week 1 --> Week 13 etc). I'll only show the code if B2 is "Week 1"
Sub CycleThroughWorksheet()
Set Target = Range("B2")
If Target.Value = "Week 1" Then
Call HideWeek2
Call HideWeek3
Call HideWeek4
Call HideWeek5
Call HideWeek6
Call HideWeek7
Call HideWeek8
Call HideWeek9
Call HideWeek10
Call HideWeek11
Call HideWeek12
Call HideWeek13
End If
End Sub
------
Sub HideWeek1()
For Each Cell In Worksheets("Gantt Table").Range("K5:K700").Cells
If Cell.Text = "Week 1" Then
Cell.EntireRow.Hidden = True
End If
Next
End Sub
-------
Sub RunWeekView()
Call PreventScreenFlicker
Rows.EntireRow.Hidden = False
Call CycleThroughWorksheet
End Sub
You can batch the hide operation like this:
Sub HideWeek(WeekNum As Long)
Dim rng As Range
For Each Cell In Worksheets("Gantt Table").Range("K5:K700").Cells
If Cell.Text = "Week " & WeekNum Then
If rng is nothing then
Set rng = Cell
Else
Set rng = Application.Union(rng, Cell)
End If
End If
Next
If not rng is nothing then rng.entirerow.hidden = true
End Sub
Call with (eg)
HideWeek 1
HideWeek 2
etc.
Depending on what the rest of your code looks like there are likely other changes you could make to reduce the size of your code. If you're typically hiding most of the rows then it might be better to first hide everything, then only show the rows you want.
You can use a Worksheet_Change event to AutoFilter your data based on the value in Range("B2"). Right click your worksheet tab and select View Code, copy and place this code in your worksheet object code window in the Visual Basic for Applications window(the DEVELOPER tab must be enabled). When you change the Week # in the target cell(B2), the code will hide the rows that contain the target Week # in Column K, and show the other Weeks, by using "<>", in the Criteria1.
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("B2")) Is Nothing Then
With Range("K4", Range("K" & Rows.Count).End(xlUp))
.AutoFilter 1, Criteria1:="<>" & Target.Value
On Error Resume Next
End With
End If
End Sub
If you actually want to only show the Week #, you can change to code by removing the "<>"...
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("B2")) Is Nothing Then
With Range("K4", Range("K" & Rows.Count).End(xlUp))
.AutoFilter 1, Criteria1:= Target.Value
On Error Resume Next
End With
End If
End Sub
I need to create an Excel document with updating totals in the A column, based on numbers entered in the B column. Each respective cell in row A should update based on its equivalent B cell value whenever a new value is added, and then the value entered into B is cleared once added to A.
I have gotten things working for one single row but don't have knowledge or understanding on how to best make this work for EACH cell pair in the entire column. I really don't want to copy and paste this 10,000 times and update the cells to reference the correct pair. Code for single cell:
Private bIgnoreEvent As Boolean
Private Sub Worksheet_Change(ByVal Target As Range)
If bIgnoreEvent Then Exit Sub
bIgnoreEvent = True
Cells(1, 2) = Cells(1, 2) + Cells(1, 1)
Cells(1, 1) = ""
bIgnoreEvent = False
End Sub
I am hoping this can be achieved with a loop function, or a range of some sort.
This should work:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rng As Range, c As Range
'any updates in ColB?
Set rng = Application.Intersect(Target, Me.Columns(2))
If Not rng Is Nothing Then
Application.EnableEvents = False '<< prevent re-triggering of event
'Process each updated cell
For Each c In rng
If IsNumeric(c.Value) Then
With c.Offset(0, -1)
.Value = .Value + c.Value
End With
c.ClearContents
End If
Next c
Application.EnableEvents = True '<< re-enable event
End If
End Sub
I have rows from 1-100.
I know how to target specific cells and get data from them, but how would I do this when any row from 1 to 100 can be changed?
Say you put anything into Row A3. How would you write "Updated" into row B3 via VBA?
I want this to apply to rows A1-A100.
Thanks
Place the following event macro in the worksheet code area:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim A As Range, Intersection As Range, Cell As Range
Set A = Range("A1:A100")
Set Intersection = Intersect(Target, A)
If Intersection Is Nothing Then Exit Sub
Application.EnableEvents = False
For Each Cell In Intersection
Cell.Offset(0, 1).Value = "Updated"
Next Cell
Application.EnableEvents = True
End Sub
Open VBA Editor
Double click on the sheet you event take action (sheets appears in the left top box)
Select Worksheet on the left box above code box
Select change on the right box above code box
Paste the code
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
With ThisWorkbook.Worksheets("Sheet1")
If Not Intersect(Target, .Range("A1:A100")) Is Nothing Then
Application.EnableEvents = False
.Range("B" & Target.Row).Value = "Updated"
Application.EnableEvents = True
End If
End With
End Sub