Simple Excel VB Cell value change - Error #1004: Application-defind or object-defined error - vba

i've been trying to fix this error for hours now and i can't seem to see what it is that i am doing wrong here.
My macro is as it follows:
Function testValueChange(cell As Range)
On Error GoTo ErrorHandler
Dim testValue As String
testValue = Range(cell.Address)
MsgBox testValue
Range("Q2") = "new value"
Exit Function
ErrorHandler:
MsgBox "Error #" & Err & " : " & Error(Err)
End Function
I am able to retrieve the "testValue" correctly and it is displayed on the MsgBox. However, when i try to set a new value to the cell "Q2", i get the error "Error #1004: Application-defined or object-defined error."
There are several entries for that error over the web but none of them in this situation when i'm trying to simply set a String value to an empty cell!
The cell "Q2" is empty, i've chosen that cell specifically because it is not merged, nor referenced on another function or anything.
Thanks in advance for all the help!

I'm on Linux OS right now so I can't test this in Excel, but try setting a range object first then assigning the value with .value property. i.e:
Dim RNG as Range
Set RNG = Range("Q2")
RNG.value = "new value"

If you not interested in a return then shahkalpesh has the answer, but if your interested in monitoring the macro silently when run from somewhere else then return boolean like so.
Function testValueChange(cell As Range) as boolean
TestValueChange = TRUE
On Error GoTo ErrorHandler
Dim testValue As String
testValue = Range(cell.Address)
MsgBox testValue
Range("Q2") = "new value"
Exit Function
ErrorHandler:
testValueChange = FALSE
End Function
Usage in a vba sub.
If testValueChange then
'process worked
Else
'process didn't work
End if
Unless testvalue was ment to be testValueChange and returned as a string?

You cannot assign values to cells, or change them in any manner, when calling from a Function (or from a call chain that has started by a Function). Only a Sub will work. The only exception is that function value can be assigned to the cell containing the function reference

Related

Excel VBA - Passing argument to a function

I attempted to create an Excel function that will bold whatever range I tell it to in whatever form I request. Unfortunately, I've only had partial success in correctly passing the variable and obtaining this outcome. Of course, nobody likes a partial so can someone please let me know what I'm missing.
Sub Macro1()
On Error Resume Next
'Create & reset testing area.
Range("A1:C6").value = "A"
Range("A1:C6").Font.Bold = False
[b2].Select
'The two lines below call the function perfectly and the cells are bolded without issue
Text_bold ([a1])
Text_bold (Cells(2, 1))
'However, the party stops there as the following code errors out.
Text_bold ([b1].Address)
Text_bold (Selection)
Text_bold (Range("B3"))
'Similarly, the below fails as well...
Text_bold (Range("B4:C4"))
'And even less surprising, the following also refuses to assist in the endeavor...
Text_bold (Application.Union(Range("B5:C5"), Range("B6:C6")))
End Sub
Function Text_bold(x As Range)
'MsgBox VarType(x)
x.Font.Bold = True
End Function
Please help.
The parentheses around your function parameters are causing the problem. They are forcing the enclosed value to be evaluated before being passed as the function parameter, passing a Range.Value instead of Range object.
Sub Macro1()
On Error Resume Next
'Create & reset testing area.
Range("A1:C6").Value = "A"
Range("A1:C6").Font.Bold = False
[b2].Select
'The two lines below call the function perfectly and the cells are bolded without issue
Text_bold [a1]
Text_bold Cells(2, 1)
'However, the party stops there as the following code errors out.
Text_bold Range([C1].Address)
Text_bold Selection.Range
Text_bold Range("B3")
'Similarly, the below fails as well...
Text_bold Range("B4:C4")
'And even less surprising, the following also refuses to assist in the endeavor...
Text_bold Application.Union(Range("B5:C5"), Range("B6:C6"))
MsgBox "OK"
End Sub
If you really want to use parentheses, prefix your function with Call statement.
Call Text_bold(Application.Union(Range("B5:C5"), Range("B6:C6")))
In order to get more details about the issue you need to remove the statement
On Error Resume Next (aka On Error Hide All Bugs)
After I removed it I was able to determine the problems
The function (which should be a Sub because it doesn't return a value) is expecting a Range object: Text_bold(x As Range)
the line Text_bold ([b1].Address) is calling it incorrectly with parenthesis, and it is attempting to send as argument a string, not a range
all your calls to the function should be without brackets
Try this:
Sub Macro1()
'Create & reset testing area.
Range("A1:C6").Value = "A"
Range("A1:C6").Font.Bold = False
[b2].Select
Text_bold [a1]
Text_bold Cells(2, 1)
Text_bold [b1]
Text_bold Selection
Text_bold Range("B3")
Text_bold Range("B4:C4")
Text_bold Application.Union(Range("B5:C5"), Range("B6:C6"))
'A sub cannot return a value, a function can but it doesn't have to
'To return a value from the Text_bold function
Dim functionResponse As Boolean
functionResponse = Text_bold([B3]) '<- THIS is where you need to use brackets
MsgBox "Text in Cell [B3] is bold: " & functionResponse
End Sub
Function Text_bold(x As Range) As Boolean
x.Font.Bold = True
Text_bold = (x.Font.Bold = True) 'assign the return value to the function name
End Function

Edge cases in IsNumeric- is this overthinking it

I have code which looks like this:
Select Case IsNumeric(somevariable)
Case True
Resume Next
Case False
Call notnum
Else
Call MyErrorHandler
End Select
Is this overthinking it? Is there a chance IsNumeric will return something other than True or False here or is this bad programming practice?
Don't need the else as it will be true or false however, just a note the Else should be Case Else (moot point though as you are about to delete it)
Based on this though I wouldn't use a case for only 2 options:
If IsNumeric(somevariable) then
Resume Next
Else
Call MyErrorHandler
End if
Edit: Here is how error checking works:
Sub SheetError()
Dim MySheet As String
On Error GoTo ErrorCheck
MySheet = ActiveSheet.name
Sheets.Add
ActiveSheet.name = MySheet
MsgBox "I continued the code"
ActiveSheet.name = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
MsgBox "I will never get to here in the code"
End
ErrorCheck:
If Err.Description = "Cannot rename a sheet to the same name as another sheet, a referenced object library or a workbook referenced by Visual Basic." Then
Resume Next
Else
MsgBox "Error I am not designed to deal with"
End If
End Sub
Copy and paste this module to your personal workbook or to a new workbook and run it, step through line by line using F8 to see how it is actually dealing with the error.
From OP's comment I'm not using my error handler. I want to do stuff with the hopefully numeric output
Sub demo()
Dim inputs As Variant
inputs = InputBox("Prompt", "Title", "Default")
If Not IsNumeric(inputs) Then
notnum
Else
' Do what you want with numeric input inside the Else
End If
' Maybe do more stuff irrespective of input
End Sub
Sub notnum()
' do not numeric stuff here
End Sub
Or if you want to keep prompting for numeric input until the users gets it right or cancels
Sub demo2()
Dim inputs As Variant
Do
inputs = InputBox("Enter something Numeric", "Title", "Default")
Loop Until IsNumeric(inputs) Or inputs = vbNullString
If Not inputs = vbNullString Then
' Do wht you want with numeric input inside the Else
End If
' Maybe do more stuff irrespective of input
End Sub
Input box can have different type of input validation. Try this
something = Application.InputBox("Pls Insert the Number", Type:=1)
If something = False Then Exit Sub
'Type:=0 A formula
'Type:=1 A number
'Type:=2 Text (a string)
'Type:=4 A logical value (True or False)
'Type:=8 A cell reference, as a Range object
'Type:=16 An error value, such as #N/A
'Type:=64 An array of values

How to check the availability of a worksheet

I have to run a set of code related to worksheet "wins", but only if that worksheet exist.
Please share a code to check the availability of sheet "wins". If worksheet "wins" exist, then only I want to run that set of code, else I want to skip executing that set of code and move to next line of code.
You could use On Error Resume Next to skip the errror which occurs if you try access a not existing worksheet and assigning it to a object variable. So if the worksheet does not exist, no error occurs but the variable is Nothing. If the worksheet exists, then the variable is not Nothing.
Example:
Sub test()
Dim wsWins As Worksheet
On Error Resume Next
Set wsWins = ActiveWorkbook.Worksheets("wins")
On Error GoTo 0
If Not wsWins Is Nothing Then
MsgBox "Worksheet wins exists."
Else
MsgBox "Worksheet wins does not exist."
End If
End Sub
Axel's answer will work nicely. Some people prefer not to use error throwing to test if something exists. If you're one of them then I use the following quite a lot in a Utility module. It'll work for Worksheets, Charts, etc. (basically anything that's a collection with a 'Name' property):
Public Function ExcelObjectExists(testName As String, excelCollection As Object) As Boolean
Dim item As Object
On Error GoTo InvalidObject
For Each item In excelCollection
If item.Name = testName Then
ExcelObjectExists = True
Exit Function
End If
Next
ExcelObjectExists = False
Exit Function
InvalidObject:
MsgBox "Developer error: invalid collection object passed in ExcelObjectExists."
ExcelObjectExists = False
End Function
You can call it like this:
If ExcelObjectExists("wins", ThisWorkbook.Worksheets) Then

VBA Error 1004 When Calling A Module with an Argument

This is my first post so bear with me.
I get a run-time error 1004 when I try calling a module from my user form and passing on an argument. I'm sure the answer is pretty obvious but I'm new to passing on arguments.
From User Form when clicking submit button:
Sub SubmitButton_Click()
Dim addRowValue As Integer
addRowValue = LineBox.Value
MsgBox "Add " & addRowValue & " rows."
Call Sheet1.ResizeTable(addRowValue)
End Sub
From Sheet1:
Sub ResizeTable(addRowValue As Integer)
Dim rng As Range
Dim tbl As ListObject
Set tbl = ActiveSheet.ListObjects("DATA_INPUT")
Set rng = Range("DATA_INPUT[#All]").Resize(tbl.Range.Rows.Count + _
addRowValue, tbl.Range.Columns.Count)
tbl.Resize rng
End Sub
Call Sheet1.ResizeTable works fine but when I add the argument is when I get the error. Also, the module ResizeTable() works fine when I change the variable addRowValue to a set number and run it.
Thanks for any help!
Problem is you are assigning a string to variable of type integer.
Change
addRowValue = LineBox.Value
To
addRowValue = CInt(LineBox.Value)
EDIT: You might also want to ensure the user enters a numeric value so have something like:
If IsNumeric(LineBox.Value) Then
addRowValue = CInt(LineBox.Value)
Else
MsgBox "Please enter numeric value", vbCritical
LineBox.Value = ""
End If

VBA: What happens to Range objects if user deletes cells?

Suppose I have some module in vba with some variable r of type Range. Suppose that, at some point, I store a Range object there (e.g. the active cell). Now my question: What happens to the value of r if the user deletes the cell (the cell, not only its value)?
I tried to figure this out in VBA, but without success. The result is strange. r is not Nothing, the value of r is reported to be of type Range, but if I try to look at its properties in the debugger window, each property's value is reported as "object required".
How can I, programmatically, determine whether variable r is in this state or not?
Can I do this without generating an error and catching it?
Nice question! I've never thought about this before, but this function will, I think, identify a range that was initialzed - is not Nothing - but is now in the "Object Required" state because its cells were deleted:
Function RangeWasDeclaredAndEntirelyDeleted(r As Range) As Boolean
Dim TestAddress As String
If r Is Nothing Then
Exit Function
End If
On Error Resume Next
TestAddress = r.Address
If Err.Number = 424 Then 'object required
RangeWasDeclaredAndEntirelyDeleted = True
End If
End Function
You can test is like this:
Sub test()
Dim r As Range
Debug.Print RangeWasDeclaredAndEntirelyDeleted(r)
Set r = ActiveSheet.Range("A1")
Debug.Print RangeWasDeclaredAndEntirelyDeleted(r)
r.EntireRow.Delete
Debug.Print RangeWasDeclaredAndEntirelyDeleted(r)
End Sub
I believe that when you use the Set keyword in VBA, it creates a pointer in the background to the worksheet's Range object in the worksheet you specified (each cell being an object in the collection of Cells of the Worksheet for a given Range). When the range is deleted while you are still referencing it in memory, the memory for the object that the Range variable was pointing to has been deallocated.
However, your Range variable most-likely still contains the pointer to the recently removed Range object, which is why it isn't nothing, but whatever it's pointing to doesn't exist anymore, which causes problems when you try to use the variable again.
Check out this code to see what I mean:
Public Sub test2()
Dim r As Excel.Range
Debug.Print ObjPtr(r) ' 0
Set r = ActiveSheet.Range("A1")
Debug.Print ObjPtr(r) ' some address
r.Value = "Hello"
r.Delete
Debug.Print ObjPtr(r) ' same address as before
End Sub
Check out this article for more info about ObjPtr():
http://support.microsoft.com/kb/199824
So while you have a valid address to an object, unfortunately the object doesn't exist anymore since it has been deleted. And it appears that "Is Nothing" just checks for an address in the pointer (which I think VBA believes that the variable is "Set").
As to how to get around this problem, unfortunately I don't see a clean way of doing it at the moment (if anyone does find an elegant way to handle this, please post it!). You can use On Error Resume Next like so:
Public Sub test3()
Dim r As Excel.Range
Debug.Print ObjPtr(r) ' 0
Set r = ActiveSheet.Range("A1")
Debug.Print ObjPtr(r) ' some address
r.Value = "Hello"
r.Delete
Debug.Print ObjPtr(r) ' same address as before
On Error Resume Next
Debug.Print r.Value
If (Err.Number <> 0) Then
Debug.Print "We have a problem here..."; Err.Number; Err.Description
End If
On Error GoTo 0
End Sub
How can I, programmatically, determine whether variable r is in this
state or not?
Can I do this without generating an error and catching it?
No.
To the best of my knowledge, you can't test for this condition reliably: not without raising and catching an error.
Your question has been noticed and discussed elsewhere: Two of the big names in Excel/VBA blogging (Dick Kusleika and Rob Bovey) have looked into it, and you may find something informative in there. But the answer's No.
All in all, a good question with rather worrying answer.
To test if a range object is currently invalid, I use this function:
Public Function InvalidRangeReference(r As Range) As Boolean
On Error Resume Next
If r.Count = 0 Then
InvalidRangeReference = Err
End If
End Function