VBA automatically changing .Range to .range - vba

I have been writing an Excel macro to help me process data at work, and now that I have finished writing the code I keep getting errors because Microsoft Visual Basic keeps changing .Range to .range. Whenever it does this I get a compile error because the method doesn't exist.
Is there anyway to fix this? Is there a way to get around using .Range if there isn't? As long as my code keeps getting changed from .Range to .range it will keep spitting out errors here.
SOLVED: the error wasn't rooted in the method but the data member that preceded it.

Try declaring Range as a Range somewhere in your code (note the case):
Dim Range As Range
then delete the statement.
This should convert all your range to Range

EDIT: The O.P. stated:
SOLVED: the error wasn't rooted in the method but the data member that preceded it.
However, the related issues of a lowercase method can come from creating a variable or routine which you named range and the system will auto change case based on that definition. You should never create a variable or routine with the same name as a defined process like Range().
As mentioned by #RubberDuck:
This is a side effect of VB being case insensitive and the IDE trying to be "helpful".

I have several models and procedures, and at some point all of my Ranges got converted to lowercase range. My easy fix was to go to ThisWorkbook in my project, and Dim Range as Range there, and then it will automatically change "range" to "Range" everywhere it appears in your code

When you have use many times words like 'range', autocorrect will change 'Range' value to 'range'. Try manually change this method to 'Range' and sure that you don't have any variables like 'range'.

Related

Application.Cells VS Application.ActiveSheet.Cells

The Macro Recorder generated the following statement:
Cells.Select
Now I understand that without the object qualifier this will return all the cells as a Range object.
However, I am wondering what the fully qualified version of this statement is?
Is it:
Application.Cells.Select
Application.ActiveSheet.Cells
Application.ActiveWorkbook.ActiveSheet.Cells
In other words, which one of those fully qualified statements is actually executed by VBE when it runs Cells.Select?
What is the difference between all of these??? As all of these access the same object in the end - is it just personal preference as to which statement I would use if I wanted to explicitly qualify all the objects?
Thank you so much!
It's complicated :)
As all of these access the same object in the end
True. Keywords "in the end". The difference is how many steps it takes to get there...
Unqualified Cells (or Range, Rows, Columns, Names, etc.) aren't magic, they're member calls (Property Get) against a hidden, global-scope object cleverly named Global:
You can validate that this hidden object is involved, by blowing up in a standard module:
Sub GoesBoom()
'throws error 1004 "Method 'Range' of object '_Global' failed"
Debug.Print Range(Sheet2.Cells(1, 1), Sheet3.Cells(1, 1))
End Sub
_Global and Global are closely related - without diving deep into COM, you can consider Global the class, and _Global its interface (it's not really quite like that though - look into "COM coClasses" for more information).
But Cells is a property of the Range class:
I think it's reasonable to presume that Global calls are pretty much all redirected to Application, which exposes all members of Global, and then some.
Now as you noted, Application also have a Cells property - but Cells belong on a Worksheet, so no matter what we do, we need to end up with a Worksheet qualifier... and then any worksheet belongs in a Worksheets collection, which belongs in a Workbook object - so we can infer that an unqualified Cells call would be, in fully-explicit notation, equivalent to... (drumroll):
Application.ActiveWorkbook.ActiveSheet.Cells
But you don't need to be that explicit, because ActiveSheet has a Parent that is always going to be the ActiveWorkbook, so this is also explicit, without going overboard:
ActiveSheet.Cells
But that's all assuming global context. This answer explains everything about it - the gist of it, is that if you're in a worksheet's code-behind, then an unqualified Cells member call isn't Global.Cells, but Me.Cells.
Now, note that Cells returns a Range. Thus, whenever you invoke it against a Range without providing parameters, you're making a redundant member call:
ActiveSheet.Range("A1:B10").Cells ' parameterless Range.Cells is redundant
Let's take the post apart:
Cells.Select
Now I understand that without the object qualifier this will return
all the cells as a Range object.
That's actually somewhat incorrect. While it is true that .Cells returns a Range.Cells object which returns all the cells, Cells.Select is actually a method of the Range object which - as you may have guessed - Selects the range (in our case, all the cells)
The Select method, as per MSDN actually returns a Variant and not a Range object.
That it is a pretty important distinction to make, especially if you plan on passing that value to anything. So if we pretended to be a compiler
Cells -> ActiveWorkbook.ActiveSheet.Range.Cells returns Range of all the cells
Range.Cells.Select -> first we take our returned Range, we then select the cells in Worksheet and actually return a Variant (not Range)
As to the other part of the question. It depends where your module is placed. By default, Cells is shorthand for the following statement:
Application.ActiveWorkbook.ActiveSheet.Range.Cells
This however is subject to change depending on where your module is placed and if Application, workbook or sheet has been modified.
In general, it is a good coding practice to always specify at least a specific Worksheet object whenever you're referencing a Range, eg.
Sheets("Sheet1").Range.Cells
This is explicit and therefore less error prone and clearer to comprehend, be it for you or anyone working with your code.. You always know what exactly you're working with and not leave it to guesswork.
Obviously, the moment you start working with multiple workbooks, it's a good idea to incorporate Workbook objects statements before the Sheet. You get my point.
Last but not least, whatever you're trying to do, it's probably for the best you avoid using Select. It's generally not worth it and prone to unexpected behaviour.
Check this question here: How to avoid using Select in Excel VBA
If you just type Cells - in and of itself it does nothing. It is the same as Range.Cells. The only advantage of Cells is that it can accept numeric value for column (second argument). It's very handy when you do complex manipulations.
Range.Cells just returns Range object. When you have Range object, think of it as a small Excel worksheet. Say, you have range Range("F3:J10"). Then following ranges all refer to H3 cell:
Range("F3:J10").Cells(3)
Range("F3:J10")(3)
Range("F3:J10").Cells(1, 3)
Range("F3:J10")(1, 3)
Range("F3:J10").Cells(1, "C")
Range("F3:J10")(1, "C")
Range("F3:J10").Range("C1")

Trouble with undefined Object in VBA Run-Time error '91'

I've been working all week to prepare a VBA application, which I'll be using in a meeting today. Unfortunately the code that has been running all week last week without a hitch, has decided to break over the weekend.
I constantly get Object variable or With block variable not set Run-time error '91' from this statement:
With Sheet5
Set adjrng = .Range(.Cells(.Range("G43:G60").Find(.Range("H39").Value).Row, 10), .Cells(.Range("G43:G60").Find(.Range("H39").Value).Row, 21))
End With
idea is to set a range in the row of the Range G43:G60 where the Value of H39 matches from Column 10 to Column 21.
Anybody spot the issue? My brainz are to nervous and sleepy this morning...
Thanks a bunch
Ben
EDIT:
After playing a bit with find and replace, the issue seems to be that excel has not yet properly calculated the "lookin" and "lookup" Ranges G43:G60 and H39. A simple recalculation didn't make excel rediscover the contents but when I used one of my input toggles to display a different value in those cells, and the went back to the original it did manage to find it.
Maybe using find for this is bad style, the find formula has these kind of hicups usually or any other comments on this? For now everything works fine again, but I'm afraid of running into these issues again. Any tips would thus still be appreciated.
EDIT: (from comment below)
We have a dynamic range (G43:J60) where unique identifiers are listed in column G and data is to the right. if something is changed in the data part of the range AND the lines uniqued identifier in column G matches the one in cell H39 a sub() is triggered via worksheet_ change intersect(target, adjrng) Defining that adjrng is the part that throws errors when find returns null.
I believe you are simply trying to set a range hoping that there will be two matches to the value in H39 within the G43:G60 range. While I avoid on Error Resume Next (never could adjust to the logic of breaking something in the hope to accomplish something), I always check that the values will be there when I look for them.
Dim rwUNIQ as long
Set adjrng = nothing
With Sheet5
if cbool(application.countif(.Range("G43:G60"), .Range("H39").Value)) then
rwUNIQ = application.match(.Range("H39").Value, .Range("G43:G60"), 0)
Set adjrng = .Cells(42 + rwUNIQ, 10).resize(1, 11)
end if
if not adjrng is nothing then
'do something with adjrng
end if
Set adjrng = nothing
End With
That checks to make sure that there are at least two H39 values in G43:G60 before proceeding. There is no further error control because we've counted at least two of them. You might want to compensate with an Else for when there isn't. If a single H39 value was found, you also might want to select a single row.
Remember that the .Find uses many parameters that were retained from the last time Find was used, whether in VBA or with a user on the worksheet. You have a real lack of parameters that specify the options that Find should use to proceed. e.g. xlPart or xlWhole, After:=what?, look in formulas or values, etc.
EDIT:* Modified the code to look for a single instance of the value in H39 and .Resize to expand the width (as per OP's comments).

Need help refining my excel macro for deleting blank rows or performing another action

Basically what I'm trying to accomplish is to search the document for blank rows and delete them, if any. This works great if there are blank rows to delete; however, if there are no blank rows, the macro ends with an error. I'd be eternally grateful if someone could advise me how to make this into an "if blank rows then this, if none then that"
Sheets ("xml") .Select
Cells.Select
Selection.SpecialCells(x1CellTypeBlanks).Select
Selection.EntireRow.Delete
Enter my second macro (this part works fine)
Regards
Let me point you to the canonical:
How to avoid using Select/Activate in Excel VBA macros
So you can start to understand why your current code fails or performs undesired operation. What happens when there are no blank cells in your selection? You'll get an error. Why?
Because in that circumstance, Selection.SpecialCells(xlCellTypeBlanks) evaluates to Nothing. (You can verify this using some debug statements) And because Nothing does not have any properties or methods, you'll get an error, because you're really saying:
Nothing.Select
Which is a null program, does not grok, does not compute, etc.
So, you need to test for nothingness with something like this:
Sheets("xml").Select
Cells.Select
If Not Selection.SpecialCells(x1CellTypeBlanks) Is Nothing Then
Selection.SpecialCells(x1CellTypeBlanks).EntireRow.Delete
End If
I still suggest avoiding Select at all costs (it is superfluous about 99% of the time and makes for sloppy code which is difficult to debug and maintain).
So you could do something more complete following that line of thought:
Dim blankCells as Range '## Use a range variable.
'## Assign to your variable:
Set blankCells = Sheets("xml").Cells.SpecialCells(xlCellTypeBlanks)
'## check for nothingness, delete if needed:
If Not blankCells Is Nothing then blankCells.EntireRow.Delete
Follow-up from comments
So in VBA we are able to declare variables which represent objects or data/values, much like a maths variable in an equation.
A Range is a type of object part of the Excel object model, which consists of the Workbook/Worksheets/Cells/Ranges/etc. (far more than I could hope to convey to you, here)
http://msdn.microsoft.com/en-us/library/office/ff846392(v=office.14).aspx
A good example of why to use variables might be here if you scroll down to the "Why Use Variables" section.
http://www.ozgrid.com/VBA/variables.htm
This is of course very simple... but the reader's digest version is that variables allow us to repeatedly refer to the same object (or value for sipmle data types) without explicitly referring to it each time.
THen there is the handy side-effect that the code bcomes more easy to read, maintain and debug, when we use variables instead of absolute references:
Dim rng as Range
Set rng = Sheets(1).Range("A1:Q543").Resize(Application.WorksheetFunction.CountA(Sheets(1).Range("A:A"),))
Imagine that fairly (but not ridiculously) complicated range construct. If you needed to refer to that range more than once in your code, it would be silly not to assign it to a variable, if for no other reason than to save your own sanity from typing (and possibly mistyping a part of it). It is also easy to maintain, since you need only modify the one assignment statement and all subsequent references to rng would reflect that change.

Method Select of Object '_Worksheet' failed - why?

Set mainWB = Workbooks("Copy Paste.xlsb")
Set mainWS = mainWB.Sheets("Sheet1")
Set testWS = mainWB.Sheets("Sheet3")
mainWS.Select
I keep getting an error on the last line in Excel VBA:
"Method Select of Object '_Worksheet' failed"
Any idea why or how to fix this? Thank you!
As discussed in comments, cannot select Sheets in VBA that are not active (or Range objects on them).
For example the following code
Sheets(1).Activate
Sheets(2).Range("A1").Select
will cause the error you are receiving.
In your case, it seems you are attempting to Select a sheet which is not active - your mainWS object is presumably not the ActiveSheet at the point you are calling this code. An easy way to test if this is happening is if you add the following to the end of your code:
if (ActiveSheet.Name <> mainWS.Name) then
msgbox ("Going to crash after clicking ok!")
end if
mainWS.Select
Note that you can refer to the activated worksheet with the command ActiveSheet to either get properties or whatever other operations you are interested in doing.
This error can also happen if you have loop working thru all of the worksheets in the workbook and there are hidden sheets. Lookout for that.
Last, and unrelated to your specific error message, I assume you are declaring those variables somewhere and simply did not copy them here - if not I would consider using Option Explicit as you can often run into all sorts of issues without having Option Explicit at the top of your VBA code.
While I agree with the above, it is also important to note that the Delete function will not work if the worksheet's visibility is currently set to xlSheetVeryHidden
I had the same issue and looked at this post for ideas on how to fix it. My issue was resolved by using "Activate" as opposed to "Select" on the line that my code was failing on. So instead of using "mainWS.Select", try using "mainWS.Activate" instead.

VBA: How to get the last used cell by VBA code when the last error occured in a Workbook/Worksheet?

Eventually, I want to move the cell to the location where the last error occured. Edit: Forgot to say that I'm using Excel 2003.
As requested in comments...
Look up the 'Caller' property of the 'Application' object in the Excel VBA help. When you use it from a VBA routine, it will tell you where the call to the routine came from - what Range, Chart, etc.
An important thing to be aware of when using 'Application.Caller' is that it isn't always a Range object. Look at the help, but the property returns a Variant value that can be a Range, String, or Error. (It is a Range object in the case you're interested in, but you'll need to be aware of this.)
Because of the above, and the vagaries of VBA syntax when it comes to objects vs. values, it can be tricky to use 'Application.Caller'. Putting a line like:
Debug.Print Application.Caller.Address
in your code will fail when the caller isn't a Range. Doing something like:
Dim v
v = Application.Caller
will "compile", but will create circular references when the caller is a Range because you're trying to access the value of the calling Range.
This all means that it's probably best to write a little utility function for yourself:
Public Function currentCaller() As String
If TypeOf Application.Caller Is Range Then
Dim rng As Range
Set rng = Application.Caller
currentCaller = rng.Address(External:=True)
Else
currentCaller = CStr(Application.Caller)
End If
End Function
and then call it from your error handlers where you want to know where the call came from.
One more thing - obviously this can only tell you the caller once a VBA routine has actually been called. If you have errors in your calling formulas, Excel will return error values to your cells without ever calling your VBA routines.
Wrap your VBA function in another function that stores the cell location and value as variants. Keep this 'wrapper' function as basic as possible so it won't cause any additional errors.
If you're trying to debug app-crashing errors, the wrapper function could even store those values in a comma-delimited text file. Once stored, Excel can crash all it wants and you'll still know what the cell location and value were since you stored them outside of Excel beforehand.
Could this be done with an error handler?
An example of what I mean below:
sub code1()
on error goto cell A1
end sub