worksheet_calculate() is not working - vba

I have added a private sub worksheet_calculate() in a sheet called Main. I have a value in column AP with formulas derived from other sheets and if that number is greater than value in X I want to display a message as a warning that it's over, but the code is not working any suggestions why?
Private Sub Worksheet_Calculate()
If Sheets("Main").Range("AP7").value > Sheets("Main").Range("x7").value Then
MsgBox "You Are Over Pieces Suggested"
End If
End Sub

Try this.
Private Sub Worksheet_Calculate()
If Range("AP7").Value > Range("X7").Value Then
MsgBox "You Are Over Pieces Suggested."
End If
End Sub
EDITED####
Edited the original code to run as a Worksheet_Calculate rather than a Change.
Working on trying to set the ranges to columns for you now.
EDIT#########
I flippin love a challenge. Try This.
Private Sub Worksheet_Calculate()
Set Target = Range("AP:AP").SpecialCells(xlCellTypeFormulas)
If Target Is Nothing Then Exit Sub
For Each c In Target
If c > Range("X" & c.Row) Then
MsgBox "You Are Over Pieces Suggested - Cell " & "AP" & c.Row
End If
Next
End Sub

Consider using Data Validation on cell AP7 using a "Custom" formula of: =AP7<=$X$7
Fill in the Error Alert tab on the validation menu: Stop; "You Are Over Pieces Suggested". I think this might achieve what you want without any macros. In fact, it can prevent an invalid number from being entered in the first place.

Related

Validating Cell Input Based on Another Cell

When I input some text in a cell, for example in cell B2-test, I want in cell A6 the input to begin with this string and to end with _VAR1-for example test_VAR1.
I have found a simple solution as formula - =IF(A2="test","test_VAR1") but I want to make it as a VBA code.
So any idea how this can be done?
This is the most minimal example that I can come up with.
The LCase(Range("B2")) would also take "Test" and "TeSt" into account:
Option Explicit
Public Sub TestMe()
With ActiveSheet
If LCase(.Range("B2")) = "test" Then
.Range("A6") = .Range("B2") & "_VAR1"
End If
End With
End Sub
And if you want to check every event of the worksheet, put your code in the corresponding worksheet (Sheet1, Sheet2 and Sheet3 on the picture below):
Option Explicit
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
If Target.Cells.Count > 1 Then Exit Sub
If Intersect(Target, Me.Range("B2")) Is Nothing Then Exit Sub
Application.EnableEvents = False
If LCase(Target) = "test" Then
Me.Range("A6") = Target & "_VAR1"
End If
Application.EnableEvents = True
End Sub
I know you said in your question you wanted macro, but I'm not going to post my own (because I feel like #Vityata's answer should be sufficient)
However, I got impression from your post, that you could adjust / improve your formula instead and avoid macro altogether. It's usually better to avoid macro, when possible, for compatibility reasons (a lot of users have macros disabled by default)
If you simply want to add the keyword "_VAR1" to the input, use the following formula instead
=LTRIM(B2) & "_VAR1"
If the input can be anything, that contains the word "test"
=IF(ISNUMBER(SEARCH("test", TRIM(LOWER(B2)))), LTRIM(B2) & "_VAR1", "Incorrect Input")
The text contains only the word "test" and nothing else
=IF(TRIM(LOWER(B2))="test", LTRIM(B2) & "_VAR1", "Incorrect input"
There are some other variations / tricks you could do with this, but these are some of the most basic examples you can use as your "building blocks"

VBA Nested IF statement

I want to show a message box when a specific cell has a particular value in it. I have done this with the following code;
If Range("P8") = "Y" Then
MsgBox "Message here"
End If
This is within the Worksheet_Change sub so shows the message box everytime another cell value changes. I have tried to get around this by adding a boolean variable, set to true when the messagebox has been shown the first time;
If Range("P8") = "Y" Then
If messageshown = False Then
messageshown = True
MsgBox "Message here"
Else
End If
Else
End If
However the message box still shows every time I change a cell in the worksheet. I have a feeling it';s to do with the way I have written the nested if statement but have tried various different ways and orders of where I place else and end if but to no avail.
Use the Target argument instead - this refers to the actual cell being changed, which is what you are interested in. Test the address of the Target to see if it's the cell you need and then act accordingly. This will stop the message showing when another cell is changed.
Private Sub Worksheet_Change(ByVal Target As Range)
With Target
If .Address = "$P$8" And .Value = "Y" Then MsgBox "Message here"
End With
End Sub
Try this code, it first checks which cell is changed, if it is anything but P8, it will not pop the messagebox.
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Address = "$P$8" Then
If Range("P8") = "Y" Then
MsgBox "This works"
End If
End If
End Sub
As pointed out by Macro Man, there is a more optimal, more efficient option.

Runtime error '28': Out of stack space in Excel VBA

I try to a create a workbook for my requirement. The first sheet include a cell which type is 'Text' and it is for DATE value.
I add Workbook_Open method for set today date when open the workbook as shown below.
Private Sub Workbook_Open()
Sheet1.Range("F6") = Date
End Sub
And I also add Worksheet_Change method for sheet of that cell. That is for validation check as below.
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Address = "$F$6" Then
'Getting insertion date.
insertionDate = Sheet1.Range("F6")
'If date field is not empty
If insertionDate <> "" Then
Call MsgBox("Insertion Date must be inserted.")
End If
End If
End Sub
After that, I tested my code. When open the work book, I got the following error.
Run-time error '28':
Out of stack space
When click 'Debug' button, the cursor shown at the first line of Worksheet_Change method.
I has tried everything what I thought. But nothing is going on. Help me. Thank You.
I got it with this code. It is not satisfied for me but my problem is solved.
Private Sub Worksheet_Change(ByVal Target As Range)
Application.EnableEvents = False
If Target.Address = "$F$6" Then
'Getting insertion date.
insertionDate = Sheet1.Range("F6")
'If date field is not empty
If insertionDate <> "" Then
Call MsgBox("Insertion Date must be inserted.")
End If
End If
Application.EnableEvents = True
End Sub
Msgbox not need Call statement. Try to remove Call and test again.
And I have some reference from https://support.microsoft.com/en-us/kb/126090?wa=wsignin1.0. Its may be explain your error.
Possible sources of error marked in the code
Option Explicit ' Candidate
Private Sub Worksheet_Change(ByVal Target As Range)
If Target.Address = "$F$6" Then
'Getting insertion date.
Dim insertionDate as String ' Candidate
insertionDate = Sheet1.Range("F6").Text ' Candidate
'If date field is not empty
If insertionDate <> "" Then
MsgBox("Insertion Date must be inserted.") ' Candidate
End If
End If
End Sub
Make sure you placed Worksheet_Change in the sheet module.

Macro launching when a cell value changes due to a formula not by the user

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

CircleInvalid and ClearCircle methods for a particular cell in excel vba 2007

I am using data validation in excel 2007. I am using this code to make invalid data marked with red circle.
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rc As Integer
Range(Target.Address).Select
ActiveSheet.ClearCircles
ActiveSheet.CircleInvalid
If Not Range(Target.Address).Validation.Value Then
rc = MsgBox("Data Validation errors exist! " & Range
(Target.Address).Validation.ErrorMessage & " Please correct circled entries!", vbCritical, "Failure")
Exit Sub
End If
End Sub
As you can see in the code when I put wrong data then first of that specific range is going to selected and then all invalid data is marked with red circle.
But I want that only that specific cell should be marked with red not all data .
Thanks.
You can try this code from an Excel MVP:
Dim TheCircledCell As Range
Sub CircleCells(CellToCircle As Range)
If Not CellToCircle Is Nothing Then
With CellToCircle
If .Count > 1 Then Exit Sub
Set TheCircledCell = CellToCircle
.Validation.Delete
.Validation.Add xlValidateTextLength, xlValidAlertInformation, xlEqual, 2147483647#
.Validation.IgnoreBlank = False
.Parent.CircleInvalid
End With
End If
End Sub
Sub ClearCircles()
If Not TheCircledCell Is Nothing Then
With TheCircledCell
.Validation.Delete
.Parent.ClearCircles
End With
End If
End Sub
Note that you can't use the Excel standard Validation function on these cells.
[Source and explanation of the code]