Compilation error when calling a Sub by a button click - excel

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"))

Related

List Box Form Control selectrion triggers Worksheet SelectionChange

I am trying to keep track of changes (to be appeared in a chart) upon every changes made in the worksheet using this reference (codes are copied below as example reference).
The changes in the worksheet actually can be made by several List Boxes (form Control Menu) and other cells e.g. Input 3 and 4. Let's say the resulting value caused by any change appears in the cell G14, and as the example code I want to keep them in I14 (as in the example image).
The formula might be e.g. D14 + E14 + K14 + L14 where K14 and L14 are values linked to the ListBoxes.
Selections in Input 3 and 4 trigger Worksheet_Change but selections in List Box apparently don't, shall I add a macro for List Box callback in which Worksheet_Change gets triggered?
Apart from that, if there is any better saving the results method, please mention it.
Many thanks in advance!
Dim xVal As String
Private Sub Worksheet_Change(ByVal Target As Range)
Static xCount As Integer
Application.EnableEvents = False
If Target.Address = Range("G14").Address Then
Range("I14").Offset(xCount, 0).Value = xVal
xCount = xCount + 1
Else
If xVal <> Range("G14").Value Then
Range("I14").Offset(xCount, 0).Value = xVal
xCount = xCount + 1
End If
End If
Application.EnableEvents = True
End Sub
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
xVal = Range("G14").Value
End Sub
As you are asking about trigger event to run a macro on selection of listbox value (form control), I believe this could be considered as answer. I am sure someone with better knowledge will improve it.
Worksheet_Change event occurs when a cell or range of cells is changed manually (not by any auto process like calculation or selection of listbox value)
There can be different scenarios
First
There is no link between calculation of cells affected by the listbox change. Say, in First ListBox, you select 10, and cell A2 is updated to 10 and cell A3 is calculated value changed to 30. Then You select in second listbox 15 and cell B2 is changed to 15 and cell B3 is calculated independently if A2 and A3 to 90.In this case these are independent listboxes and their results. So, in this case, you can assign macro to each listbox which will run every time you change value in each listbox.
Second
Dependent calculations: Say the desired result for tracking is in D3 which will be calculated only on selection of four listbox values in A2, B2, C2 and D2. In this case you will not like to run macro for every list change but only after selecting values in all listboxes and calculation of D3. So in that case, instead of assigning macros to all the listboxes you could assign it only to last listbox.
Third
By now you must have understood there are two events taking place. One is selection of listbox value and second is calculation. In the second scenario, if you want to run macro for every change in calculation, say when you select A2 and when you select B2, etc. then you can simply use calculation event instead of assigning macro to every listbox. It will run everytime when a value is changed causing worksheet to calculate.
For assigning macro to listbox (form control) --- You can directly assign a macro to the list box.. First create a macro in VBA . Then Just right click on the list box and click assign macro. then select a macro to be assigned. .. The macro will be run when you click the listbox to change the value
Also, as you want to track the result calculated with macro, you need the sheet to be calculated first. Start the macro with Worksheet.Calculate method to be safe (in case formula results are not updated for some reason).
Tahnks to #Naresh, solved the problem in the following way, any improvement editis more than welcome since I know the codes might seem inefficient!
Dim xVal As String
Public Function customRecorder(Target As String)
Static xCount As Integer
Application.EnableEvents = False
Range("I14").Offset(xCount, 0).Value = xVal
xCount = xCount + 1
Application.EnableEvents = True
End Function
Private Sub Worksheet_Calculate()
xVal = Range("G14").Value
customRecorder (Range("G14"))
End Sub

Hide rows based on choice

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.

Change event triggered by change of cell value

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.

My script breaks Excel's calculation can someone help me identify what's wrong with it?

I've set up a VBA code that sets up a form for the user to fill out information. I've set up another VBA code that runs upon sheet calculation that runs some checks and corrections to any information that is out of line. As soon as the second code runs all sheet calculation stops and I can only edit the first unlocked cell, even when sheet protection is off and I have selected another cell. All typing goes to the first unlocked cell.
This script has worked when triggered by selection change or by running the script manually. I've searched the internet but all I find are list of settings (all of which I have checked).
Private Sub Worksheet_Calculate()
'Transfer
'
If Range("AAA1").Value = "Transfer" And Range("AAB1").Value = "Not Empty" Then
If Range("D3").Value = "0" Or Range("D3").Value = "" Then
Range("AAB1").Value = "Empty"
End If
End If
If Range("AAA1").Value = "Transfer" And Range("AAB1").Value = "Empty" Then
If Range("E3").Value = Empty Or Range("F3").Value = Empty Then
Else
Range("D3").Value = Range("AA3").Value
Range("AAB1").Value = "Not Empty"
End If
End If
If Range("D3").Value > Range("AA3").Value And Range("AAA1").Value = "Transfer" Then
Range("D3").Value = Range("AA3").Value
End If
End Sub
The expected result is for cell D3's value to be automatically set to the max value (calculated in cell AA3 using the value in cell E3) when both cells E3 and F3 have been filled out and cell D3 is either empty or equal to zero. I use cell AAB1 as a "switch" so that the user can enter a custom amount without a hasle. Cell AAA1 is set by the first macro that sets up the form. It all works once. Then I encounter the problem described above.
I followed the advice of Harassed Dad and put Application.EnableEvents = FALSE at the very beginning of the script and = TRUE at the very end. It did the trick. I didn't think that changing values would trigger calculation before the script had finished. Thanks guys.

Hide and unhide rows based on a value

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).

Resources