VBAAttributes

Syntax

  • Attribute VB_Name = "ClassOrModuleName"
  • Attribute VB_GlobalNameSpace = False ' Ignored
  • Attribute VB_Creatable = False ' Ignored
  • Attribute VB_PredeclaredId = {True | False}
  • Attribute VB_Exposed = {True | False}
  • Attribute variableName.VB_VarUserMemId = 0 ' Zero indicates that this is the default member of the class.
  • Attribute variableName.VB_VarDescription = "some string" ' Adds the text to the Object Browser information for this variable.
  • Attribute procName.VB_Description = "some string" ' Adds the text to the Object Browser information for the procedure.
  • Attribute procName.VB_UserMemId = {0 | -4}
    • ' 0: Makes the function the default member of the class.
    • ' -4: Specifies that the function returns an Enumerator.

VB_Name

VB_Name specifies the class or module name.

Attribute VB_Name = "Class1"

A new instance of this class would be created with

Dim myClass As Class1
myClass = new Class1

VB_GlobalNameSpace

In VBA, this attribute is ignored. It was not ported over from VB6.

In VB6, it creates a Default Global Instance of the class (a "shortcut") so that class members can be accessed without using the class name. For example, DateTime (as in DateTime.Now) is actually part of the VBA.Conversion class.

Debug.Print VBA.Conversion.DateTime.Now
Debug.Print DateTime.Now

VB_Createable

This attribute is ignored. It was not ported over from VB6.

In VB6, it was used in combination with the VB_Exposed attribute to control accessibility of classes outside of the current project.

VB_Exposed=True 
VB_Creatable=True

Would result in a Public Class, that could be accessed from other projects, but this functionality does not exist in VBA.

VB_PredeclaredId

Creates a Global Default Instance of a class. The default instance is accessed via the name of the class.

Declaration

VERSION 1.0 CLASS
BEGIN
  MultiUse = -1  'True
END
Attribute VB_Name = "Class1"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
Option Explicit

Public Function GiveMeATwo() As Integer
    GiveMeATwo = 2
End Function

Call

Debug.Print Class1.GiveMeATwo

In some ways, this simulates the behavior of static classes in other languages, but unlike other languages, you can still create an instance of the class.

Dim cls As Class1
Set cls = New Class1
Debug.Print cls.GiveMeATwo

VB_Exposed

Controls the instancing characteristics of a class.

Attribute VB_Exposed = False

Makes the class Private. It cannot be accessed outside of the current project.

Attribute VB_Exposed = True

Exposes the class Publicly, outside of the project. However, since VB_Createable is ignored in VBA, instances of the class can not be created directly. This is equivalent to a the following VB.Net class.

Public Class Foo
    Friend Sub New()
    End Sub
End Class    

In order to get an instance from outside the project, you must expose a factory to create instances. One way of doing this is with a regular Public module.

Public Function CreateFoo() As Foo
    CreateFoo = New Foo
End Function

Since public modules are accessible from other projects, this allows us to create new instances of our Public - Not Createable classes.

VB_Description

Adds a text description to a class or module member that becomes visible in the Object Explorer. Ideally, all public members of a public interface / API should have a description.

Public Function GiveMeATwo() As Integer
    Attribute GiveMeATwo.VB_Description = "Returns a two!"        
    GiveMeATwo = 2
End Property

Description Text in Object Explorer

Note: all accessor members of a property (Get, Let, Set) use the same description.

VB_[Var]UserMemId

VB_VarUserMemId (for module-scope variables) and VB_UserMemId (for procedures) attributes are used in VBA mostly for two things.

Specifying the default member of a class

A List class that would encapsulate a Collection would want to have an Item property, so the client code can do this:

For i = 1 To myList.Count 'VBA Collection Objects are 1-based
    Debug.Print myList.Item(i)
Next

But with a VB_UserMemId attribute set to 0 on the Item property, the client code can do this:

For i = 1 To myList.Count 'VBA Collection Objects are 1-based
    Debug.Print myList(i)
Next

Only one member can legally have VB_UserMemId = 0 in any given class. For properties, specify the attribute in the Get accessor:

Option Explicit
Private internal As New Collection

Public Property Get Count() As Long
    Count = internal.Count
End Property

Public Property Get Item(ByVal index As Long) As Variant
Attribute Item.VB_Description = "Gets or sets the element at the specified index."
Attribute Item.VB_UserMemId = 0
'Gets the element at the specified index.
    Item = internal(index)    
End Property

Public Property Let Item(ByVal index As Long, ByVal value As Variant)
'Sets the element at the specified index.    
    With internal
        If index = .Count + 1 Then
            .Add item:=value
        ElseIf index = .Count Then
            .Remove index
            .Add item:=value
        ElseIf index < .Count Then
            .Remove index
            .Add item:=value, before:=index
        End If
    End With
End Property

Making a class iteratable with a For Each loop construct

With the magic value -4, the VB_UserMemId attribute tells VBA that this member yields an enumerator - which allows the client code to do this:

Dim item As Variant
For Each item In myList
    Debug.Print item
Next

The easiest way to implement this method is by calling the hidden [_NewEnum] property getter on an internal/encapsulated Collection; the identifier needs to be enclosed in square brackets because of the leading underscore that makes it an illegal VBA identifier:

Public Property Get NewEnum() As IUnknown
Attribute NewEnum.VB_Description = "Gets an enumerator that iterates through the List."
Attribute NewEnum.VB_UserMemId = -4
Attribute NewEnum.VB_MemberFlags = "40" 'would hide the member in VB6. not supported in VBA.
'Gets an enumerator that iterates through the List.
    Set NewEnum = internal.[_NewEnum]    
End Property