I want to convert a string into a range variable that I will use somewhere else. My first idea was to create a text-to-range function and to avoid dealing with the sintaxis ever again. I know it is probably a very basic question but I couldn't figure it.
My first attempt was this. It prints "indirect" were I want to (this was just a test), but running the macro step by step I see that there is an error 91 at the time to "exit" the function.
Dim rng As Range
rng = TXT2RNG(Range("A1").Value)
'This is the function, located in Modulo1
'Function TXT2RNG(text As String) As Range
'Set TXT2RNG = Range(text)
'TXT2RNG.Value = "indirect"
'End Function
End Sub
I have attempted the same but without the function, and it works as I expected.
Dim rng As Range
Set rng = Range(Range("A2").Value)
'Set rng = Range(Range("A1").Value)
rng.Value = "direct"
End Sub
Summary: The second code works as a workaround but I want to know why the first one doesn't so can learn from it and use similar structures in the future. Thank you
Basically, you are simply missing a Set when assigning the result from the function to your variable - you do it correct in your direct example.
Whenever you are dealing with objects (eg worksheet, range), you need to use Set when assigning it to a variable. A good explanation can be found at https://stackoverflow.com/a/18928737/7599798
Omitting the Set will cause an error 91 when assigning it to an object variable. However, if you would declare your rng-variable as Variant, you wouldn't get a runtime error. Instead, VBA would use the so called default property, for a Range this is the Value, so you would end up having the content of the Range in your variable ("indirect" in your example). This is the reason to use the data type Variant only if really needed.
That said, there are at least 2 issues you should take care about:
when you use the Range-function as you do, it refers to ActiveSheet, which is the sheet that currently has the focus. When coding, that's not always what you want, so think about if you need to approve your function. You should really take the time to read the answers of How to avoid using Select in Excel VBA to get an understanding.
You should think about what should happen when the text you pass to your function doesn't contain a valid range-address. Currently, you would get a runtime error (1004). Error handling in VBA is done with On Error-statements. You should avoid On Error Resume Next.
You could change your function to:
Function TXT2RNG(ws as Worksheet, text As String) As Range
On Error Goto InvalidRange
Set TXT2RNG = ws.Range(text)
' TXT2RNG.Value = "indirect"
Exit Function
InvalidRange:
' Think about what to do here, show a message, simply ignore it...
Set TXT2RNG = Nothing
End Function
And the call to it would be
Dim rng as Range, address as string
address = Range("A1").Value
Set rng = TXT2RNG(activeSheet, address)
if not rng is Nothing then
(...)
Welcome to stack overflow.
From walking through your code, you're not declaring "text" as anything.
If you use the "Watches" feature, you can see it's a blank string
I believe you need to have a function that pulls the range, then a second function to pull the string of that. A Private Sub is much better
See this answer https://stackoverflow.com/a/2913690/2463166
Related
I am having trouble with losing static variables within an Excel user form.
I have been working on a routine for excel. I am a (very) novice coder.
I am attempting to populate a cell range to an array. I have been able to do this without issue.
However, when I attempt to store the array as a *static * variable, the variable is not retained for as long as I want it to be.
I think the problem occurs when another page is selected in the multipage, the static variable is cleared.
Code is something like this:
Sub UserForm_Initialize ()
static myArray () as variant
dim myRange as range
set myRange = [namedrange]
myArray=myRange
msgbox myArray(0,0) 'this works fine
call OtherSub
end sub
sub OtherSub ()
msgbox myArray(0,0) 'this returns a blank
end sub
The first sub of code shows the array element just fine. The array element is blank in the second sub.
I have tried:
Declaring the array as a public variable, but this returns an error (I think that variables within user forms are private by default and cannot be changed).
using a very small variable (a simple string)
writing code in a module before opening the user form (variable is not retained).
I am aware that I can just write data to a cell range, but this would defeat the purpose. I was hoping to avoid multiple instances of reading large arrays from the worksheet.
This might explain it a bit clearer. Moving MyArray outside of the Procedure will set it's scope to a Module Level, making it usable through other subs within that module. You will generally want to keep the scope of your variables to the lowest level required. The other option would be to pass your variable as a parameter to your other procedure.
Option Explicit
Dim MyArray() As Variant ' Private Module Level Scope
Public ExampleVariable As String ' Public Module Level Scope (Not required, just showing an example.)
Sub UserForm_Initialize()
Dim myRange As Range ' Procedure Level Scope
Set myRange = [namedrange]
MyArray = myRange
MsgBox MyArray(0, 0) 'this works fine
Call OtherSub
End Sub
Sub OtherSub()
MsgBox MyArray(0, 0) 'this returns a blank
End Sub
I have looked through the similar questions and cannot find one that quite matches my issue: I have a UDF that I pass arguments to to retrieve data. The arguments can be simple strings (e.g. "vapour_mass_flow"), simple cell references (e.g. "C4"), or complex cell formulae (e.g. "CHOOSE($B$8, "overall", "vapour", "liquid") & _mass_flow").
I have a 'helper' function that needs to be able to resolve the argument value from parsing the arguments. I have a working parser that can extract the relevant arguments as a String() array, but I am struggling with how to get VBA to 'resolve' the complex arguments to a string value.
The 'helper' function is essentially a navigation button, that is looking to find a call to the UDF somewhere in a worksheet, and detect which call is a match on one of the arguments.
I have tried using Worksheet.Evaluate(argument) using the Worksheet object that the UDF is being called from (in any given instance). For clarity, I have:
Private Function resolveCellRef(CellRef As String, cell As Range) As String
' CellRef is the argument to the UDF as a String, cell is the calling cell in the sheet
Dim ws As Worksheet
On Error GoTo errhandler ' Use error handling to handle two known issues
Set ws = cell.Parent
resolveCellRef = ws.Evaluate(CellRef)
If error_raised Then resolveCellRef = ActiveWorkbook.Names(CellRef).RefersToRange.Value2
Exit Function
errhandler:
If Err.Number = 1004 And Not error_raised Then ' Set the error_raised flag and resume next to try a Named Range
Err.Clear: error_raised = True
Resume Next
ElseIf Err.Number = 1004 And error_raised Then ' If we get here, then the cellRef isn't a cell reference or Named Range, so just return it as is
Err.Clear
resolveCellRef = CellRef
Resume Next
End If
End Function
This code successfully resolves the argument CellRef to whatever value it contains if:
It is a simple string already (obviously)
It simply points to a cell on the same or another sheet
It is a Named Range (the error handling for this may be unnecessary now, but I originally was not using the Evaluate function and needed to check for Named Ranges in this way).
The code above does not work for my example where CellRef is equal to:
"CHOOSE($B$8, "overall", "vapour", "liquid") & _mass_flow"
Instead, the function resolves to a null string. Any thoughts on what I might be doing wrong, or whether there is a fundamental limitation here I am unaware of?
Thanks.
Turns out I had 'cleaned' my complex argument strings previously - I have left the original message unedited so you can see the text I had that was failing, but the correctly parsed argument should have read (note the " mark before _mass_flow that was missing in my original!):
"CHOOSE($B$8, "overall", "vapour", "liquid") & "_mass_flow"
So my approach was right all along, and the error handler is all not needed as well, it turns out. The Worksheet.Evaluate function handles it all for you.
I have recently Written some code to take an input from a userform text box and search for it in my database. If found I would like it to return the value and insert it into cell A1149; I have written the below code but it gives me error #424 "Object Required". I am very new to VBA so any and all help is greatly appreciated.
Private Sub CMDSearch_Click()
Dim pesquisa As Range
Set pesquisa = Worksheets("Petrobras").Activate.Range("$W:$W") _
.Find(What:=Opp_Num_Search.Value, LookIn:=xlValues, Lookat:=xlWhole).Activate
Worksheets("Petrobras").Range(A1149).Value = pesquisa.Value
UserForm1.Hide
End Sub
Range.Activate doesn't return anything, it's like a Sub procedure:
Public Sub DoSomething()
' does something...
End Sub
If you did Set foo = DoSomething.Something, you'd get the same error: an object is required, otherwise that .Something member call is illegal (except now the error would be at compile-time and not run-time, because of how binding works).
Set pesquisa = Worksheets("Petrobras").Activate...
You don't want to Activate any sheets.
Part of the problem is the implicit late-binding going on: Worksheets returns an Object, so everything you wrote after that, all these chained member calls, can only be resolved at run-time.
Make it early-bound, by declaring an explicit Worksheet variable:
Dim sheet As Worksheet
Set sheet = Worksheets("Petrobras")
Now if you want to activate it, you can do sheet.Activate (but you don't need to). And if you want to get a Range from that worksheet, you can make a .Range member call, and the IDE will now help you do it:
Dim result As Range
Set result = sheet.Range("$W:$W").Find(...)
NEVER chain any member calls to what Range.Find returns. If the search turned up a result, you have a Range object. If it didn't, you have Nothing - and any member call made against Nothing will always raise run-time error 91.
Validate the search result first:
If result Is Nothing Then
MsgBox "Could not find '" & Opp_Num_Search.Value & "' in column W."
Exit Sub
End If
Or:
If Not result Is Nothing Then
sheet.Range("A1149").Value = result.Value
End If
Note that A1149 is a string literal representing a cell address, and as such it must be surrounded with double quotes ("). If it's not in double quotes and it looks like a valid variable name, VBA will treat it as ..a variable... and that will cause yet another error (1004), because Range will be rather unhappy to work with a Variant/Empty value.
To prevent VBA from "declaring" on-the-fly variables with a typo (and causing hard-to-find bugs), make sure you have Option Explicit at the very top of every module in your project.
One last thing:
UserForm1.Hide
This hides the default instance of UserForm1, which may or may not be the current object / instance that's shown - and the form itself has no way to know how it was shown:
UserForm1.Show '<~ shows the default instance
With New UserForm1
.Show '<~ shows a new (non-default) instance
End With
For this reason, you should avoid referring to the default instance in a form's code-behind. Use Me instead:
Me.Hide
That way you're referring to whatever instance of the form is currently shown, whether that's the default instance or not. See UserForm1.Show for more information, tips, pitfalls and common mistakes involving userforms.
Note that Rubberduck has quite a few inspections that can identify and warn you about (and often, automatically fix) most of these problems. Rubberduck is a free and open-source VBIDE add-in project I manage.
In my excel sheet, I have a cell called su_callLog.
My VBA code is:
Private Sub su_callLogCopy_Click()
CopyCell (Range("su_callLog")) 'Error Here
End Sub
Sub CopyCell(cell As Range)
MsgBox cell.Value
End Sub
I'm trying to use the function to process the info in that cell. Even when i try to merely print the contents of the Range, I get the VBA Run-time error '424' Object Required error. I'm new to VBA. Please tell me what I'm doing wrong.
NOTE: MsgBox (Range("su_callLog")) produces the expected results.
Remove the parentheses:
CopyCell Range("su_callLog")
If you use parentheses, you are forcing that the arguments are taken ByVal. And the Range() is an object type, which is passed ByRef.
ByVal vs ByRef VBA
As #Vityata correctly pointed oute, you're forcing ByVal to be
taken, while you need to be passing a reference to a range.
While that much is true and CopyCell Range("su_callLog") will produce the desired result, there is one more way to achieve this:
in which you implicity reference the Range correctly while in parentheses
so in other words, you (knowingly or unknowingly) pass the reference properly.
CopyCell (Sheets("Sheet1").Range("A1"))
Not only you maintain your desired (although unnecessary) braces syntax, but it actually works. Additionally it's a good VBA coding practice to be using a specific Sheet every time you're working with a Range object
So to speak, you sort of kill two birds with one stone here
I have a function that finds any bold words within a specific range. This function is called in the for each loop below. The way it's currently set up it returns true if bold words exist anywhere in the range, or false if no bold words exist in the range. What I want to do is get it so it stops when it finds the bold words so I can do some other actions, then continue on down the list. I was thinking of doing something along these lines, however it throws an error on the second line
dim i as range
set i = range("C11:C6000")
for each i in range("C11:C6000")
FindBoldCharacters(Range(i)) 'throws "run time error 1004 application or object defined error" here
if i = true then
'do some stuff here'
else
end if
next i
here is the function that the piece of code is calling
Function FindBoldCharacters(ByVal aCell As Range) As Boolean
FindBoldCharacters = IsNull(aCell.Font.Bold)
If Not FindBoldCharacters Then FindBoldCharacters = aCell.Font.Bold
End Function
The function works when just called by itself, but doesn't work when I try to call it in this way. What can I do to solve this problem?
As mentioned in my comment there are a couple of things off with your code as is.
The variable i, if undeclared prior to this loop, will be dimensioned at run time as a Range object, not as a string holding the cell address as you appear to be using it as
Related, i is later being used as a Boolean when it is a Range object
Each For.. should only have one Next, your code will throw a Next without For error if you try to run it
First, declare i as a Range object, and then pass it into your function like so FindBoldCharacters(i) (this makes sense as in your function declaration you say you are expecting aCell as a Range)
Second, evaluate the function directly in the If statement
Finally, move the Next i from inside the If statement outside.
Something like this:
Option Explicit
Sub Test()
Dim i As Range
For Each i In Range("C11:C6000")
If FindBoldCharacters(i) = True Then
'do some stuff here'
Else
End If
Next i
End Sub