Modifying cells in Excel with a VBA function - excel

Every cell modifying line in the code below "throws" a 1004 error.
The following Function is called through a cell, this way: =bonjour()
Here is the code:
Public Function bonjour() As Integer
On Error GoTo Handler
Range("B2").Value = 41
Cells(2, 2) = 42
ThisWorkbook.Sheets("Feuil1").Range("B2").Value = 43
ThisWorkbook.Sheets("Feuil1").Cells(2, 2) = 44
Handler:
If Err.Number <> 0 Then
Debug.Print ("Error n° " & Err.Number & " - " & Err.Description)
Err.Clear
Resume Next
End If
bonjour = 45
End Function

You cannot modify cells of the worksheet using a UDF (User Defined Function) called from the worksheet. Sorry. Excel will not allow this. Attempting it will cause an error every time.
From Microsoft Website :
A user-defined function called by a formula in a worksheet cell cannot
change the environment of Microsoft Excel. This means that such a
function cannot do any of the following:
Insert, delete, or format cells on the spreadsheet.
Change another cell's value.
Move, rename, delete, or add sheets to a workbook.
Change any of the environment options, such as calculation mode or screen views.
Add names to a workbook.
Set properties or execute most methods.
You need to find another way to call bonjour. Maybe using a button/shape or perhaps using Events.

Related

Why all these variants on correct formula?

I made a macro that combines three reports in to one.
I first find the dynamic name by looking at open workbooks to find a matching name
For Each wk In Workbooks
If Left(wk.Name, 14) = "PayrollSummary" Then
Set wbpay = Workbooks(wk.Name)
End If
If Left(wk.Name, 12) = "PunchedHours" Then
Set wbpun = Workbooks(wk.Name)
End If
Next
And from the start this line worked (ws is the report it's working on).
ws.Range("K5").Formula = "=IFERROR(VLOOKUP(A5,['" & wbpay.Name & "']payrollsummary!$B:$B,1,FALSE),""Fel"")"
Then that line started acting up and this worked:
ws.Range("K5").Formula = "=IFERROR(VLOOKUP(A5,[" & wbpay.Name & "]payrollsummary!$B:$B,1,FALSE),""Fel"")"
Now I have added a third:
On Error Resume Next
ws.Range("K5").Formula = "=IFERROR(VLOOKUP(A5,['" & wbpay.Name & "']payrollsummary!$B:$B,1,FALSE),""Fel"")"
ws.Range("K5").Formula = "=IFERROR(VLOOKUP(A5,[" & wbpay.Name & "]payrollsummary!$B:$B,1,FALSE),""Fel"")"
ws.Range("K5").Formula = "=IFERROR(VLOOKUP(A5,'[" & wbpay.Name & "]payrollsummary'!$B:$B,1,FALSE),""Fel"")"
On Error GoTo 0
Because today only the third line worked.
Here is an example of the formula in the Excel:
The workbook name will always be ParollSummary_DateFrom_DateTo_SomeRandomStuff.xlsx.
Looking at the image it seems I have accidentally downloaded the file twice (1).
But either way, I still don't see the reason why three different lines of code works randomly (my impression) with different files.
Is there any way to make sure it will always work so that I don't need to find out what will be the correct way next week?
Apologies for posting an "Answer" here but the discussion is running out of space. Let's look at your code in detail.
For Each wk In Workbooks
If Left(wk.Name, 14) = "PayrollSummary" Then
Set wbpay = Workbooks(wk.Name)
End If
If Left(wk.Name, 12) = "PunchedHours" Then
Set wbpun = Workbooks(wk.Name)
End If
Next
It's not clear why a workbook name that starts with "PayrollSummary" should be checked whether it also starts with "PunchedHours". The two are mutually exclusive. When both are found the search should stop and when one of them isn't found the rest of your macro shouldn't be executed. Either of these things could happen with your above code leading to the errors that follow later. The code below wouldn't have the faults just described.
Sub Trial()
Dim WbPay As Workbook
Dim WbPun As Workbook
If GetWorkbook(WbPay, "PayrollSummary") Then
If Not GetWorkbook(WbPun, "PunchedHours") Then Exit Sub
' continue your code here
Debug.Print WbPay.Name
Debug.Print WbPun.Name
End If
End Sub
Private Function GetWorkbook(Wb As Workbook, _
WbName As String) As Boolean
For Each Wb In Workbooks
If InStr(1, Wb.Name, WbName, vbTextCompare) = 1 Then
GetWorkbook = True
Exit For
Next Wb
End Function
Now we know that the rest of the code can't fail because one of the workbooks wasn't found. Both WbPay and WbPun actually exist and are open.
That leads to the question why we need to use a worksheet function to access them. Since all their content is accessible, why not just get it? But you want this:-
=IFERROR(VLOOKUP(A5,['ParollSummary_DateFrom_DateTo_SomeRandomStuff.xlsx']payrollsummary!$B:$B,1,FALSE),"Fel")
There are three questions attached to this demand.
In which workbook is the formula? Bear in mind that A5 is on the ActiveSheet of that workbook. What will happen if the sheet on which the formula is entered isn't active at that time? I don't know but if Excel would attempt to execute the formula in such a scenario an error must occur - probably an error 1004.
'ParollSummary_DateFrom_DateTo_SomeRandomStuff.xlsx' should be WbPay.Name. Why not use that definition? It would guarantee that the referenced workbook really exists and is open. We don't know that of 'ParollSummary_DateFrom_DateTo_SomeRandomStuff.xlsx'. In fact, the name contains a spelling error even here (lifted from your published code).
Why do you wish to return a value from columns(1) of the look-up array? That would be the same value as you have in A5. Not that this would cause an error in itself but it adds to the general confusion.
The verdict, therefore, is that your plan is high in risk and low in precision. The solution must be in reducing the risk and increasing the precision.

Excel VBA plus =hyperlink()

I'm attempting to write hyperlinks to cells via one button click from the ribbon in Excel 2007. These hyperlinks must, in turn, execute another routine that does stuff to the row on which they're located. However, the standard method of Worksheet_FollowHyperlink() will not work, as I need to execute this via an external xlam addin, and as far as I understand, this event triggers from hyperlink clicks located only in the same worksheet.
As a result, I have found that using the =HYPERLINK("#funName()","Click me") method works, but a problem exists that I am still experiencing.
The following is a trimmed down version of my already-working code:
Sub InsertLink() ' This is run directly by the callback for an IRibbonControl button click
With Sheet1
lastrow = Range("A" & Rows.count).End(xlUp).Row
While i <= lastrow
Range("E" & i).Value = "=HYPERLINK(""#consolidateDuplicate(A" & i & ",C" & i & ")"",""Copy this row to next"")"
Wend
Call MsgBox ("Finished inserting hyperlinks on each row.", vbOKOnly)
End With
End Sub
Sub consolidateDuplicate(PN, Quantity)
Dim thisRow, prevQuant As Long
thisRow = Selection.Row
MsgBox("You clicked the hyperlink for " & thisRow & "Q: " & Quantity & ", PN: " & PN)
End Sub
Clicking the ribbon button successfully writes all hyperlinks into their respective rows as expected. However, when I try to click any of these hyperlinks, thus executing consolidateDuplicate() in the same add-in module, I immediately receive a "Reference is not valid" warning followed by the MsgBox message.
I have tried removing all code from the routine to no avail. I also tried the standard brute-force bypass method of adding On Error Resume Next ... On Error Goto 0 as well as Application.DisplayAlerts = False ... Application.DisplayAlerts = True. Neither helps. I still receive the warning.
So while my script works fine and does what I want it to in the end, I feel like there should be a way to suppress the warning, or fix whatever is causing it, which I have a feeling, like Worksheet_FollowHyperlink(), is from storing the routine in the scope of an add-in module (required for my application), rather than keeping it totally contained in the calling workbook/sheet itself.
Any ideas?
Your consolidateDuplicate subroutine should be a Function, returning the address that the hyperlink should take you to. The lack of a return value is what is causing the "Reference is not valid" error.
The following code might work, effectively telling the hyperlink to take you to the cell in which the hyperlink was located:
Function consolidateDuplicate(PN, Quantity) As Range
Dim thisRow, prevQuant As Long
thisRow = Selection.Row
MsgBox ("You clicked the hyperlink for " & thisRow & "Q: " & Quantity & ", PN: " & PN)
Set consolidateDuplicate = Selection
End Function

Excel Error Handling not Stopping Sheet Select Pop-Up

I've created a function which checks if a sheet exists in an external workbook. Code as follows (I've checked this and this works perfectly with other sheets, commenting out the If statement that references this stops the error):
Function ExtSheetExists(formString) As Boolean
Dim val As Variant
On Error Resume Next
val = ExecuteExcel4Macro(formString)
ExtSheetExists = (val <> Error(2023))
On Error GoTo 0
End Function
Note: FormString is passed as "'" & wkBookRef1 & firstShtName & "'!" & "R6C12" where wkBookRef1 is just the path to the spreadsheet and firstShtName is the spreadsheet name that is being looked up.
However later when I go to update the same spreadsheet using the UpdateLink method it pops up the Select Sheet dialogue box and thus stops the run of the macro. Does anyone have an inkling as to what is going on here?
The select sheet box is as follows:
It's a bug. Effectively the formstring is run and the message box is suppressed. However it seems that it remains as a latent process in the other spreadsheet. So when it is updated it shows the suppressed message box.

Excel 2010 calculates when nothing changed

I have an Excel 2010 workbook that insists on "calculating" every time I enter, then exit a cell (blank) even when nothing is changed.
If the above workbook is open it spreads the problem to any other workbook is that is open. Problem will disappear when the above workbook is closed.
The problematic workbook contains 3 Sheets;
1 has a table that pulls external data with an SQL command,
2 has various formulas that summarise the data from Sheet 1,
3 has various graphs that use data from Sheet2.
The problematic workbook contains no macros.
I've searched Google high and low to no avail, any ideas?
As the TODAY function is considered a volatile¹ function, it isn't wise to use it repeatedly within long columns and/or rows of formulas, particularly within calculation-intensive array formulas. Calculation lag can quickly build up and the function's volatile nature will force a calculation cycle whenever anything in the entire workbook changes, not just when something that actually affects its outcome changes as is the norm with non-volatile functions.
A formula based defined name that does not actually refer to any worksheet call is a good fit here. Using something like =DATE(YEAR(TODAY()), MONTH(TODAY()), DAY(TODAY())) is counterproductive because you have not removed the volatile TODAY() function. However, VBA can write a hardcoded native worksheet function into the RefersTo: of the defined name. In this case, the current date will be constructed using the worksheets' DATE function.
The following code belongs in the ThisWorkbook code page. Tap Alt+F11 and when the VBE opens, locate ThisWorkbook in the Project Explorer. If the project explorer is not visible, tap Ctrl+R or use the pull-down menus to choose View ► Project Explorer. 
            
Double-click ThisWorkbook or right-click and choose View Code. Paste the following into the code sheet titled something like Book1 - ThisWorkbook (Code).
Private Const dnCURRENT As String = "Current"
Private Sub Workbook_Open()
'force a Workbook_SheetChange on open to check Current date
Call Workbook_SheetChange(Worksheets(1), Worksheets(1).Cells(1))
End Sub
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
On Error GoTo no_Current
With ThisWorkbook.Names(dnCURRENT)
On Error GoTo bm_Safe_Exit
If Application.Evaluate(.RefersTo) <> Date Then
Application.EnableEvents = False
.RefersTo = "=DATE(" & Year(Date) & ", " & Month(Date) & ", " & Day(Date) & ")"
End If
End With
GoTo bm_Safe_Exit
no_Current:
ThisWorkbook.Names.Add Name:=dnCURRENT, _
RefersTo:="=DATE(" & Year(Date) & ", " & Month(Date) & ", " & Day(Date) & ")", _
Visible:=True
Resume
bm_Safe_Exit:
If CBool(Err.Number) Then _
Debug.Print Err.Number & ":" & Err.Description
Application.EnableEvents = True
End Sub
If you wish to change the name of the defined name, do so in the private constant variable declaration at the top. Tap Alt+Q to return to your worksheet(s).
The first time that any change is made on any worksheet, a new defined name with workbook scope will be created called Current. It will have a RefersTo: with a hardcoded DATE function that returns the current system date (e.g. =DATE(2015, 10, 14)). If the current date is not equal to the defined names value returned by that formula, a new formula is written using the current date and this forces a new workbook-wide calculation cycle on all formulas that reference the defined name.
The defined name Current is a direct replacement for anywhere that the TODAY() function can be used. In practise, the formula(s) involving Current will only be recalculated once per day. The following image demonstrates how it will appear as an option as you start to type it into a formula.
           
¹ Volatile functions recalculate whenever anything in the entire workbook changes, not just when something that affects their outcome changes. Examples of volatile functions are INDIRECT, OFFSET, TODAY, NOW, RAND and RANDBETWEEN. Some sub-functions of the CELL and INFO worksheet functions will make them volatile as well.

Error 1004 for range based cell copy

I've looked through countless examples (and there are many to be found) of how to use Ranges to accomplish a VBA user defined function/sub to copy the value of one specified cell to another. Unfortunately, no matter which I try, I am unable to avoid Error 1004 Application-defined or object-defined error. Here is the very simple test code:
Private Sub Foobar()
On Error GoTo fooErrorHandler
Dim c1 As Range
Dim c2 As Range
Set c1 = Sheets("Sheet1").Range("D2")
Set c2 = Sheets("Sheet1").Range("B3")
c2.Value = c1.Value
Exit Sub
fooErrorHandler:
MsgBox "Error Number: " & Err.Number & vbNewLine _
& "Description: " & Err.Description
End Sub
Thanks for any help/pointers!
Generally speaking you cannot use a Function to manipulate the worksheet object. It looks like you're trying to get around that by invoking a subroutine from the barfoo function call. I suspect that is the error... If you run the subroutine foobar manually (press F5 or run from the macro menu) it should execute without error.
I confirm that this implementation raises the error, and also test the manual call to foobar without error.
If you can better describe the ultimate goal, perhaps we can recommend a more reliable way to achieve it.
Modifying cells inside UDF (a function, that is used inside a cell the same way as built-in formulas) is not allowed. If you remove a call to barfoo from C1 cell, both Foobar and barfoo should work without problems.

Resources