2

If I write this in the VBA editor:

Dim ws As Worksheet: set ws = ActiveSheet
ws.Columns(

IntelliSense shows me a seemingly unrelated tooltip:

_Default([RowIndex], [ColumnIndex])

The Worksheet.Columns property only accepts the index (column) number as far as I can see in the documentation.

So why am I asked for a RowIndex? Why does it refers to _Default (and what is it)?

Mathieu Guindon
  • 69,817
  • 8
  • 107
  • 235
6diegodiego9
  • 503
  • 3
  • 14
  • 3
    See the screenshot in [this answer](https://stackoverflow.com/a/32997154/9245853). `Columns` returns a `Range`, and `_Default` is the default member. Hopefully someone like Mathieu Guindon can provide a more thorough answer. His article [here](https://rubberduckvba.wordpress.com/2018/03/15/vba-trap-default-members/) about default members is a good read. – BigBen Sep 25 '19 at 14:40

1 Answers1

4

The Worksheet.Columns property only accepts the index (column) number as far as I can see in the documentation.

Nowhere in the documentation does it say the Columns property takes a parameter, and indeed, it would be wrong to mention that, because it doesn't have any:

Worksheet.Columns definition in Object Browser

Like Worksheet.Rows, Worksheet.Columns yields a Range object. So when you "parameterize" it, what's really happening is this:

Set foo = ws.Columns.[_Default](value)

Any argument you provide, get interpreted as arguments to an implicit default member call against the Range object that was returned by the call to Columns.

You may have read somewhere that the default member of a Range is its Value - and that is not true. The default member of a Range is a hidden property named [_Default] (square brackets are required in VBA if you want to invoke it explicitly, because no legal VBA identifier can begin with an underscore), that takes two optional parameters:

Range._Default hidden property in Object Browser

When you read ("get") this default property without providing any arguments, this default property does get you the Range.Value (i.e. a single Variant value for a single cell, or a 2D Variant array for multiple cells). When you assign to this default property, you are assigning the Range.Value.

But when any arguments are provided when reading ("get") this default property, what you get is a call to the very standard Range.Item indexer property:

Range.Item definition in the object browser

So what Columns does, is simply take your input range, and yield a Range object laid out in such a way that it can be accessed using a RowIndex argument - we can prove this using named arguments, which show that this code is illegal:

?Sheet1.Range("A1:C1").Columns.Item(ColumnIndex:=2).Address
>> "wrong number of arguments"

As is this equivalent code:

?Sheet1.Range("A1:C1").Columns(ColumnIndex:=2).Address
>> "error 1004"

Note that the _Default property yields a Variant, so the above .Address member call can only be resolved at run-time (and you don't get any intellisense for it, and the compiler will not flinch at any typo, even with Option Explicit specified - you will get error 438 at run-time though).

Best stick to safe early-bound land, and pull the returned object reference into a local variable:

Dim foo As Range
Set foo = ws.Columns(1)

Debug.Print foo.Address '<~ early-bound w/intellisense & compile-time validation

TL;DR: You're being prompted for a RowIndex argument because you are making a call (albeit an implicit one) to a hidden _Default property that accepts a RowIndex argument.

Mathieu Guindon
  • 69,817
  • 8
  • 107
  • 235
  • 1
    Great answer - this is why I deferred to you in my original comment. Great read and thorough. Thanks for taking the time to lay it out. – BigBen Sep 25 '19 at 16:50
  • Wow, thanks Mathieu for this lesson and sorry for my delayed reading and approval! – 6diegodiego9 Oct 04 '19 at 13:47