I am VBA beginner and was trying to filter the data based a cell value,after googling around a bit I have written a code which works
Sub FilterDepartment_Sales()
Sheet6.Activate
Sheet6.Cells.Select
Selection.AutoFilter
Selection.AutoFilter Field:=12, Criteria1:=Sheet8.Range("B3").Value
End Sub
I had to assign this macro to object to run , the data was not getting automatically refreshed wen I change the cell value in B3, how to make it auto refresh whenevr I change the value in the B3
Also in the above code when I try to do the following it gives me an wrror
Sub FilterDepartment_Sales()
Sheet6.Activate
'Sheet6.Cells.Select
'Selection.AutoFilter
'Selection.AutoFilter Field:=12, Criteria1:=Sheet8.Range("B3").Value
Sheet6.Cells.Select.AutoFilter Field:=12, Criteria1:=Sheet8.Range("B3").Value
End Sub
I get an "Object not found error" , any reason on why I can't condense the code like that
Base on the answer I had modified the code
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
Sheet6.Activate
Sheet6.Cells.Select
Selection.AutoFilter
Selection.AutoFilter Field:=12, Criteria1:=Sheet8.Range("B3").Value
End Sub
But now when I change the value in B3 nothing is happening , do I need to add anything ?
If you want to run a code every time you change something in a sheet, you have to place the code within a method, which handles event of changing something.
https://i.stack.imgur.com/sZQqH.png
Here is the place, where you have to find change event, on the left you have to select Sheet1, then chose Worksheet fromd ropdown list, the right one will contain all events. Change event will be raised on every change, so you'll need to ,,filter" this and handle only situation when the particular cell was changed:
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Row = 2 And Target.Column = 3 Then
Sheet6.Cells.AutoFilter Field:=12, Criteria1:=Sheet8.Range("B3").Value
End If
End Sub
In the example we watch for changes only in cell B3.
Related
I am very new at VBA, which is why I'll ask a question here:
I have a report, which is set to change the right header font, size and content, all dependent on a cell value, and it is updated if I change the value of another cell. The problem is, the cell which value it is dependent on, stems from a formula, which is controlled by a timeline slicer.
The slicer determines the period of a certain pivot table, and the cell which changes takes the max date of this period (Cell is called 'EndDate'). From this, the cell which has the input to the header shows the current quarter (Cell is called 'ReportNo').
The code I have now is the following:
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Application.Intersect(Target, Me.Range("EndPeriod")) Is Nothing Then
ThisWorkbook.Worksheets("Report").PageSetup.RightHeader = "&28" & Me.Range("ReportNo").Text
End If
End Sub
Is there any way, in which I can make the sheet run the VBA when the cell value changes in the formula dependent cell?
I've tried to add the first answer to the thread to the code:
How about something like this:
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Address = Range("EndPeriod").Address Then 'if something changes on ReportNo
Call yourmacro 'call your vba macro
End If
End Sub
Sub yourmacro()
ThisWorkbook.Worksheets("Report").PageSetup.RightHeader = "&28" & Me.Range("ReportNo").Text
End Sub
Add the code to:
Anyone know how to block cells from input (also gray it out) if for example cells A1:C1 = "No" then the rest of the row up to a say F1 is grayed out and blocked from input? I was hoping to do this in VBA but if there are other easier ways, please let me know! Thank you!
Didi
Just to show a different approach:
Put this in the sheet-code-tab:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
If Not (Intersect(Target, Me.Range("D1:F1")) Is Nothing) And Me.Evaluate("AND(LOWER(A1:C1)=""no"")") Then Me.Range("A1").Select
End Sub
And to grey them out, best will be conditional formatting:
Range: =$D$1:$F$1
Formula: =AND(LOWER($A$1:$C$1)="no")
Using the conditional formatting allows to change the cells as you like without the need to alter the VBA code (this also will be faster)
The VBA part itself, just sets the selected cell to A1 if A1:C1 is "No" and a range is selected which also includes any of the cells of D1:F1
The LOWER can be skipped if you want it to be case sensitive
The only con is: if A1:C1 is "no" you still can paste a range to a cell (not any of D1:F1 directly) which also include the locked cells.
The biggest pro is: this also works for shared workbooks (as there is no need to lock/unlock sheets)
EDIT
If the cells need to be protected then something like this will do:
Private Sub Worksheet_Change(ByVal Target As Range)
Dim a As Boolean
a = Me.Evaluate("AND(LOWER(A1:C1)=""no"")")
If a <> Me.Range("D1").Locked Then
Me.Unprotect
Me.Range("D1:F1").Locked = a
Me.Protect
End If
End Sub
as was mentioned in the comments, look to use a workbook change event with the following sub
Sub test()
If Worksheets("Sheet1").Range("A1").Value = "no" And Worksheets("Sheet1").Range("B1").Value = "no" And Worksheets("Sheet1").Range("C1").Value = "no" Then
Worksheets("Sheet1").Range("D1:F1").Interior.Color = RGB(220, 220, 220)
Worksheets("Sheet1").Range("D1:F1").Locked = True
Worksheets("Sheet1").Protect
End If
End Sub
I have tried code that I've found here on stackoverflow, and elsewhere but they aren't working as I think they can. I'll list them below. I'm almost certain this is an easy question.
What I'm trying to do: If in any of the cells in the range A2:A100 there is any text or number whatsoever, then make the worksheet tab red. And I will need to do this on over 20 tabs. This must execute upon opening the workbook, and thus not require manually changing a cell or recalculating.
The problems I've had with other code: As far as I can tell they require editing a cell, and then quickly hitting enter again. I tried SHIFT + F9 to recalculate, but this had no effect, as I think this is only for formulas. Code 1 seems to work albeit with having to manually re-enter text, but no matter what color value, I always get a black tab color.
Code I've tried:
Code 1:
Private Sub Worksheet_Change(ByVal Target As Range)
MyVal = Range("A2:A27").Text
With ActiveSheet.Tab
Select Case MyVal
Case ""
.Color = xlColorIndexNone
Case Else
.ColorIndex = 6
End Select
End With
End Sub
Code 2: This is from a stackoverflow question, although I modified the code slightly to fit my needs. Specifically, if in the set range there are no values to leave the tab color alone, and otherwise to change it to color value 6. But I'm sure I've done something wrong, I'm unfamiliar with VBA coding.
Private Sub Worksheet_Calculate()
If Range("A2:A100").Text = "" Then
ActiveWorkbook.ActiveSheet.Tab.Color = xlColorIndexNone
Else
ActiveWorkbook.ActiveSheet.Tab.Color = 6
End If
End Sub
Thanks for your help!
I posted this on superuser first, but perhaps stackoverflow is more appropriate since it is explicitly programming-related.
Only two things will be able to switch the condition in this statement:
If Range("A2:A100").Text = "" Then
You've already identified both of them, changing the contents of the one of the cells in that range on a worksheet, or a formula in one of those cells recalculating to or from a value of "". As far as event triggers go, if the formula result changes, both the WorkSheet_Calculate and Worksheet_Change events will fire. Of the two, Worksheet_Change is the one to respond to, because WorkSheet_Calculate will only fire if any of the cells in A2:A100 contain a formula. Not if they only contain values - your "Code 2" isn't wrong, the event was just never firing.
The simple solution is to set your tab colors when you open the workbook. That way it doesn't matter if you have to activate a cell in that range and change it - that's only way the value you're testing against is going to change.
I'd do something like this (code in ThisWorkbook):
Option Explicit
Private Sub Workbook_Open()
Dim sheet As Worksheet
For Each sheet In Me.Worksheets
SetTabColor sheet
Next sheet
End Sub
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
If Not Intersect(Target, Sh.Range("A2:A100")) Is Nothing Then
SetTabColor Sh
End If
End Sub
Private Sub SetTabColor(sheet As Worksheet)
If sheet.Range("A2:A100").Text = vbNullString Then
sheet.Tab.Color = xlColorIndexNone
Else
sheet.Tab.Color = 6
End If
End Sub
EDIT: To test for the presence of specific text, you can do the same thing but need to have the test check every cell in the range you're monitoring.
Private Sub SetTabColor(sheet As Worksheet)
Dim test As Range
For Each test In sheet.Range("A2:A100")
sheet.Tab.Color = xlColorIndexNone
If test.Text = "whatever" Then
sheet.Tab.Color = vbRed
Exit For
End If
Next test
End Sub
Maybe test the len of the trimmed joined string of cells:
Private Sub Worksheet_Calculate()
If Len(Trim(Join(Application.Transpose(Range("A2:A100"))))) = 0 Then
ActiveWorkbook.ActiveSheet.Tab.Color = xlColorIndexNone
Else
ActiveWorkbook.ActiveSheet.Tab.Color = 6
End If
End Sub
This code will fire off every time the sheet calculates though as it is event code, I am not sure if that is what you want? If not then post back and we can drop it into a normal sub for you and make it poll all the sheets to test.
Worksheet_Change function will get called everytime there's change in the target range. You just need to place the code under Worksheet. If you have placed the code in the module or Thisworkbook then it wont work.
Paste the below in Sheet1 of your workbook and check if it works. Of Course you will need to do modification to the below code as I have not written complete code.
Private Sub Worksheet_Change(ByVal Target As Range)
Dim WatchRange As Range
Dim IntersectRange As Range
Set WatchRange = Range("A1:A20")
Set IntersectRange = Intersect(Target, WatchRange)
If IntersectRange Is Nothing Then
''Here undo tab color
Else
ActiveSheet.Tab.ColorIndex = 6
End If
End Sub
I would like my Macro to launch whenever a value in a cell containing a formula changes.
i.e. the user is modifying another cell thus changing the value of the cell in question.
I have noticed that using the statement (found herein), only works if the user modifies the cell itself but not if the cell changes automatically - due to a formula as specified above.
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Intersect(Target, Range("A20")) Is Nothing Then ...
Any thoughts??
I tried to follow the answers from this question "automatically execute an Excel macro on a cell change" but it did not work...
Thanks in advance :)
A possible work-around comes from the fact that, to change a value, the user needs to change the selection first. So I would:
1) Declare a global variable called "oldValue" on top of the WS source code module:
Dim oldValue As Variant
2) Register the old value of your formula before the user types anything (let's say it's in Range("A4"), I let you adapt with the others):
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
oldValue = Range("A4")
End Sub
3) Check if the change has affected the formula in the Change event:
Private Sub Worksheet_Change(ByVal Target As Range)
If Range("A4") <> oldValue Then
MsgBox "User action has affected your formula"
End If
End Sub
I've tested with a simple sum, I'm able to write cells that are not involved without any prompt but if I touch one of the cells involved in the sum the MsgBox will show up. I let you adapt for multiple cases, for user adding/removing rows (in that case I suggest to name the ranges containing the formulas you want to track) and the worksheet references.
EDIT I'd like to do it at once, not by going through 2 processes, is it possible? The problem is my macro involves a range containing more than one cell so it will be hard to store old values for 10 cells.
If ranges are next to each other, then instead of using a variable you can use a collection:
Dim oldValues As New Collection
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
For j = 1 To 10
oldValues.Add Range("A" & j).Value
Next j
End Sub
Private Sub Worksheet_Change(ByVal Target As Range)
For j = 1 To 10
If Range("A" & j).Value <> oldValues(j) Then
MsgBox "The value of Range(A" & j & ") has changed"
End If
Next j
End Sub
Of course, if ranges are not close to each other, you can just store them anyway in the SelectionChange event like this:
oldValues.Add Range("A1").Value
oldValues.Add Range("B7").Value
'...
and if you done this ONCE, with 10 ranges only, it should be a reasonable solution to your problem.
You said, "I would like my Macro to launch whenever a value in a cell containing a formula changes..."
If having your code run whenever a cell containing a formula is recalculated (which is not exactly what you asked for), one solution might be to create a VBA function that simply returns that value passed to it, plus does whatever else you want to do when the formula is recalculated...
Public Function Hook(ByVal vValue As Variant) As Variant
Hook = vValue
' Add your code here...
End Function
...then "wrap" your formula in a call to this function. For example, if the formula you are interested in is =A1+1, you would change this to =Hook(A1+1), and the Hook function would be called whenever A1+1 is recalculated (for example, when the value in A1 changes). However, it is possible that recalculating A1+1 will yield the same result and still call the Hook function (for example, if the user re-enters the same value in A1).
You can have a go at this:
First, in a Module Code declare a Public Variable.
Public r As Range, myVal '<~~ Place it in Module
Second, initialize your variables in Workbook_Open event.
Private Sub Workbook_Open()
Set r = Sheet1.Range("C2:C3") '<~~ Change to your actual sheet and range
myVal = Application.Transpose(r)
End Sub
Finally, set up your Worksheet_Calculate event.
Private Sub Worksheet_Calculate()
On Error GoTo halt
With Application
.EnableEvents = False
If Join(myVal) <> Join(.Transpose(r)) Then
MsgBox "Something changed in your range"
'~~> You put your cool stuff here
End If
myVal = .Transpose(r)
forward:
.EnableEvents = True
End With
Exit Sub
halt:
MsgBox "Error " & Err.Number & ": " & Err.Description
Resume forward
End Sub
Above will trigger the event when values in C2:C3 changes.
Not really very neat but works in detecting changes in your target range. HTH.
Declaring a module -level variable like Matteo describes is definitely one good way to go.
Brian 's answer is on the right track with regards to keeping all is the code in the same place, but it's missing one critical part : Application.Caller
When used in function that is called by a single cell, Application.Caller will return the Range object of that cell. This way you can store the old value within the function itself when it is called, then once you're done with calculating the new value you can compare it with the old and run more code as required.
Edit: The advantage with Application.Caller is that the solution scales in and of itself, and does not change no matter how the target cells are arranged (I.e. Continuous or not).
how do I, using VBA, make a G1 read only if any cell in a range say like A4:E50 has a value? My intent is to disable the option in G1 which is a drop down list, as soon as the user populates any of the cells in range A4:E50. If the users deletes all the values in the range, only then will the options in G1 be available. How do i achieve this?
This is a quick solution I just came up with... Not pretty, but it'll do the trick:
First run this macro just once:
Sub LockOneTime()
ActiveSheet.Cells.Locked = False
ActiveSheet.Range("G1").Locked = True
End Sub
Then put this in your worksheet code:
Private Sub Worksheet_Change(ByVal Target As Range)
If Application.WorksheetFunction.CountA(Range("A1:E40")) > 0 Then
ActiveSheet.Protect contents:=True
Else
ActiveSheet.Protect contents:=False
End If
End Sub
It's a quick and dirty way to get what you're looking to achieve...
EDIT Based upon Other cells being Locked:
Given you can't use worksheet protection to avhieve your goal, just put this code in your worksheet's code module (You no longer need the first LockOneTime macro):
Private Sub Worksheet_Change(ByVal Target As Range)
If Not (Intersect(Target, Range("G1")) Is Nothing) Then
If Application.WorksheetFunction.CountA(Range("A4:E50")) > 0 Then
Application.EnableEvents = False
MsgBox "You cannot change the value in cell G1"
Application.Undo
Application.EnableEvents = True
End If
End If
End Sub
This won't let a change happen to cell G1, BUT its draw-back is, assuming you changed a lot of cells and G1 was one of them, it won't let any changes happen... In other words, if G1 is one of the cells being changed, then none of the cells will be allowed to be changed.
Hope this is ok by you, otherwise, the code gets a bit more complex and involved....