I am trying to create a survey in Excel and want to hide and unhide rows based on their answers. For example, if D3 = "no" hide rows D4:D10, and I want to repeat this multiple times throughout, but the number of rows to hide changes. So if D3 = "yes" leave unhidden. Then move to answer D5, if D5 = "no" hide rows D6:D7. And this continues on and on throughout.
Using the worksheet_change() event. In your VBE double click the worksheet where this change (this cell) will happen. Then enter:
Private Sub Worksheet_Change(ByVal Target As Range)
'Detect if the worksheet change was on cell D3
If Not (Intersect(Target, Range("D3")) Is Nothing) Then
'Hide rows if the value of D3 is "No"
Range("D4:D10").EntireRow.Hidden = (Range("D3").Value = "No")
End If
End Sub
Every time a change happens on this worksheet this subroutine will fire off, test if the change occured in cell "D2" then toggle the row's hidden property based on the value.
The Hidden property of the EntireRow range object takes a True or False value, so we are able to just set it equal to the result of the conditional statement (Range("D3").value = "No") which will return a True or False greatly simplifying the amount of code you need to write.
You'll just need to add more If Not (Intersect(Target, Range("whatever")) Is Nothing) Then lines to test for your other cells like D5 and hide the appropriate rows base on whatever value you are testing for in that cell.
If that gets to be too much code (testing all those intersects) you can just test the intersects once like:
If Not (Intersect(Target, Union(Range("D3"), Range("D5"), Range("D8"))) Is Nothing) Then
'And then in here your individual lines that toggle the hidden property:
Range("D4:D10").EntireRow.Hidden = (Range("D3").Value = "No")
Range("D6:D7").EntireRow.Hidden = (Range("D5").Value = "No")
...
End If
Lastly, because this beast will fire any time any change is made on this worksheet, you may want to shut off event firing while the subroutine is running. So turn enableEvents off at the top of the subroutine and turn it back on at the end:
Private Sub Worksheet_Change(ByVal Target As Range)
Application.EnableEvents = False
If Not (Intersect(Target, Union(Range("D3"), Range("D5"), Range("D8"))) Is Nothing) Then
Range("D4:D10").EntireRow.Hidden = (Range("D3").Value = "No")
Range("D6:D7").EntireRow.Hidden = (Range("D5").Value = "No")
Range("D9:D12").EntireRow.Hidden = (Range("D8").Value = "No")
End If
Application.EnableEvents = True
End Sub
This will prevent this same subroutine getting called by itself while it's busy changing the sheet causing an infinite loop and locking up excel (which sucks if you didn't save the workbook before firing it off).
Related
I have a bunch of choices that can be made in the cells (C41:C59) using a dropdown, the value of the dropdown also is seen in the cells (E41:E59). For each choice I want different rows to show or hide. Hide if the dropdown is N/A else show the rows. The problem i can't get around is that every choice has different rows and als a different amount of rows. So I tried to make a code per choice and only change this when the cell in column E changes. This is what I have so far, but doesn't do anything.
If Not Application.Intersect(Target, Range("E41")) Is Nothing Then
If Range("E41") = "N/A" Then
[67:73].EntireRow.Hidden = True
Else
[67:73].EntireRow.Hidden = False
End If
End If
The code below is an event procedure. It runs when a cell is changed on the worksheet in whose code sheet the procedure is found. (The location of the code in that particular module is paramount.) If a single cell was changed - ignoring multiple simultaneous changes such as might be caused by copy/paste action - the code will check if the modified cell was in the ranges C41:C59 or E41:E59. If so, it will hide or show rows in the same worksheet depending upon whether or the cell's value is "N/A" after modification.
Private Sub Worksheet_Change(ByVal Target As Range)
' 010
Dim TriggerRange As Range
Dim Rng As Range
' ignore simultaneous changes of many cells
If Target.Cells.CountLarge > 1 Then Exit Sub
Set TriggerRange = Application.Union(Range("C41:C59"), Range("E41:E59"))
If Not Application.Intersect(TriggerRange, Target) Is Nothing Then
Select Case Target.Row
Case 41, 46, 59
Set Rng = Range("67:73")
Case 50 To 59
Set Rng = Range(Rows(67), Rows(73))
Case Else
Set Rng = Range(Rows(67), Rows(73))
End Select
Rng.Rows.Hidden = (Target.Value = "N/A")
End If
End Sub
In this code always the same rows are hidden or shown. The code serves to demonstrate how you could specify different row ranges depending upon which row the changed cell is in, using different syntax depending upon your preference.
I have built an appraisal sheet that allows you select your name from a drop down and all other details about you pop up in other cells. One of such detail will either pop up as Yes or No, depending on whether you manage a team. Now, my VBA code references this cell (F8) that has Yes or No, and it hides or unhides some rows depending on the value of F8. The problem here is that F8 has a formula that was used to derive its value (A Vlookup) and the formula reads
=IFERROR(VLOOKUP($C$6,Data!$E:$I,5,0),"")
My VBA code no longer hides or unhides any cells since I started using a formula in F8. My VBA code is below
ActiveSheet.Activate
If Not Application.Intersect(Range("F8"), Range(Target.Address)) Is Nothing Then
Select Case Target.Value
Case Is = "Yes": Rows("25:26").EntireRow.Hidden = False
Case Is = "No": Rows("25:26").EntireRow.Hidden = True
End Select
End If
End Sub
Range(Target.Address) is actually the same as Target, don't over complicate things.
And ActiveSheet is called ActiveSheet because that sheet is active. So to .Activate an aleady active sheet ActiveSheet.Activate is pretty useless :)
The following should do it.
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
If Not Application.Intersect(Me.Range("C6"), Target) Is Nothing Then
Select Case Me.Range("F8").Value
Case Is = "Yes": Me.Rows("25:26").EntireRow.Hidden = False
Case Is = "No": Me.Rows("25:26").EntireRow.Hidden = True
End Select
End If
End Sub
But note that if the values in Data!$E:$I that you lookup change then the formula result of F8 =IFERROR(VLOOKUP($C$6,Data!$E:$I,5,0),"") will change without triggering the Worksheet_Change event! So be aware that after changing something in the Data sheet you need to rewrite/re-select the value in C6, to trigger the macro again.
I want to hide columns with value 0 in range G4:FC4
The cell can take values 0 (in that case the columns should be hidden) or 1 (in that case they should be visible). The value 0 or 1 is formula based and depends on the drop down input value in cell D2.
When I change the value in D2 manually the macro works fine, but when I use the drop down box it doesn't. Please find below my code. Any advice?
Many thanks
Private Sub Worksheet_Change(ByVal Target As Range)
Dim r As Range
Dim c As Range
For Each c In Sheets("P_MM6").Range("G4:FC4").Cells
c.EntireColumn.Hidden = True
If c.Value = 1 Then
c.EntireColumn.Hidden = False
End If
Next c
End Sub
Changing manually vs drop-down should not matter, except if perhaps somewhere else in your code events are being disabled and not turned back on, or maybe calculations are set to manual and thus do not create a change in the formula value, so, even though the code fires, the column values don't change.
That said, the code below will help you because it will not try to hide/unhide on every cell change, but only when D2 changes.
Private Sub Worksheet_Change(ByVal Target As Range)
If target.address = "$D$2" Then
Dim c As Range
For Each c In Sheets("P_MM6").Range("G4:FC4").Cells
c.EntireColumn.Hidden = c.Value = 0
Next c
End If
End Sub
Also make sure that calculations are set to automatic and they are not being turned off somewhere in your code and also that events are not being disabled and not turned back on somewhere else in the code.
If all that doesn't help, put a break point on the Worksheet_Change line of code and change the cell via drop-down and see what happens.
I'm trying execute this code when a button is pressed in the sheet,
but I receive this error :
Call Worksheet_Change -> Compilation error - The argument is not optional
Sub Worksheet_Change(ByVal Target As Range)
Set MyPlage = Range("A1:I1200")
For Each cell In MyPlage
Select Case cell.Value
Case Is = "OK"
cell.EntireRow.Interior.ColorIndex = 43
Case Is = "NOTOK"
cell.EntireRow.Interior.ColorIndex = 3
Case Is = "P"
cell.EntireRow.Interior.ColorIndex = 6
Case Else
cell.EntireRow.Interior.ColorIndex = xlNone
End Select
Next
End Sub
Private Sub CommandButton1_Click()
Call Worksheet_Change
End Sub
Look at the Worksheet_Change sub - it has '(ByVal Target As Range)' as an argument. That means that when the worksheet changes, the sub triggers, and it carries the value of the range which changed. This allows you to manipulate the last edited cell, for example. In your command button, you aren't providing the Worksheet_Change sub with information about what Range you are referring to. As the error states, that argument is not optional.
However in your case, I can't see why you need to use the Worksheet_Change sub at all. I suggest you simply take the code currently within the top sub, and put it fully within the button's sub.
The worksheet_change sub requires a range to be passed to it. Something like this should make it run:
Private Sub CommandButton1_Click()
Call Worksheet_Change(Range("A1"))
End Sub
However putting this in the worksheet change event means that it triggers and runs every time anything in the sheet changes. The range value passed to the change event (A1 in my example above) is always the range of whatever cell/cells changed in the sheet.
If you want it to run ONLY when the button is pressed, name the sub something else and remove the 'byval target as range' argument to avoid having to pass a range to the sub. Something like:
Sub testSub()
Set MyPlage = Range("A1:I1200")
For Each cell In MyPlage
Select Case cell.Value
Case Is = "OK"
cell.EntireRow.Interior.ColorIndex = 43
Case Is = "NOTOK"
cell.EntireRow.Interior.ColorIndex = 3
Case Is = "P"
cell.EntireRow.Interior.ColorIndex = 6
Case Else
cell.EntireRow.Interior.ColorIndex = xlNone
End Select
Next
End Sub
Private Sub CommandButton1_Click()
Call testSub
End Sub
If you want it to run any time the sheet is changed and also when the button is pressed, you're fine as you are, but I can't see that you're doing anything in your change sub that needs it to be in the worksheet change event?
Edit: Your problem is that you're looping through a range and undoing what you previously. Consider that this is your data:
__|_A__|_B_|___C___|__D__|_E_|_F_|_
1_|_OK_|___|_NOTOK_|_FOO_|_P_|___|_
2_|____|___|_______|_____|___|___|_
And for the purposes of this example (to keep it short) your MyPlage range is A1 to F2. Your loop is finding values and taking action like so:
A1 = OK > Colour whole row green
A2 = "" > Clear colour from whole row
B1 = "" > Clear colour from whole row (the row that was previously green)
B2 = "" > Clear colour from whole row
C1 = NOTOK > Colour whole row red (the row that was previously cleared of colour)
C2 = "" > Clear colour from whole row
D1 = "FOO" > Clear colour from whole row (the row that was previously red)
D2 = "" > Clear colour from whole row
E1 = "P" > Colour whole row yellow(the row that was previously cleared of colour)
E2 = "" > Clear colour from whole row
F1 = "" > Clear colour from whole row (the row that was previously yellow)
F2 = "" > Clear colour from whole row
Your end result is that all rows are cleared of colour as you're only taking the value in your last column as the input to determine row colour. So in your example, you'll only see a coloured row if your I column contains any of the values you're looking for.
You can see this in action by hitting F8 to step through the sub, or place a breakpoint on your celk.entirerow... elements so it stops when a case select is found to be true and then hitting F8 from there. I'd recommend trying it on a small data set first to see it in action or you'll have to hit F8 200 times to move across a column.
You don't use Target in Worksheet_Change sub, so you can pass any range to this sub like:
Call Worksheet_Change(Range("A1"))
or
Call Worksheet_Change(Range("B5000"))
I have a cell on Sheet1 that contains a drop-down list, let's say N3. The items in the drop down change, depending on the value in J3. At start, both cells are blank. Enter data in J3, and the drop down populates in N3. If I clear the contents of J3, the drop down in N3 is now empty, but the last selected value (if one was selected), still appears as a 'ghost' entry. It's a ghost entry to ME because it is old data, but I do understand the software is doing as designed. If J3 is cleared of contents, how do I get N3 to be cleared of that last selection? I am not VBA trained, but dangerous enough to handle it if that's what's needed to accomplish this. thanks!
You may consider to use the worksheet_change event.Put the below code in sheet1 code module.
Private Sub Worksheet_Change(ByVal Target As Range)
On Error GoTo err_rout
Application.EnableEvents = False
If Not Intersect(Range("J3"), Target) Is Nothing And Target.Value = vbNullString Then
Range("N3").Value = vbNullString
End If
err_rout:
Application.EnableEvents = True
End Sub