3

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:

  1. Application.Cells.Select
  2. Application.ActiveSheet.Cells
  3. 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!

Samuel Hulla
  • 6,617
  • 7
  • 36
  • 70
GeekyFreaky
  • 283
  • 1
  • 2
  • 9
  • Avoid Select, it is not needed in VBA very often. Avoid Activesheet, unless you are in a single sheet workbook and are just moving quickly. Cells would normally mean a single Cell, Range for Range, they have some different methods. Cells is often used inside of a Range specification, especially when variables are used to step through rows and/or columns. Better to name the worksheet in developer tab-properties and call it directly (ex. wsMySheet.Cells (x,y).Value = wsMyOtherSheet.Cells(x,y).Value) x and y being loop variables of double or long for instance. – Wookies-Will-Code Aug 17 '18 at 14:30
  • Probably active sheet, it will always need an object defined. But as I said above, I would avoid its usage if possible or it will lead to some hard to track errors. Always let VBE know what workbook and what sheet you are using. Common practice for those that start opening workbooks and copying info and then closing them, you never want to leave it in doubt or the Application will not be able to resolve the object you wish to work with, usually throws and error, might just give strange results. – Wookies-Will-Code Aug 17 '18 at 14:33
  • This would be the most fully qualified, it has the least amount of ambiguity for the "compiler" to fill in: Application.ActiveWorkbook.ActiveSheet.Cells – Wookies-Will-Code Aug 17 '18 at 14:35
  • Wookies-Will-Code - thank you for all those tips! I wrote them all down so I can remember for the future. :D – GeekyFreaky Aug 17 '18 at 14:49
  • @Wookies-Will-Code except `ActiveSheet.Parent` will always point to `ActiveWorkbook`, so qualifying `ActiveSheet` is always redundant - VBA doesn't need to work "harder" to know what sheet it's referring to, it *already* has the reference. Hence, any worksheet-qualified member call is sufficiently qualified to be perfectly unambiguous - qualifying all the way up to `Application` **is** useful though, but in scenarios where you own the `Application` instance (e.g. Excel automation, from Word). In fact, implicit `Application` refs can cause "ghost process" and bad teardown in these cases. – Mathieu Guindon Aug 17 '18 at 15:06

3 Answers3

5

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:

object browser: members of '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:

object browser: members of 'Range'

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
Mathieu Guindon
  • 69,817
  • 8
  • 107
  • 235
  • You weren't kidding when you said it was complicated. When I learn more (because I just started a week ago) I will come back to this to see if I understand your answer better. I got about 50% of it though. lol Thanks for the response! :) – GeekyFreaky Aug 17 '18 at 16:08
2

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

  1. Cells -> ActiveWorkbook.ActiveSheet.Range.Cells returns Range of all the cells
  2. 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

Samuel Hulla
  • 6,617
  • 7
  • 36
  • 70
  • Rawrplus, thank you for your expeditious and informative answer. That clarified a lot for me!!!! – GeekyFreaky Aug 17 '18 at 14:46
  • Rawrplus, one more question: Cells is a property of quite a few objects. How do I know which one the compiler is using if the object qualifiers are missing from the statement? I think that's what drives me most insane. – GeekyFreaky Aug 17 '18 at 14:47
  • @GeekyFreaky you don't. not without peeking at the source code. but you can infer it. – Mathieu Guindon Aug 17 '18 at 14:54
  • I upvoted this answer too early - `ThisWorkbook` has nothing to do here, it's `ActiveWorkbook`; `ThisWorkbook` refers to *the code that contains the VBA project*, not the active workbook. Assuming otherwise can bite you. – Mathieu Guindon Aug 17 '18 at 14:56
  • anyway.. *avoid select and writing explicit code* in itself is upvote-worthy =) – Mathieu Guindon Aug 17 '18 at 15:02
  • 1
    @MathieuGuindon oops that is indeed correct. I have edited the answer now. That was my bad, somehow I got fixated on the `ThisWorkbook` in the original qeustion – Samuel Hulla Aug 17 '18 at 15:25
  • @GeekyFreaky Mathieu basically answered that one for you. To put it in laymans terms, think of this like an onion. The outmost layer is the reference to the actual Cells and the deeper we go *the more layers we unpeel* we need to manipulate with them. That's why I recommended to always declare specific `Worksheet` when refering to range. So we know with which layer we're always referring to and not leave it to presumptions. The innermost layer - the core - is indded the `Application` itself *(that's not exactly correct, as Mathieu's answer points out, but as a simplified example it goes...)* – Samuel Hulla Aug 17 '18 at 15:31
  • @MathieuGuindon - I just figured that out now and came back to correct it! hahahaha You beat me to the chase. :) – GeekyFreaky Aug 17 '18 at 16:06
0

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:

  1. Range("F3:J10").Cells(3)
  2. Range("F3:J10")(3)
  3. Range("F3:J10").Cells(1, 3)
  4. Range("F3:J10")(1, 3)
  5. Range("F3:J10").Cells(1, "C")
  6. Range("F3:J10")(1, "C")
  7. Range("F3:J10").Range("C1")
JohnyL
  • 6,894
  • 3
  • 22
  • 41