Application.Cells VS Application.ActiveSheet.Cells - vba

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

Related

What is the difference between 'Range.Parent' and 'Range.Worksheet' in VBA?

The .Parent and .Worksheet properties, when used with a range, seem to reference to the same worksheet object where Range is located.
For example, both of these lines return the same value:
Debug.Print Selection.Parent.Name
Debug.Print Selection.Worksheet.Name
Is there a difference between the two? Are there advantages/disadvantages to each method?
Unless you can guarantee Selection is always part of a Worksheet, it's not said that Selection.Parent.Name will yield the same result as Selection.Worksheet.Name. If it's "in" other types of objects (charts or graphics, for example) the result could be quite different - you'd need to do some testing.
So, Selection.Worksheet.Name is more exact (and, as someone has pointed out in Comments, potentially faster in execution). But if you can't guarantee Selection is going to reference a Worksheet it can trigger an error or yield an unexpected result.

Referencing Excel Cells in VBA

My main problem is assigning a variable an manipulating it in VBA.
I know that this code works, but I'm not sure if it this is a proper way to assign a variable in VBA like I have with currentcell = Cells(i, 1).Value
Sub C()
Dim i As Integer
For i = 1 To 4
currentcell = Cells(i, 1).Value
MsgBox currentcell
Next i
End Sub
What the macro recorder and a lot of tutorials won't tell you, is that these calls are implicitly referring to the ActiveSheet, which is obviously implicitly in the ActiveWorkbook. When all you ever have to deal with is a single worksheet, it's probably fine, but reality is that it's never the case, and code written with implicit references to the ActiveSheet is incredibly frail, bug-prone, and the underlying cause behind way too many Stack Overflow questions tagged with vba:
Cells
Range
Name
Rows
Columns
Whenever you use any of those, you're making calls against some global-scope Property Get accessor that "conveniently" fetches the ActiveSheet reference for you:
Screenshot of Rubberduck's toolbar, showing the declaration site of the selected code. Disclaimer: I manage that open-source VBIDE add-in project.
So yes, this "works", but what you'll want is to work off an explicit Worksheet object reference instead. There are many ways to get a Worksheet object reference, but the best one is to not care about where the worksheet is coming from, and take it as a parameter:
Sub DoSomething(ByVal ws As Worksheet)
Dim currentCell As Variant
Dim i As Integer
For i = 1 To 4
currentcell = ws.Cells(i, 1).Value
'in-cell error values can't be converted to a string:
If Not IsError(currentCell) Then MsgBox currentcell
Next i
End Sub
This takes the responsibility of knowing exactly what worksheet to work with away from the procedure, and gives it to the calling code, which can look like this:
DoSomething Sheet1 'uses the sheet's CodeName property
Or this:
DoSomething ThisWorkbook.Worksheets("Period" & n)
Or whatever. There are plenty of ways, each with their own gotchas - often you'll see something like this:
DoSomething Sheets("Sheet1") 'where "Sheet1" is in ThisWorkbook
Except Sheets implicitly refers to the ActiveWorkbook which may or may not be ThisWorkbook, and the Sheets collection could return a Chart object, which isn't a Worksheet. Also if "Sheet1" is in ThisWorkbook (the workbook with the code that's running), then referring to a worksheet by name means your code breaks as soon as the user renames the tab for that sheet.
The most robust way to refer to a worksheet that exists at compile/design-time, is by its CodeName. Select the sheet in the Project Explorer (Ctrl+R), then bring up the Properties toolwindow (F4), and set its CodeName by changing the (Name) property to whatever you need: VBA defines a global-scope global object variable by that name, so you can call it MyAwesomeReport and then refer to it as such in code:
DoSomething MyAwesomeReport
And that code will not break if the user changes the worksheet's "name".
Your code (however small that snippet might be) has other issues, notably it's using undeclared variables, which means you're not using Option Explicit, which is another thing that will end up biting you in the rear end and come to Stack Overflow with an embarrassing question that boils down to a typo - because without Option Explicit specified at the top of every module, VBA will happily compile a typo, making your code behave strangely instead of simply not compiling. These bugs can be excruciatingly hard to find, and they're ridiculously easy to prevent:
Use. Option. Explicit. Always.

How do I use RefersToRange?

Can anyone tell me how to use RefersToRange in vba? and what and when is the need of it.
Please provide with simple example first.
Thanks in advance.
In Excel, there is the concept of a named range, that is a range of cells which has a name attached to it. This is represented by the Name object.
The RefersToRange method:
Returns the Range object referred to by a Name object.
For example, let's say I want to read the values only in the print area of the current worksheet. I need the Name object in order to access the print area, but I can't do anything useful with it; if I want to do something useful I have to access the range of cells referred to by that name:
Dim n As Name
Set n = ActiveWorkbook.Names("Print_Area")
Dim rng As Range
Set rng = n.RefersToRange
Dim values() As Variant
values = rng 'I can't read values with a Name object, but I can with a Range object
One thing I have discovered is that sometimes referring to a named range in this fashion:
ThisWorkbook.Range("DownloadPictures").Value = "yes"
results in:
Run-time error '1004':
Method 'Range' of object '_Worksheet' failed
But referring to the named range in this fashion:
ThisWorkbook.Names("DownloadPictures").RefersToRange = "yes"
works fine. My particular circumstance is that the named range ("DownloadPictures") is scoped to the entire workbook, which has several worksheets. I am referring to the name in one of the worksheet's Worksheet_Calculate subroutines. I don't understand why the first method causes an error and the second does not but apparently this is one reason to use the RefersToRange property.
RefersToRange is, one of many ways, to 'point' a range. For example,
ThisWorkbook.Names("DownloadPictures").RefersToRange = "yes"
the above code, points to the range, which can be a cell, named "DownloadPictures" and sets it value to "yes". As a matter of fact, I'd rather use,
ThisWorkbook.range("DownloadPictures").Value = "yes"
The 'Name' in MS Excel is similar to the Variant variable type, but unlike the variables you 'Dim' in the VB code, the 'Name' only be used (or you can consider it is only be used) in the Workbook. The 'Name' and variables are not interfered with each other.
If you open the 'Name Manager', the concept becomes easier to understand. The Name can be created to refer to a cell, a range of cell (a range), some value (so called constant), or a formula.
Each Name object represents a defined name for a range of cells. Names can be either built-in names—such as Database, Print_Area, and Auto_Open—or custom names. ---MSDN
As a Name can refer to range, constant or a formula .RefersToRange specifically refer to a range. And return false if not so.

vba application word using as prefix for workbooks

Could someone explain what is the point of writing Application.Workbooks.Worksheets() ... instead of just Workbooks.Worksheets(), omitting Application?
What additional restrictions does it impose on the Workbooks object?
Short answer: none.
Long answer: it depends.
By fully qualifying Workbooks, you're making it absolutely completely unambiguous that Workbooks is referring to Excel.Application.Workbooks. If you had a class module called Workbooks (with a PredeclaredId=True attribute), then an unqualified Workbooks would be referring to that class module, and if that class module didn't have an Item default indexer property that returned a Workbook object, it would actually prevent this code from compiling:
Debug.Print Workbooks(1).Worksheets(1).Name
If you don't have a class module named Workbooks, then VBA resolves the above to Application.Workbooks.
So, in this case, it doesn't matter.
It's not always the case - take this code as an example:
Range("F12") = 42
That's referring to Application.ActiveSheet.Range, and it can bite you in the rear end. Unqualified Range functions are the source of unquantifiable bugs, because the programmer is assuming what sheet ActiveSheet is.
Cells(2, 2) = 42
Same here. Unqualified usages of Range and Cells, are implicit references to the active sheet, and they're a plague. Avoid them. Code that uses those, usually also feature Select and Activate - a recipe for disaster, that lots of beginner think is "the right way" because macro-recorded code uses it like it's candy.
Avoid implicit references to the active sheet, and avoid implicit references to the active workbook. Implicit references to the active application though, are probably fine. A tiny wee bit lazy, but fine.

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.