I am trying to automatically hide/unhide rows on sheet2 when cell c9 changes on sheet1.
I have my Hide/Unhide Rows toggle all set up.
My worksheet change works when the target cell is on sheet2 but does not when I attempt to set the target cell to sheet1 (As is shown in my code below).
Module1 Code:
Sub Hide_Rows_Toggle()
Dim r As Range
For Each r In Columns(2).Cells
If r.Value = "X" Then
r.EntireRow.Hidden = True
End If
Next r
End Sub
Sheet2 Code:
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Address = Worksheets("Sheet1").Range("$C$9") Then
Call Hide_Rows_Toggle
End If
End Sub
In ThisWorkbook:
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
If Sh.Name = "Sheet1" And Target.Address = "$C$9" Then
Call Hide_Rows_Toggle
End If
End Sub
Now I'm home, and have tested the following;
You haven't qualified a Workbook or Worksheet object in your Hide_Rows_Toggle() sub. As such it is implicitly referencing the ActiveWorksheet
Per Worksheet.Columns documentation:
Using the Columns property without an object qualifier is equivalent to using ActiveSheet.Columns. If the active document isn't a worksheet, the Columns property fails.
The Worksheet_Change event is Worksheet specific, meaning, the event is triggered on each worksheet independantly - If you make a change on Sheet1 the event won't trigger on Sheet2 etc.
You should do the following:
Move your Worksheet_Change event to the module for Sheet1, where the change is happening.
Qualify your target worksheet (at least) in your Hide_Rows_Toggle Subroutine like so:
Qualify your range in your sub like so:
Sub Hide_Rows_Toggle()
Dim r As Range
For Each r In ThisWorkbook.Sheets("Sheet2").Columns(2).Cells
If r.Value = "X" Then
r.EntireRow.Hidden = True
End If
Next r
End Sub
This ensures the rows will only be hidden on Sheet2, otherwise it will always target the ActiveSheet which has to be the sheet you made the change in.
Lastly, It's a little unclear exactly what you are attempting to evaluate in your Worksheet_Change event.
Currently you are looking to see if the Target.Address is equal to the Value in Cell $C$9 on Sheet1. This is because the default member for the Range object is Value. So it will only return true if you are setting a cell reference in $C$9 to dictate which cell triggers the sub.
If you are intending to run Hide_Rows_Toggle when the value in $C$9 is changed, you will need to add the .Address property to your range - If Target.Address = Worksheets("Sheet1").Range("$C$9").Address - or - simply change it to a string to match the address like If Target.Address = "$C$9" Then.
Related
I am trying to use the Workbook_SheetChange feature of excel. I want to have multiple worksheets feed information into a master worksheet, and if you update a cell in a source sheet, the corresponding cell in the master sheet will also change, and vice versa.
Currently, I am starting small with just trying to get this thing to work with one cell so I can build on this. The code I have works, but only in one direction; when I edit something in the source sheet, the cell value in the master sheet changes to that new value. However, when I try to change the value in the master sheet, the value in the master sheet kinda bounces around until it finally decides to stick to the value derived from the source sheet. This only occurs whenever I try to have the cell portion of the address be the same between the two sheets; if the target cell in Sheet1 is $A$1 and the target cell in Sheet2 is any cell that is not $A$1, then there are no issues. This issue only occurs if the cell in both sheets is the same.
Below is the code that I am currently using.
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
cell_1 = Worksheets("Sheet1").Range("$R$3").Address
cell_2 = Worksheets("Sheet2").Range("$R$3").Address
If Target.Address = cell_1 Or Target.Address = cell_2 Then
Call cellUpdate(Target.Address)
End If
End Sub
Sub cellUpdate(Target As String)
cell_1 = Worksheets("Sheet1").Range("$R$3").Address
cell_2 = Worksheets("Sheet2").Range("$R$3").Address
Application.EnableEvents = False
With ActiveWorkbook
If Target = cell_1 Then
Worksheets("Sheet2").Range("$R$3").Value = Worksheets("Sheet1").Range(Target)
ElseIf Target = cell_2 Then
Worksheets("Sheet1").Range("$R$3").Value = Worksheets("Sheet2").Range(Target)
End If
End With
Application.EnableEvents = True
End Sub
How do I get around this issue? I couldn't find any information online on how to avoid this since the uses I found for this Workbook_SheetChange function are for things that occur in one sheet rather than multiple sheets.
What is happening here is that you extensively use Target.Address. The issue with this is that Target.Address only returns the cell address, not the sheet it is on. For example it would return $A$1. Not Sheet1!$A$1. This means that in your if statement it tests whether "$A$1" = "$A$1" regardless of which sheet this address is on. Therefore it will only ever run the first clause of this loop resulting in it only working one way.
Secondly, you have a lot of redundant code, hard-coding a bunch of addresses multiple times. This can be massively simplified as demonstrated below:
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
Dim cell_1, cell_2 As Range
Set cell_1 = Worksheets("Sheet1").Range("$R$3")
Set cell_2 = Worksheets("Sheet2").Range("$R$3")
Application.EnableEvents = False
If Target = cell_1 Then
cell_2.Value = cell_1.Value
ElseIf Target = cell_2 Then
cell_1.Value = cell_2.Value
End If
Application.EnableEvents = True
End Sub
This code uses the first and second cell as range which stores the entire cell in memory, address, values, the lot. It then switches off EnableEvents as you did (good effort by the way, to prevent yourself from getting stuck in an infinite loop as most people would with this kind of code). Then it checks whether your target cell is cell 1 and switches the value of cell two with cell one, and the same for cell 2. No need for a separate function.
Couple of observations:
Worksheets("Sheet1").Range("$R$3").Address will always return the string $R$3, as will Worksheets("Sheet2").Range("$R$3").Address.
Both lines return exactly the same thing - it doesn't care what sheet it's on.
The Call statement isn't required.
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
Application.EnableEvents = False
'Address is R3 on whichever sheet you're changing.
If Target.Address = "$R$3" Then
'Figure out which sheet was changed and update as required.
Select Case Sh.Name
Case "Sheet1"
Worksheets("Sheet2").Range("R3") = Sh.Range("R3")
Case "Sheet2"
Worksheets("Sheet1").Range("R3") = Sh.Range("R3")
Case "Sheet3", "Sheet4"
'Do stuff if you're on sheet 3 or sheet 4.
Case Else
'Do stuff if you're on any other sheet.
End Select
End If
Application.EnableEvents = True
End Sub
I need to compare two cells: A1 and C1.
If A1 <> C1 Then run some code.
A1's content is a formula dependent cell (from another sheet, same workbook).
C1's content is static, only changing at the end of the macro run.
Issue:
Having an issue with catching Worksheet_Change(ByVal Target As Range) and Worksheet_Calculate() event when the cells contents are changed, as a result of formulas coming from other sheets (same workbook): when A1's content is updated (by formula), event CHANGE nor CALCULATE will catch this change.
Unless I directly key-in in the target sheet any changes, those formula-updated-result cells won't trigger those events, hence not being able to run associated macros 1 and 2.
I have checked out some suggestions from https://stackoverflow.com/search?q=%5Bvba%5D+WORKSHEET_CHANGE&s=ceca4078-9061-4cfb-ae34-f57285b98d7d, but couldn't fix it.
Any ideas or suggestions? Higly appreciated.
Private Sub Worksheet_Change(ByVal Target As Range)
Set Target = Range("a1")
Dim my_Target1 As Variant
Dim my_Target2 As Variant
my_Target1 = Cells(1, 1).Value
my_Target2 = Cells(1, 3).Value
If Not my_Target1 = my_Target2 Then
Call macro1
MsgBox ("end of update routine")
Else: Call macro2
End If
Exit Sub
End Sub
----- here goes the CALCULATE event code----
Private Sub Worksheet_Calculate()
Dim my_Target1 As Variant
Dim my_Target2 As Variant
my_Target1 = Cells(1, 1).Value
my_Target2 = Cells(1, 3).Value
If Not my_Target1 = my_Target2 Then
Call macro1
MsgBox ("end of update routine")
Else: Call macro2
End If
Exit Sub
End Sub
NOTE - I have tried 2 different scenarios:
(1) scenario OK (successful), if the formulas are fed by new data that is keyed in directly into the workbook;
(2) scenario KO (not successful), if the formulas are fed by new data that is coming from an online external source;
i could capture the event with the Workbook_SheetChange(ByVal Sh As Object, _ ByVal Source As Range)
thank you all for your help.
I have Excel Workbook in which there are 4 sheet which has two cell name start date and end date, whose values should be same across all 4 sheet, I want that If I change the value in anyone of the sheets the other three sheets automatically update that values. And vice versa.
Use the Workbook_SheetChange event to update the same cells on every worksheet if any one of the cells changes.
For example, if each sheet has the named ranges start_date and end_date (where their scope is limited to that sheet only), changes made to any start_date or end_date range on any sheet will update the corresponding range on all the other sheets.
Option Explicit
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
On Error GoTo SafeExit
Application.EnableEvents = False
Dim ws As Worksheet
With Sh
If Not Intersect(Target, .Range("start_date")) Is Nothing Then
For Each ws In Worksheets
ws.Range("start_date").Value = Target.Value
Next ws
End If
If Not Intersect(Target, .Range("end_date")) Is Nothing Then
For Each ws In Worksheets
ws.Range("end_date").Value = Target.Value
Next ws
End If
End With
SafeExit:
Application.EnableEvents = True
End Sub
If you are referring to the cells by their address and not by a defined name, something like this could work:
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
On Error GoTo SafeExit
Application.EnableEvents = False
Dim ws As Worksheet
With Sh
' "A1" is the start date, change as needed
If Not Intersect(Target, .Range("A1")) Is Nothing Then
For Each ws In Worksheets
ws.Range("A1").Value = Target.Value
Next ws
End If
' "B1" is the end date, change as needed
If Not Intersect(Target, .Range("B1")) Is Nothing Then
For Each ws In Worksheets
ws.Range("B1").Value = Target.Value
Next ws
End If
End With
SafeExit:
Application.EnableEvents = True
End Sub
This code goes in the ThisWorkbook module in the VBA editor.
You don't need VBA for this but rather named ranges. Put your cursor in the input cell, say E5, for start_date and then click in the named range box (in the upper left corner of the worksheet grid, directly above column A). Type start_date over E5 and hit Enter. Now, cell E5 is named start_date. If you use =start_date anywhere else in the workbook, it will refer to the current contents of cell E5. If you want to edit the named ranges in any way, go to the ribbon menu FORMULAS -> Name Manager.
If you want to do this with VBA for the sole purpose of learning VBA, I would recommend taking a VBA course instead. Udemy has some good ones that often go on sale for about $10, and I'm sure that there are plenty of free resources elsewhere as well.
I have a Function that is attempting to change the current active sheet ("Sheet1") to (Sheet2) and then assign a value to a cell in Sheet2.
However the ActiveSheet.name doesn't change and the assignment fails. see the code snippet. The function is called from sheet1.
Dim oldwksheet as String
oldwksheet = ActiveSheet.Name 'value is Sheet1
Sheets("Sheet2").Activate
Sheets("Sheet2").Select
oldwksheet = ActiveSheet.Name 'value still is Sheet1"
Sheets("Sheet2").Range("F2") = Now() 'this fails.
end function
Why? What am I doing wrong.
You need a sub and not a function.
A function can only return a value to the cell in which it resides. It can't select or activate worksheets or deposit values in arbitrary cells.
I couldn't say why, but you could use a Function
assuming your function's named after "MyFunction" then place in relevant worksheet code pane the following code:
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Count = 1 Then If Left(Target.Formula, 12) = "=MyFunction(" Then Sheets("Sheet2").Range("F2") = Now()
End Sub
this way, whenever you type =MyFunction() in any cell of the relevant worksheet, the current date and time is being placed in cell F2 of Sheet2
I have a macro so that when you highlight a row on sheet1, the macro takes all the info from this row and displays this by itself on sheet2. If you highlight a different row on sheet1, the info on sheet2 is changes to show the info from that row.
My problem is that if I change the info displayed on sheet2, it doesn't change the info on sheet1. Is there a way I could add this functionality?
I have the following code at the moment:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Dim myList
If Target.Address <> Target.EntireRow.Address Then Exit Sub
If Target.Rows.Count > 1 Then Exit Sub
myList = [{"B1","B2","B3","B4","B5","B6","B7","B8","B9","B10","B11","B12","B13","B14","B15"}] '<- adjust to your need
With Target.EntireRow
For i = 1 To UBound(myList)
Sheets("sheet2").Range(myList(i)).Value = .Cells(i).Value
Next
End With
End Sub
Any Help would be awesome! :)
After copying your sheet1 row to sheet2 you could also record the original row # that the values came from. Then you can add an additional macro that would compare the sheet2 values with the values in sheet1 - any changes could then be migrated over.
A possible basic flow:
copy sheet1 row to sheet2 (current macro)
copy sheet1 row # to sheet2 (ie one row down)
make changes on sheet2
copy sheet2 row to sheet1 row (use row # saved on sheet2) -> this assumes that no changes will be made to sheet1.
You are currently using a Worksheet_SelectionChange event macro to recognize when a full single row has been selected. You need a Worksheet_Change event macro for Sheet2 to recognize when values in the B1:B15 range have been changed and pass the changes back to Sheet1.
Because the Worksheet_Change is triggered on a change in values, you will need to disable the Application.EnableEvents property so that it is not triggered when you write the values from Sheet1's Worksheet_SelectionChange sub.
You are going to require a couple of public variables. One to remember the position that changes should be returned to and another to locate the target cells on Sheet2. These can only be made public in a module code sheet.
Book1 - Module1 (Code)
Option Explicit
Public Const sRNG As String = "B1:B15"
Public rRNG As Range
I've made a couple of small modifications to your original Worksheet_SelectionChange and added the disabling of event handling.
Book1 - Sheet1 (Code)
Option Explicit
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
If Target.Cells.Count = Columns.Count And Target.Rows.Count = 1 And _
CBool(Application.CountA(Target)) Then '<~~ one complete non-blank row
On Error GoTo bm_Safe_Exit
Application.EnableEvents = False
With Sheet2.Range(sRNG)
Set rRNG = Target.Cells(1, 1).Resize(.Columns.Count, .Rows.Count)
.Cells = Application.Transpose(rRNG.Value)
End With
End If
bm_Safe_Exit:
Application.EnableEvents = True
End Sub
The Worksheet .CodeName property was used to identify Sheet2 since this does not change if the worksheet is conventionally renamed.
It is a little unclear on how you were planning to identify the row to return the values to once they were changed. I've used a public range-type variable declared in Module1 to record the last location that values were transferred from Sheet1 to Sheet2. Changes on Sheet2 will return them to the last recorded location.
Book1 - Sheet2 (Code)
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range(sRNG)) Is Nothing Then
Debug.Print rRNG.Address(0, 0, external:=True)
On Error GoTo bm_Safe_Exit
Application.EnableEvents = False
rRNG = Application.Transpose(Range(sRNG).Value)
End If
bm_Safe_Exit:
Application.EnableEvents = True
End Sub
Note that the 'remembered' location is in memory only. Closing and reopening the workbook effectively 'zeroes' it. Do not make changes on Sheet2 unless you have freshly loaded values from Sheet1.