Capture the final change in a Worksheet Change Event - vba

In the worksheet(1), I have multiple plan-to-change cells, A1 and other cells, and others cells value depends on the the value of A1 as I set up the function(eg.,B1=A1+1). In this way, if I change the value of A1, other cells would change value as well, and the total changed cells would be the A1 plus others changed cells. However, as the quantity of "other cells" is quite large, when I run the following function in VBA:
Private Sub Worksheet_Change(ByVal Target As Range)
Cells(Target.Cells.Count, 21) = "ok"
End Sub
The column 21 would appear 3 "ok"s as I suppose because the large cells change happen in order, which actives three times the Worksheet_Change function. (the first ok for I change A1, the second for the time delay of updated function, the third for the final cell) However, I only want the Worksheet_Change to capture the final cells change after I change the value of A1, what should I do to avoid the previous capturing of Worksheet_Change function?

Modify your code to run only when A1 is changed.
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Address = Range("A1").Address Then
Cells(1, 21).Value = "ok"
End If
End Sub

Target is often more than a single cell. Determine if one of the changed cells is A1 with Intersect and then only deal with A1.
If you plan to change a value on the same worksheet, shut down event handling temporarily so the Worksheet_Change does not try to run on top of itself.
Private Sub Worksheet_Change(ByVal Target As Range)
if not intersect(target, range("a1")) is nothing then
on error goto safe_exit
application.enableevents = false
dim trgt as range
for each trgt in intersect(target, range("a1"))
Cells(trgt.row, 21) = "ok"
next trgt
end if
safe_exit:
application.enableevents = true
End Sub
Since I don't believe that a1 is the actual target of your Target in your real world, I've made the code a little more verbose than it probably needs to be.

Related

BeforeDoubleClick code placing data based on where cursor currently exists

I may or may not have an easy one here... For some reason I can't properly think of the solution.
Spreadsheet has numbers 1 to 14 in column A. 14 ends on row 15 as there is a header in cell A1. What I'm trying to do is double click one of these numbers and transfer that number to a specific cell on the same sheet (named "Sheet1"). I was able to put the code together to make it work for transferring a chosen number to a specific cell. Code below works well. However, I don't want to add a bunch of areas to double click for different cells. For example: The destination cell for the chosen data is E6, H6, and G6. I'd like to place the cursor starting in E6, double click a number in range A2 to A15, and have that number that I choose between A2 and A15 to appear in E6 since that is where the cursor was when I double clicked a cell in A2 to A15. Then I would move the cursor by click H6 and then going back to same selection between A2 to A15, placing whatever number I choose in that range in H6 since that is where the cursor currently exists.
Hopefully this makes sense and is even possible.
example screenshot
Working code that allows the double click to put data in a certain cell
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
If Not Intersect(Target, Range("A2:A15")) Is Nothing Then
Cancel = True
Target.Copy Destination:=Cells(6, "E")
End If
End Sub
I've added the solution I would try, with comments in the code. The reason for two sheet variables, is because the first click in the double-click registers as a SelectionChange event. So to get the correct cell location, you need it from two selections back, instead of just one.
Public selectedCell As String 'Sheet Variable
Public lastCell As String
' This updates the Sheet variable with the most recent selection
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
selectedCell = lastCell
lastCell = Target.Address
End Sub
' Added a check for having a previously selected cell
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
If Not Intersect(Target, Range("A2:A15")) Is Nothing Then
If selectedCell = vbNullString Then
Cancel = True
MsgBox "Please select a destination cell for the data."
selectedCell = vbNullString
lastCell = vbNullString 'Prevents overwriting same cell by accident
Else
Cancel = True
Target.Copy Destination:=Range(selectedCell)
selectedCell = vbNullString
lastCell = vbNullString 'Prevents overwriting same cell by accident
End If
End If
End Sub
Here is a tiny tool that you may be able to adapt to your needs. If you double-click on an empty cell, it becomes the FinalDestination. If you then double-click on another cell that is not empty, its contents will be copied to FinalDestination:
In the worksheet code area:
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, Cancel As Boolean)
Cancel = True
Application.EnableEvents = False
If Target.Value = "" Then
Set FinalDestination = Target
Else
Target.Copy FinalDestination
End If
Application.EnableEvents = True
End Sub
In a standard module:
Public FinalDestination As Range
NOTE:
In this simple demo code there are no restrictions on source/destination.

VBA is not updating automatically after input

I have created a Excel workbook for my work where I collect information. In this workbook I have a sheet where details in various currencies can be filled in. Based on the selection by the preparer the value in cell B5 will change to either USD or LC. In case the value in cell B5 will be USD, columns C and E should be hidden. The issue in here is that this code will not immediately unhide the columns. After clicking on a random cell, the columns are hidden. Please let me know if there is a solution for this issue whereby the columns are hidden without clicking on a random cell each time. Thank you.
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
If Range("B5").Value = "USD" Then
Union(Columns("C"), Columns("E")).EntireColumn.Hidden = True
ElseIf Range("B5").Value = "LC" Then
Union(Columns("C"), Columns("E")).EntireColumn.Hidden = False
End If
End Sub
Thank you all for your comments. Let me further elaborate on my issue. In principle my VBA code works. The only thing is that after cell B5 is changed to USD, initially nothing happens. After I click on a random cell in this sheet, the VBA code works and hide the columns. The same is applicable in case the value in cell B5 is LC. Then again the VBA code does not work immediately. After clicking on a random cell in the sheet the columns are unhided.
A small update with respect to cell B5. So cell B5 contains a formula that is linked to listed cell in another sheet. After a value is selected from the list in another sheet, cell B5 will determine through the IF functions if the value in B5 will be LC or USD.
I am now afraid that after the preparer select in the listed cell a value, it will not click on a random cell in designated sheet resulting that he or she will see the wrong information.
Please let me know if you require further information. Thank you.
PS. I am not very strong in creating VBA codes.
You're using the wrong event.
SelectionChange fires when you select a different cell, etc.
Change fires immediately after a cell's contents change.
Private Sub Worksheet_Change(ByVal Target As Range)
If Range("B5").Value = "USD" Then
Union(Columns("C"), Columns("E")).EntireColumn.Hidden = True
ElseIf Range("B5").Value = "LC" Then
Union(Columns("C"), Columns("E")).EntireColumn.Hidden = False
End If
End Sub
Alternate Solution:
Private Sub Worksheet_Change(ByVal Target As Range)
If Target = Range("B5") Then Union(Columns("C"), Columns("E")).EntireColumn.Hidden = (Target.Value = "USD")
End Sub
(adapted from #Vityata's comment below)
More Information:
MSDN : Worksheet.SelectionChange Event (Excel)
MSDN : Worksheet.Change Event (Excel)
A few changes to your code:
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Address = "$B$5" Then
If Range("B5").Value = "USD" Then
Union(Columns("C"), Columns("E")).EntireColumn.Hidden = True
Else
Union(Columns("C"), Columns("E")).EntireColumn.Hidden = False
End If
Application.Calculate
End If
End Sub
1: the right event is Change (when a value changes in the sheet), not SelectionChange (when the selection changes).
2: Your code works on my side, so I guess it's not working on your side because you have some calculation off. I've added the line Application.Calculate which will refresh the spreadsheet even if your calculations are set to manual.
3: I've added an If Target.Address condition to check only when the cell that changed is the right one.
4: I've added just Else instead of ElseIf cell = "LC", because it's faster and because I think it's cleaner (if the user removes completely the value of the currency and before it was USD, nobody will unhide the columns C and E).
Keep it simple,
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim rng As Range
Set rng = Range("B5")
If rng.Value = "USD" Then Columns("C:E").EntireColumn.Hidden = True
If rng.Value = "LC" Then Columns("C:E").EntireColumn.Hidden = False
End Sub

Excel Macro to update Cells automatically based on User selection

I am trying to write a Macro that updates 4 Cell if the User select "Mailing" From Cell A1. If the User selects "Mailing" in A1, then Automatically update A2,A3,A4, and A5 to Value in B1. If the User selects something other than "Mailing", Then all four cells should be blank and the user should be able to type in any value. Any help is appreciated. Thanks
I have gotten this far, but VBA is not my thing:
Sub test()
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Address = "$A$1" And Target.Value = "Mailing" Then
Range("A2:A4").Value = "B1"
End If
End Sub
As the others have mentioned, you just need to put it to Sub Worksheet_Change. Note that if you are "typing" the word into cell A1, you will actually be in A2 after the "Enter".
Private Sub Worksheet_Change(ByVal Target As Range)
If Range("A1").Value = "Mailing" Then
Range("A2:A4").Value = "B1"
End If
End Sub
The problem is you are trying to change the value of some of the cells in your code, so the code should run itself. You need to turn off events before changing the cell values and then turn it back on:
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Address = "$A$1" And Target.Value = "Mailing" Then
Application.EnableEvents = False
Range("A2:A4").Value = "B1"
Application.EnableEvents = True
End If
End Sub

excel filtering off multiple references?

How can I set up vba code to allow user to type in several selections and then the accompanying list filters based on that input, reference cell? I tried this but its only filtering the first selection.
I set it up as a worksheet script, so it only runs on that one sheet and set it up to only run when the user input cells (A1, A2) are updated.
I then tried getting the lists in columns C:D to filter, based on the values in A1 and A2.
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
'This line stops the worksheet updating on every change, it only updates when cell is touched
If Intersect(Target, Range("A1:B5")) Is Nothing Then Exit Sub
With Sheets("TestTab")
.Range("C1:D100").AutoFilter Field:=1, Criteria1:=.Range("A1").Value, Field:=2, Criteria1:=.Range("A2").Value
End With
End Sub
If you are trying to filter a single column with what is in either cell A1 or A2:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
'This line stops the worksheet updating on every change, it only updates when cell is touched
If Intersect(Target, Range("A1:A2")) Is Nothing Then Exit Sub
With Sheets("TestTab")
.Range("C3:D100").AutoFilter Field:=1, Criteria1:=.Range("A1").Value, Operator:=xlOr, Criteria2:=.Range("A2").Value
End With
End Sub
If you are trying to filter column C on A1 and column D on A2:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
'This line stops the worksheet updating on every change, it only updates when cell is touched
If Intersect(Target, Range("A1:A2")) Is Nothing Then Exit Sub
With Sheets("Sheet1")
.Range("C1:D100").AutoFilter Field:=1, Criteria1:=.Range("A1").Value
.Range("C1:D100").AutoFilter Field:=2, Criteria1:=.Range("A2").Value
End With
End Sub

excel vba on cell change error

Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Range = Range("A1") Then
If Range("A1").Value <> Range("A2").Value Then
Range("C1").Value = Range("C1").Value + 1
Range("A2").Value = Range("A1").Value
End If
End If
End Sub
that's the code however when i copy paste a set off cell, let's say 2 columns and 3 rows
it produce runtime error 13 type mismatch on line
If Target.Range = Range("A1") Then
why?
i simply wants the vba to do something everytime cell A1 changes
the value of A1 itself is an excel sum formula
You get type-missmatch error, becase you're trying to compare range (containing many cells) with single cell. If you want to do something every time cell A1 changed, use this one instead:
Private Sub Worksheet_Change(ByVal Target As Range)
Application.EnableEvents = False
On Error GoTo ErrHandler
If Not Intersect(Target, Range("A1")) Is Nothing Then
If Range("A1").Value <> Range("A2").Value Then
Range("C1").Value = Range("C1").Value + 1
Range("A2").Value = Range("A1").Value
End If
End If
ExitHere:
Application.EnableEvents = True
Exit Sub
ErrHandler:
Resume ExitHere
End Sub
also note that I'm using Application.EnableEvents = False - it's a good habbit for Worksheet_Change event to use it. It prevents code from infinity firing itself each time you change any cell in event handler code.
UPD:
Btw, the value of A1 itself is an excel sum formula - you can't track changes of formula using above approach. I covered in details how you can do it in this question: Using Worksheet_Calculate event for tracking changes of formula
Simoco's answer should work for you. Another way (the one I usually use, though only out of habit) is to compare the addresses:
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Address = Range("A1").Address Then
If Range("A1").Value <> Range("A2").Value Then
Range("C1").Value = Range("C1").Value + 1
Range("A2").Value = Range("A1").Value
End If
End If
End Sub
You are getting an error because Target.Range is not defined. You should either just refer to Target (a Range Object) or Target.Address (the address of the Range Object). Secondly, depending on the context, Range("A1") refers to either the cell A1 itself (a Range Object) or the value in cell A1 (a literal value). You need to carefully think what you want to compare to what.
If, as you said, you want the comparison done whenever the value in Range("A1") changes then you should follow Simoco's suggestion.