Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
Guide
SEPTEM BER 9, 2016 BY PAU L KELLY ·36 COMMEN TS
“Classes struggle, some classes triumph, others are eliminated. Such is history” – Chairman Mao
Event - Initialize Sub that automatically runs when the class module
object is created.
Event - Terminate Sub that automatically runs when the class module
object is deleted.
Introduction
VBA Class Modules allow the user to create their own objects. If you are not familiar with objects then I would highly
recommend that you first check out my previous post VBA Objects – The Ultimate Guide.
In languages such as C# and Java, classes are used to create objects. Class Modules are the VBA equivalent of these
classes. The major different is that VBA Class Modules have a very limited type of Inheritance* compared to classes in
the other languages. In VBA, Inheritance works in a similar way to Interfaces** in C#\Java.
In VBA we have built-in objects such as the Collection, Workbook, Worksheet and so on. The purpose of VBA Class
Modules is to allow us to custom build our own objects.
Let’s start this post by looking at why we use objects in the first place.
*Inheritance is using an existing class to build a new class.
**Interfaces are a form of Interitance that forces a class to implement specifics procedures or properties.
This is similar to how things are built using Lego® bricks. There are many different types of Lego® components used. For
example, a block, steering wheel, and laser are different items. They behave completely independently each other. The
wheel spins, the laser rotates etc. Yet we can connect them together to create a building, vehicle, space station and so on.
If you are still not clear about this then don’t worry. We’ll be breaking it all down into simple terms in the rest of this post.
*If you create an application using objects it will take longer to create it initially as you have to spend more time planning
and designing it. However, in the long run it will save you a huge amount of time. Your code will be easier to manage,
update and reuse.
Our new class is called Class1. We can change the name in the Properties window as the following screenshot shows
Let’s change the name of the class module to clsCustomer. Then we will add a variable to the class module like this
oCustomer.Name = "John"
Debug.Print oCustomer.Name
Class Module versus Objects
People who are new to using classes and VBA class modules, often get confused between what is a class and what is an
object.
Let’s look at a real world example. Think of a mass produced item like a coffee mug. A design of the mug is created first.
Then, thousands of coffee mugs are created from this design.
This is similar to how class modules and objects work.
The class module can be thought of as the design.
The object can be thought of as the item that is created from the design.
The New keyword in VBA is what we use to create an object from a class module. For example
Note: We don’t use New with items such as Workbooks and Worksheets. See When New is not required for more
information.
For example, imagine we have two identical PrintCustomer subs. One is in a class module and one is in a normal
module…
End Sub
You will notice the code for both is exactly the same.
To use the PrintCustomer sub from the class module, you must first create an object of that type
Sub UseCustomer()
oCust.PrintCustomer
End Sub
To use PrintCustomer from the normal module you can call it directly
Sub UseCustomer()
PrintCustomer
End Sub
For the normal module variable there will only be one copy of this variable in our application.
StudentName = "John"
For the class module a new copy of the variable StudentName is created each time a new object is created.
student1.StudentName = "Bill"
student2.StudentName = "Ted"
When you fully understand VBA class modules, these differences will seem obvious.
1. Methods – functions/subs.
2. Member variables – variables.
3. Properties– types of functions/subs that behave like variables.
4. Events – subs that are triggered by an event.
You can see they are all either functions, subs or variables.
Let’s have a quick look at some examples before we deal with them in turn
' Properties
Balance = dBalance
End Property
dBalance = dValue
End Property
dBalance = 100
End Sub
' Methods
End Sub
End Sub
Now that we have seen examples, let’s take a look at each of these in turn.
Debug.Print sText
End Sub
End Function
' private procedures can only be called from within the Class Module
GetDeduction = 2.78
End Function
Sub ClassMembers()
oSimple.PrintText "Hello"
dTotal = oSimple.Calculate(22.44)
Debug.Print dTotal
End Sub
Class Module Member Variables
The member variable is very similar to the normal variable we use in VBA. The difference is we
use Public or Private instead of Dim.
Note: Dim and Private do exactly the same thing but the convention is to use Dim in sub/functions and to use Private
outside sub/functions.
The Public keyword means the variable can be accessed from outside the class module. For example
oAccount.AccountID = "499789"
oAccount.Balance = 678.90
In the above example we cannot access Balance because it is declared as Private. We can only use a Private variable
within the class module. We can use in a function/sub in the class module e.g.
Balance = 100
Debug.Print Balance
End Sub
It is considered poor practice to have public member variables. This is because you are allowing code outside the object to
interfere with how the class works. The purpose of the using classes is so that we hide what is happening from the caller.
To avoid the user directly talking to our member variables we use Properties.
Class Module Properties
1. Get – returns an object or value from the class
2. Let – sets a value in the class
3. Set – sets an object in the class
End Property
End Property
End Property
We have seen already that the Property is simply a type of sub. The purpose of the Property is to allow the caller to get and
set values.
When the user wants to get the number of countries in the list they could do this
NumCountries = UBound(oCountry.arrCountries) + 1
1. To get the number of countries you need to know how the list is stored e.g. Array.
2. If we change the Array to a Collection, we need to change all code that reference the array directly.
To solve these problems we can create a function to return the number of countries
' Array
Count = UBound(arrCountries) + 1
End Function
This code solves the two problems we listed above. We can change our Array to a Collection and the caller code will still
work e.g.
Count = collCountries.Count
End Function
The caller is oblivious to how the countries are stored. All the caller needs to know is that the Countfunction will return
the number of countries.
As we have just seen, a sub or function provides a solution to the above problems. However, using a Property can provide
a more elegant solution.
Count = UBound(arrCountries) + 1
End Function
Count = UBound(arrCountries) + 1
End Function
In this scenario, there is not a lot of difference between using the Property and using a function. However, there are
differences. We normally create a Get and Let property like this
TotalCost= dTotalCost
End Property
Property Let TotalCost(dValue As Long)
dTotalCost = dValue
End Property
Using Let allows us to treat the property like a variable. So we can do this
oAccount.TotalCost = 6
The second difference is that using Let and Get allows us to use the same name when referencing the Get or Let property.
So we can use the property like a variable. This is the purpose of using Properties over a sub and function.
oAccount.TotalCost = 6
dValue = oAccount.TotalCost
If we used a function and a sub then we cannot get the behaviour of a variable. Instead we have to call two different
procedures e.g.
oAccount.SetTotalCost 6
dValue = oAccount.GetTotalCost
You can also see that when we used Let we can assigned the value like a variable. When we use SetTotalCost , we had to
pass it as a parameter.
Let a = 7
a = 7
So we use Let to assign a value to a variable and we use Set to assign an object variable to an object.
Dim a As Long
Let a = 7
In the following example, we use Get and Let properties for a string variable
Name = m_sName
End Property
m_sName = sName
End Property
We can then use the Name properties like this
Sub TestLetSet()
oCurrency.Name = "USD"
sName = oCurrency.Name
End Sub
In the next example, we use Get and Set properties for an object variable
End Property
End Property
End Sub
We use the Get property to return the values for both items. Notice that even though we use the GetProperty to return the
Collection, we still need to use the Set keyword to assign it.
In Object Oriented languages like C++, these events are referred to as the Constructor and the Destructor. In most
languages, you can pass parameters to a constructor but in VBA you cannot. We can use a Class Factory to get around
this issue as we will see below.
Initialize
Let’s create a very simple class module called clsSimple with Initialize and Terminate events
End Sub
Debug.Print "Hello"
End Sub
In the following example, we use Dim and New to create the object.
In this case, oSimple is not created until we reference it for the first time e.g.
Sub ClassEventsInit2()
oSimple.PrintHello
End Sub
When we use Set and New together the behaviour is different. In this case the object is created when Set is used e.g.
Sub ClassEventsInit()
oSimple.PrintHello
End Sub
Note: For more information about the different between using New with Dim and using New with Setsee Subtle
Differences of Dim Versus Set
As I said earlier, you cannot pass a parameter to Initialize. If you need to do this you need a function to create the object
first
End Sub
End Sub
oSimple.Init Price
End Function
Terminate
The Terminate event occurs when the class is deleted. This happens when we set it to Nothing
Sub ClassEventsTerm()
End Sub
If we don’t set the object to Nothing then VBA will automatically delete it when it goes out of scope.
What this means is that if we create an object in a procedure, when that procedure ends VBA will delete any objects that
were created.
Sub ClassEventsTerm2()
oSimple.PrintHello
End Sub
For i = 2 To rg.Rows.Count
Year = rg.Cells(i, 3)
rowColl.Add rg.Cells(i, 1)
rowColl.Add rg.Cells(i, 2)
' and so on
coll.Add rowColl
End If
Next i
As you can imagine this code would get messy very quickly.
© BigStockPhoto.com
Lucky for us we have VBA class modules to make our life easier. We can create a class module to store the items.
Artist = m_sArtist
End Property
m_sArtist = sArtist
End Property
' etc
oAlbum.Artist = rg.Cells(i, 1)
oAlbum.Title = rg.Cells(i, 2)
oAlbum.Year = rg.Cells(i, 3)
oAlbum.Genre = rg.Cells(i, 4)
oAlbum.Sales = rg.Cells(i, 5)
coll.Add oAlbum
You can see that this makes our code much more readable. It is clear what Artist, Title etc. are being used for.
We can then easily use this data to create reports, write to files etc.
' Print out the title and artist for each album
Next
End Sub
Sub CreateReport()
PrintAlbum coll
PrintTotalSales coll
End Sub
As Collection
Dim rg As Range
Set rg = Sheet1.Range("A1").CurrentRegion
For i = 2 To rg.Rows.Count
Year = rg.Cells(i, 3)
oAlbum.Artist = rg.Cells(i, 1)
oAlbum.Title = rg.Cells(i, 2)
oAlbum.Year = Year
oAlbum.Genre = rg.Cells(i, 4)
oAlbum.sales = rg.Cells(i, 5)
coll.Add oAlbum
End If
Next i
End Function
Next
End Sub
Next
End Sub
The products have different fields so we need to use a different class module for each product type. One type for a Book
row, one type for a Film row.
We’ll create our class modules first. As you can imagine the are very similar for both product types
' Properties
ItemType = "Book"
End Property
Title = m_Title
End Property
Year = m_Year
End Property
' Methods
m_Title = rg.Cells(1, 2)
End Sub
End Sub
' Properties
ItemType = "Film"
End Property
Title = m_Title
End Property
Year = m_Year
End Property
' Methods
m_Title = rg.Cells(1, 2)
End Sub
Public Sub PrintToImmediate()
End Sub
As you can see, the only real difference is the Init sub.
When we read each record we need to determine if it is a Book or Film. Then we create the appropriate object. You would
imagine we would have to create a variable for each type e.g.
If we had lots of different types this would get very messy indeed. The good news is we only need to use one variable!
In VBA we can declare a variable as a Variant. When we use a Variant we are essentially saying “We will decide the type
of variable when the code is running”.
This is very useful when dealing with objects and allows us to get away with using one variable e.g.
' If clsBook
oItem.InitBook
oItem.InitFilm
However, if they have the same name, e.g. Init, we can replace the If\ElseIf lines of code with one line
' this will call the Init sub of whatever type oItem is set to
oItem.Init
We can now create a function to create the appropriate object. In Object Oriented Programming, we have what is called a
Class Factory. This is simply a function that creates an object based on a given type.
We saw earlier that the Initialize event does not take parameters. We can call Init in the Class Factory to get around this
issue.
The full code for the ClassFactory function is here
sType = rg.Cells(1, 1)
Case "Book":
Case "Film":
Set oItem = New clsFilm
Case Else
End Select
oItem.Init rg
End Function
This following is our starting sub. In this sub, we read through the worksheet and pass the range to ClassFactory.
It creates the object, passes the range to the object Parse method. Then it returns the object which we add to our
Collection.
Sub ReadProducts()
Dim rg As Range
Dim i As Long
For i = 1 To 2
coll.Add product
Next
' Print the product details to the Immediate Window(Ctrl + G)
PrintCollection coll
End Sub
We can also use the variant object to print the items. As long as both objects have a sub with the same name and
parameters(e.g PrintToImmediate) we can call it using a Variant type.
Dim v As Variant
v.PrintToImmediate
Next
End Sub
Conclusion
That concludes my post on the VBA Class Modules. In this post, we have looked at the parts of the VBA Class Module
and two example cases where you would use them.
It’s important to understand that Classes and Objects is a vast topic. There are countless types of objects you can create
and ways you can use them.
If you plan to use Class Modules then my advice is to start simple and get familiar with how to create a simple one. Once
you have mastered the basics it will be much easier to move onto more challenging scenarios