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.
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 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.
I'm trying to see if it is possible to choose an item from a data validation list in either sheet 2 or 3 or 4, cell E2 to change automatically. Meaning i want the cell E2 in each of these sheets linked with each other, so if i change any one of them the other has to show the new value. The list itself is in sheet 1.
I also would like to extend this macro from just cell E2 to a range of cells (E2 to E300).
The problem i have now is if i use the code, it also changes the value of cell E2 in sheet 1. I have the code copied to modules in sheets 2, 3 4 and not in module of sheet 1.
Private Sub Worksheet_Change(ByVal Target As Range)
Dim ws As Worksheet
If Target.Address = "$E$2" Then
For Each ws In ThisWorkbook.Worksheets
If Not ws.Name = Me.Name And Not ws.Name = "Sheet1" Then
If Not ws.Range(Target.Address) = Me.Range(Target.Address) Then
ws.Range(Target.Address) = Me.Range(Target.Address)
End If
End If
Next ws
End If
End Sub
Welcome to SO. You cna handle an unique sub from Worksheet_Change event
I made up this code in that event on each worksheet.
Private Sub Worksheet_Change(ByVal Target As Range)
UPDATE_E_VALUES ActiveSheet, Target
End Sub
And the code in a module of sub UPDATE_E_VALUES is:
Public Sub UPDATE_E_VALUES(ByVal vThisWK As Worksheet, ByVal vThisCell As Range)
Application.EnableEvents = False
Application.ScreenUpdating = False
If vThisCell.Row >= 2 And vThisCell.Row <= 300 And vThisCell.Column = 5 Then 'check if cell is in range E2:E300
Select Case vThisWK.Name
Case "Sheet1"
'we do nothing
Case "Sheet5"
'we do nothing
Case Else
'we update all E cells with same address
With ThisWorkbook
.Worksheets("Sheet2").Range(vThisCell.Address).Value = vThisCell.Value
.Worksheets("Sheet3").Range(vThisCell.Address).Value = vThisCell.Value
.Worksheets("Sheet4").Range(vThisCell.Address).Value = vThisCell.Value
End With
End Select
With ThisWorkbook
.Worksheets("Sheet2").Range(vThisCell.Address).Value = vThisCell.Value
End With
End If
Application.EnableEvents = True
Application.ScreenUpdating = True
End Sub
Hope you can adapt this to your needs. You can add/remove extra lines where you want sheets to be affected or not
I am trying to create a workbook where if I change a dropdown on 1 sheet, it automatically updates that same dropdown on a second sheet. These dropdowns will represent different scenarios and my purpose in creating this is to allow the end-user the ability to change the scenario dropdown from any sheet, rather than just one.
I used this reference to create a VBA for changing 1 dropdown -Original VBA code referenced - and it worked correctly (See example workbook to download). However, now I want to add the other 2 dropdowns so that if any changes are made to them it updates accordingly.
I'm also open to other solutions if you know of something better.
Sorry if this question was elementary -- I am new to VBA.
Using the example from the Original VBA code referenced, if you were looking to apply this rule to 2 sets of drop downs (4 total), instead of 1 set - you would duplicate the snippet from "If Not Intersect(..." to "..End if" and then update the reference to the 2nd set of cells.
EXAMPLE:
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
Dim targetSheet As Worksheet
If Not Intersect(Target, Range("A1")) Is Nothing Then ' watch only cell A1
Set targetSheet = ActiveWorkbook.Worksheets("Sheet2") ' define the sheet to copy to
On Error Resume Next
Application.EnableEvents = False
targetSheet.Range("B1") = Target.Value ' copy to cell B1 on the target sheet
Application.EnableEvents = True
End If
If Not Intersect(Target, Range("A2")) Is Nothing Then ' watch only cell A1
Set targetSheet = ActiveWorkbook.Worksheets("Sheet2") ' define the sheet to copy to
On Error Resume Next
Application.EnableEvents = False
targetSheet.Range("B2") = Target.Value ' copy to cell B1 on the target sheet
Application.EnableEvents = True
End If
End Sub
You would then repeat for the other worksheet.
I have a macro (ApplyFilter) that filters through many worksheets based on a date I enter into cell B1 on another worksheet (Grand Totals). That macro is:
Sub ApplyFilter() 'Filters all worksheets except worksheet1 for date entered into _
'Grand Totals!B1
Dim WS_Count As Integer
Dim I As Integer
Dim FilterRange As Variant
FilterRange = Range("'Grand Totals'!B1")
' Set WS_Count equal to the number of worksheets in the active
' workbook.
WS_Count = ActiveWorkbook.Worksheets.Count
' Begin the loop.
For I = 2 To WS_Count
Sheets(I).Select
ActiveSheet.AutoFilterMode = False 'Remove any existing filters
Worksheets(I).Range("A2").AutoFilter Field:=1, Criteria1:=Range("'Grand Totals'!B1").Text
Next I
Sheet1.Activate
End Sub
When I execute this macro manually, it executes and filters as it should. However, when I call this macro from another sub:
Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Me.Range("B1")) Is Nothing Then _
Call ApplyFilter
End Sub
I get a "Macros" window that offers a list of macros available. I can select the "ApplyFilter" macro and click Run and the Macro executes and filters the worksheets as I desire.
I found many references to automatically executing a macro from within a sub, but none refer to the "Macros" window, from which I now must select the macro to run. Instead, when I enter a date in cell B1 of the "Grand Totals" worksheet and hit enter, sub worksheet_change(ByVal Target As Range) should automatically call "ApplyFilter" and apply the date filter to the many worksheets.
I have created a button and used Button_Click to call "ApplyFilter", and all is well. But, it seems more intuitive to enter the date and then press Enter to execute the macro. I could put up with the Button_Click method, but I'm trying to learn VBA firstly, and I'm just stubborn enough to want to learn how to make it work, and I do not want settle just for what will work.
Sheet Code must be in Grand Totals sheet
right click your Grand Totals sheet tab
View Code
Ensure the code below is pasted in here
altf11 back to Excel
Grand Totals Sheet Code
Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Me.Range("B1")) Is Nothing Then Call ApplyFilter
End Sub
more efficient filter code
Sub ApplyFilter()
Dim ws As Worksheet
For Each ws In ActiveWorkbook.Sheets
If ws.Name <> "Grand Totals" Then
ws.AutoFilterMode = False
ws.Range("A2").AutoFilter Field:=1, Criteria1:=Range("'Grand Totals'!B1").Text
End If
Next
End Sub