Learn Microsoft Access Advanced Programming Techniques, Tips and Tricks.

MS-Access Base Class and Derived Objects

Introduction.

If you have not seen the earlier Posts on the Microsoft Access Class Module, please go through them before continuing. The links are given below.

  1. MS-Access Class Module and VBA.
  2. MS-Access VBA Class Object Array.

 ClsArea A Class can serve as a base class for other class objects, allowing its calculations to be reused as part of more advanced operations. For instance, it could be integrated into a class designed to calculate the volume of an object, where area is only one step in the overall computation.

The dbl prefix in the dblLength and dblWidth Property procedure names simply indicate that these properties expect double-precision numeric values. Similarly, if we were to rename the property procedures to Quantity and UnitPriceMultiplying one by the other would yield the Total Price of an item.

This demonstrates the flexibility of the ClsArea class. Wherever the result of multiplying two values is required—such as TotalPrice * TaxRate to compute tax, or TotalPrice * DiscountRate to determine a discount—it can be adapted as a base class to fit the scenario.

Although we started with a simple class module, it has the potential to be used as part of many other derived classes. The possibilities are limited only by your imagination and creativity.

Currently, our ClsArea Class calculates the area of materials, rooms, or similar objects using only the Length and Width properties. It does not yet support shapes like triangles or circles. However, it can be extended into a new class object that calculates the volume of rooms or warehouses to determine storage capacity. To achieve this, we would simply introduce an additional property, such as Height, into the design.

The Volume Class: ClsVolume.

Let’s now create a new class module named ClsVolume, using ClsArea as its base class.

  1. Insert a new Class Module.

  2. In the Properties Window, change the Name property to ClsVolume.

  3. Type or copy and paste the following code into the class module:

Option Compare Database
Option Explicit

Private p_Area As ClsArea
Private p_Height As Double

Private Sub Class_Initialize()
    Set p_Area = New ClsArea
End Sub

Private Sub Class_Terminate()
    Set p_Area = Nothing
End Sub

Public Property Get dblHeight() As Double
    dblHeight = p_Height
End Property 

Public Property Let dblHeight(ByVal dblNewValue As Double)
   Do While Val(Nz(dblNewValue, 0)) <= 0
      dblNewValue = InputBox("Negative/0 Values Invalid:", "dblHeight()", 0)
    Loop
    p_Height = dblNewValue
End Property

Public Function Volume() As Double

If (p_Area.Area() > 0) And (p_Height > 0) Then
    Volume = p_Area.Area * p_Height
Else
    MsgBox "Enter Valid Values for Length,Width and Height.", , "ClsVolume"
End If

End Function

Let’s examine the code line by line. On the third line, we declare a private member  p_Area of type ClsArea — a reference to an ClsArea instance that this class will use internally. The next line declares a private field p_Height As Double to store the height value. Both members use the p_ prefix to indicate private scope; they will be accessed and validated through property procedures rather than directly from outside the class.

The Class_Initialize() and Class_Terminate() Sub-Routines.

The next two subroutines—Class_Initialize() and Class_Terminate()—play a crucial role in the ClsVolume class.

  • Class_Initialize() runs automatically when an ClsVolume object is created in a standard module. Within this routine, we instantiate the ClsArea object in memory, ensuring that all of its functionality is available to the new class.

  • Class_Terminate() is triggered when we explicitly clear the ClsVolume object with a statement such as Set ClsVolume = Nothing. At this point, the subroutine ensures that the memory allocated to the ClsArea object is also released properly.

 Property Get dblHeight The procedure simply returns the current value of the private field p_Height to the calling program.

Property Let dblHeight The procedure validates the value passed into the NewValue parameter before assigning it to the private property p_Height. This safeguard prevents invalid values (such as zero or negative numbers) from being stored.

The Public Function Volume() calculates the volume by calling the p_Area.Area() function. The returned area value is then multiplied by p_Height using the expression:

Volume = p_Area.Area * p_Height

Before executing this calculation, a validation check ensures that p_Area.Area() returns a value greater than zero (which confirms that both p_Area.dblLength and p_Area.dblWidth contain valid values) and that the p_Height Property is also greater than zero. Only when all three properties hold valid values is the volume calculation performed.

Note: Since the p_Area object of the ClsArea Class is defined as a private member of the ClsVolume class, we must expose its properties (strDesc, dblLength, dblWidth) and its Area() function to the outside world. This is done by creating corresponding Get/Let property procedures in the ClsVolume class module, effectively making these members accessible for use while maintaining encapsulation. The Let/Get Property Procedures.

Here’s the refined code you can add to your ClsVolume class module. These procedures expose the strDesc, dblLength, dblWidth, and Area() members of the private p_Area object to the outside world:

Public Property Get strDesc() As String
   strDesc = p_Area.strDesc
End Property

Public Property Let strDesc(ByVal NewValue As String)
   p_Area.strDesc = NewValue
End Property

Public Property Get dblLength() As Double
   dblLength = p_Area.dblLength
End Property

Public Property Let dblLength(ByVal NewValue As Double)
   p_Area.dblLength = NewValue
End Property

Public Property Get dblWidth() As Double
   dblWidth = p_Area.dblWidth
End Property

Public Property Let dblWidth(ByVal NewValue As Double)
   p_Area.dblWidth = NewValue
End Property

Public Function Area() As Double
    Area = p_Area.Area()
End Function

Check the strDesc property procedures (Get/Let) carefully. The choice of the procedure name strDesc is arbitrary—you could use a different name if you prefer. However, since the original property in the ClsArea class is also named strDesc Reusing the same name here helps maintain a clear connection with the base class and makes the relationship between the two classes more intuitive.

In the Get dblLength() property procedure, the expression to the right of the equals sign p_Area.dblLength retrieves the stored length value from the ClsArea object and returns it to the calling program.

The corresponding Let procedure assigns the incoming parameter value (NewValue) to the p_Area.dblLength property of the ClsArea object. Notice that we do not run a separate validation check here—the validation is already enforced within the ClsArea class itself when the value is assigned.

The same logic applies to the dblWidth property. Its Get and Let procedures expose the corresponding property of the p_Area object while delegating validation to the base class.

Finally, the p_Area.Area() function is surfaced through the ClsVolume class, making it directly accessible to the calling program. This ensures that the Area method defined in the base class can be reused seamlessly in the derived class.

The ClsVolume Derived Class Module Code.

The completed code of the ClsVolume Class Module is given below.

Option Compare Database
Option Explicit

Private p_Area As ClsArea
Private p_Height As Double

Private Sub Class_Initialize()

‘Open ClsArea Object in Memory with the name p_Area
    Set p_Area = New ClsArea 

End Sub

Private Sub Class_Terminate()

‘Removes the Object p_Area from Memory
    Set p_Area = Nothing ‘
End Sub

Public Property Get dblHeight() As Double
    dblHeight = p_Height
End Property

Public Property Let dblHeight(ByVal dblNewValue As Double)

Do While Val(Nz(dblNewValue, 0)) <= 0
      dblNewValue = InputBox("Negative/0 Values Invalid:", "dblHeight()", 0)
    Loop
     p_Height = dblNewValue

End Property

Public Function Volume() As Double

If (p_Area.Area() > 0) And (Me.dblHeight > 0) Then
    Volume = p_Area.Area * Me.dblHeight
Else    

MsgBox "Enter Valid Values for Length,Width and Height.",vbExclamation , "ClsVolume"
End If

End Function

‘ClsArea Class Property Procedures and Method are exposed here

Public Property Get strDesc() As String
   strDesc = p_Area.strDesc
End Property 

Public Property Let strDesc(ByVal NewValue As String)
   p_Area.strDesc = NewValue
End Property

Public Property Get dblLength() As Double
   dblLength = p_Area.dblLength
End Property

Public Property Let dblLength(ByVal NewValue As Double)
   p_Area.dblLength = NewValue
End Property

Public Property Get dblWidth() As Double
   dblWidth = p_Area.dblWidth
End Property

Public Property Let dblWidth(ByVal NewValue As Double)
   p_Area.dblWidth = NewValue
End Property

Public Function Area() As Double
    Area = p_Area.Area()
End Function

At this point, you might be thinking: “This feels like double work—wouldn’t it be better if we could somehow skip the repeated property procedures in the ClsVolume class?” Or perhaps, “Why not just add the dblHeight property directly into ClsArea and calculate both Area() and Volume() from there?”

That’s a fair question. But the purpose here is to demonstrate how a base class object can be incorporated into the design of another class object. This pattern shows the power of reusability and encapsulation in VBA class design.

The key benefit of building reusable class modules is that your main programs remain simple, while all the complexity stays hidden inside the class object. This separation makes your code easier to maintain, reuse, and extend.

Of course, there are more compact approaches that could reduce code repetition—we’ll explore those later. For now, let’s continue with our original plan so you can fully understand the step-by-step process.

The Main Program that Uses the ClsVolume Class.

Let us test our new ClsVolume Class in the main Program in the Standard Module.  The sample code is given below.

Public Sub TestVolume()
Dim vol As ClsVolume

Set vol = New ClsVolume

vol.strDesc = "Warehouse"
vol.dblLength = 25
vol.dblWidth = 30
vol.dblHeight = 10

Debug.Print "Description", "Length", "Width", "Height", "Area", "Volume"

With vol
    Debug.Print .strDesc, .dblLength, .dblWidth, .dblHeight, .Area(), .Volume()
End With
End Sub

You can see how simple the main program is, without the printing lines.

Copy and paste the code into a Standard Module. If the Immediate (Debug) Window is not already open, press Ctrl+G to display it. Next, click anywhere inside the code and press F5 to run the procedure.

The sample output displayed in the Debug Window should look similar to the example shown below: Description Length Width Height Area Volume

Warehouse      25            30            10            750           7500 

Description Length Width Height Area Volume
Warehouse 25 30 10 750 7500

Validation Checks Performance Tests.

Next, we will run a few tests to confirm that the validation checks built into the base class (ClsArea) still works when values are passed through the ClsVolume class. Recall that we also added additional validation in the Area() and Volume() functions.

Let’s test them one at a time.

Test 1: Pass a negative value to the ClsArea.dblLength property through the ClsVolume class. This should immediately trigger the error message and invoke the InputBox() function within the Do While…Loop, prompting you to enter a valid (positive) value.

1.  Replace the Value 25 with -5 in the line Vol.dblLength = 25 and press the F5 Key to run the Code.

  The validation check will trigger the error and will ask for a value greater than zero.  Enter a value greater than 0.  After that, restore the value 25.

2.  Disable the line Vol.dblHeight = 10 by inserting a comment symbol (‘) at the beginning of the line as shown: ‘Vol.dblHeight = 10.  After the change, press the F5 Key to run the Code.

If no input values are provided for the properties, the Volume() Function will generate an error, indicating that all three properties—dblLength, dblWidth, and dblHeight—must contain valid values before the calculation can proceed.

In the same way, you can also test the behavior of the Area() function to verify that it responds correctly to missing or invalid inputs.

To make testing easier, we can create a data-printing function that accepts an ClsVolume object as a parameter and prints its property values and calculated results in the Debug Window.

The Changed Code of Main Programs.

The changed Code for both Programs is given below:

Public Sub TestVolume()
Dim Vol As ClsVolume

Set Vol = New ClsVolume

Vol.strDesc = "Warehouse"
Vol.dblLength = 25
Vol.dblWidth = 30
Vol.dblHeight = 10

Call CVolPrint(Vol)

Set Vol = Nothing

End Sub
Public Sub CVolPrint(volm As ClsVolume)

   Debug.Print "Description", "Length", "Width", "Height", "Area", "Volume"  
With volm
    Debug.Print .strDesc, .dblLength, .dblWidth, .dblHeight, .Area, .Volume
End With

End Sub

Next week, we will build the Volume Class Object.

The Links of All Pages on this Topic.

Earlier Post Link References:

  1. MS-Access Class Module and VBA
  2. MS-Access VBA Class Object Arrays
  3. MS-Access Base Class and Derived Objects
  4. VBA Base Class and Derived Objects-2
  5. Base Class and Derived Object Variants
  6. Ms-Access Recordset and Class Module
  7. Access Class Module and Wrapper Classes
  8. Wrapper Class Functionality Transformation
  9. Ms-Access and Collection Object Basics
  10. Ms-Access Class Module and Collection Object
  11. Table Records in Collection Object and Form
  12. Dictionary Object Basics
  13. Dictionary Object Basics-2
  14. Sorting Dictionary Object Keys and Items
  15. Display Records from Dictionary to Form
  16. Add Class Objects as Dictionary Items
  17. Update Class Object Dictionary Item on Form

Share:

Ms-Access VBA Class Object Arrays

Introduction.

Last week, we introduced a simple custom class object with two properties—Length and Width—and a method to calculate the Area of any item using these values. I hope you now have a good understanding of the basics of an MS Access custom class object. If not, please review this page before continuing: MS Access Class Module and VBA.

Class module objects can significantly reduce the amount of repetitive code in your programs. Whenever you find yourself writing the same logic repeatedly, consider whether it could be encapsulated in a class module. (Of course, some repetitive code may also be handled with utility functions, but class objects require a different approach.)

Creating a class object may seem like extra work, as refining its functionality often requires more code initially. However, your main modules remain simpler, while the detailed and more complex code stays hidden inside the class—organized, reusable, and easier to maintain.

For now, we’ll build on our simple class module and extend it to create an array of objects and calculate the area of multiple items.

The ClassArray() Sub-Routine.

The following sample VBA code, placed in a Standard Module, creates an array of five objects from the ClsArea class. It then prints their property values and method results in the Debug Window.

You can copy and paste the code into a standard module, but it’s even better to type it out yourself—this way, you’ll understand each line and what it does more thoroughly.

Public Sub ClassArray()
Dim tmpA As ClsArea
Dim CA() As ClsArea
Dim j As Long, title As String
Dim L As Long, U As Long

title = "ClassArray"
For j = 1 To 5 ‘the Loop is set for 5 items
  'instantiate temporary ClsArea Object
  'to enter input
  Set tmpA = New ClsArea

tmpA.strDesc = InputBox(Str(j) & ") Description:", title, "")
     tmpA.dblLength = InputBox(Str(j) & ") Enter Length:", title, 0)
     tmpA.dblWidth = InputBox(Str(j) & ") Enter Width:", title, 0)
    
  'Redimension Array
  ReDim Preserve CA(1 To j) As ClsArea
  Set CA(j) = tmpA   'Copy Object to Array
  
Set tmpA = Nothing 'Remove temporary object from memory.
Next


‘PRINTING SECTION
'Print the Array contents into the Debug Window
L = LBound(CA)
U = UBound(CA)

Debug.Print "Description", "Length", "Width", "Area"
For j = L To U
  With CA(j)
    Debug.Print .strDesc, .dblLength, .dblWidth, .Area
  End With
Next

’stop

'Clear array objects from memory
  Erase CA


End Sub
 

Click somewhere in the middle of the Code and press F5 to execute the Code. Input the Values for Description, Length, and Width from the Keyboard for 5 different Items, one after the other.

The Sample Run of the Program is given below:

Description   Length        Width         Area
Door Mat       5             8             40 
Carpet         12            23            276 
Bed Room       21            23            483 
Store Room     15            25            375 
Terrace        40            50            2000 

Review of Code Line by Line.

Let’s examine the code line by line. In the declarations area, we first declare an object variable:

Dim tmpA As ClsArea

The next line declares an empty array of the same type:

Dim CA() As ClsArea 

The array is left un-sized here — we’ll set its length later in the procedure (typically with ReDim) to hold the required number of ClsArea instances.

In the next two lines, the variable title is declared as a String, and the variables j, L, and U are declared as Long integers.

The title variable is then initialized with the string "ClassArray", which will be used as the prompt text in the InputBox() function.

The For… Next loop is set up with j as the control variable, running five times. After the next two comment lines, the Set statement is used to instantiate (allocate memory for) the ClsArea class object, assigning it to the temporary object variable tmpA.

The following three lines of code assign input values to the strDesc, dblLength, and dblWidth properties of the tmpA object.

After another comment line, the ClsArea object array (CA) is resized with the ReDim statement to hold elements from 1 to j. On the first pass, this creates space for element 1; on subsequent passes, the array is resized to hold elements 1 to 2, 1 to 3, and so on, up to 1 to 5. The Preserve keyword ensures that previously created objects in the array are retained while expanding its size.

Note: The ReDim statement changes the size of the object array, but without the Preserve keyword, it erases any objects already stored in the array. Using Preserve ensures that existing objects remain intact when the array is resized.

The Set statement in the next line copies the tmpA object, along with its data, into the CA(j) object, the newly created j-th element of the array.

Immediately afterward, Set tmpA = Nothing removes the temporary object from memory.

The For…Next loop then repeats this process four more times, allowing input for additional items. Each time, a new tmpA object is created, populated with data, and then stored in the CA array.

After the loop, two lines of code determine the lower and upper bounds of the array (the smallest and largest index values).

Next, a Debug.Print statement prints a heading line in 14-character-wide columns in the Debug window. A second For…Next loop then iterates through the array, using the LBound and UBound values to access each object.

Inside this loop, a With…End With structure is used to simplify referencing the current object (CA(j)). This avoids repeatedly writing CA(j).strDesc, CA(j).dblLength, CA(j).dblWidth, and CA(j).Area. Instead, the property values and the Area() method result are printed more concisely.

Finally, you can remove the comment marker from the Stop statement to pause the program during execution. Run the code again, enter five sets of item details, and experiment with the object array. You can access or modify any property of any object in the array by referencing it with its index number, and you can print the results in the Debug window as shown.


The statement Set CA(j) = Nothing within the For ... Next Loop clears the array of objects, one by one, from Memory.

Pass Class Object Array as Function Parameter.

We can also pass the Class Object Array as a parameter to another program, similar to how we worked with a User-Defined Data Type.

To demonstrate this, let’s separate the printing section of the code from the main program and move it into its own subroutine.

  1. Copy the ClassArray() subroutine code.

  2. Paste it into the same Standard Module.

  3. Rename the new procedure to ClassArray2(), as shown below.

This new subroutine will handle the printing logic independently, while still working with the array of class objects that we pass to it.

Public Sub ClassArray2()
Dim tmpA As ClsArea
Dim CA() As ClsArea
Dim j As Long, title As String
Dim L As Long, U As Long

title = "ClassArray"
For j = 1 To 5 ‘the Loop is set for 5 items
  'instantiate temporary ClsArea Object
  'to enter input
  Set tmpA = New ClsArea

     tmpA.strDesc = InputBox(Str(j) & ") Description:", title, "")
     tmpA.dblLength = InputBox(Str(j) & ") Enter Length:", title, 0)
     tmpA.dblWidth = InputBox(Str(j) & ") Enter Width:", title, 0)
    
  'Redimension Array
  ReDim Preserve CA(1 To j) As ClsArea
  Set CA(j) = tmpA   'Copy Object to Array
  
 Set tmpA = Nothing 'Remove temporary object from memory.
Next

‘PRINTING SECTION
'Print the Array contents into the Debug Window
L = LBound(CA)
U = UBound(CA)

Debug.Print "Description", "Length", "Width", "Area"
For j = L To U
  With CA(j)
    Debug.Print .strDesc, .dblLength, .dblWidth, .Area
  End With
Next

’stop

'Clear array objects from memory
Erase CA

End Sub
 

The ClassArray2 Code Without Printing Section.

Create a new Sub-Routine as given below:

Public Sub ClassPrint(ByRef clsPrint() As ClsArea)
Dim L As Long, U As Long
Dim j As Long

End Sub
 

In the next step, we will separate the printing logic into its own subroutine. To do this, highlight the printing section of the ClassArray2() program, up to the Stop statement, cut it, and paste it below the Dim statements in a new subroutine named ClassPrint(). The revised versions of both programs are shown below:

Public Sub ClassArray2()

Dim tmpA As ClsArea
Dim CA() As ClsArea
Dim j As Long, title As String

title = "ClassArray"
For j = 1 To 5 ‘the Loop is set for 5 items
  'instantiate temporary ClsArea Object
  'to enter input
  Set tmpA = New ClsArea
     tmpA.strDesc = InputBox(Str(j) & ") Description:", title, "")
     tmpA.dblLength = InputBox(Str(j) & ") Enter Length:", title, 0)
     tmpA.dblWidth = InputBox(Str(j) & ") Enter Width:", title, 0)
    
  'Redimension Array
  ReDim Preserve CA(1 To j) As ClsArea
  Set CA(j) = tmpA   'Copy Object to Array
  
 Set tmpA = Nothing 'Remove temporary object from memory.
Next

Call ClassPrint(CA) ‘Pass the Object Array to print routine

'Clear array objects from memory
Erase CA

End Sub

The ClassPrint() Program.

Public Sub ClassPrint(ByRef clsPrint() As ClsArea)
Dim L As Long, U As Long
Dim j As Long

'Printing Section
'Print the Array Contents into the Debug Window.
L = LBound(clsPrint)
U = UBound(clsPrint)

Debug.Print "Description", "Length", "Width", "Area"
For j = L To U
  With clsPrint(j)
    Debug.Print .strDesc, .dblLength, .dblWidth, .Area
  End With
Next

End Sub

Review the code changes in both programs carefully and make corrections wherever necessary. Once ready, place the cursor in the middle of the ClassArray2() code and press F5 to run the program.

In this version, the Call statement in the first program passes the CA Class Object Array as a ByRef parameter to the ClassPrint() subroutine. The subroutine then prints the object properties and invokes the Area() function to calculate and display the results.

In the next session, we will extend our ClsArea class module by using it as a base class to build a new VBA Class Object that calculates the volume of a given item.

  1. MS-Access Class Module and VBA
  2. MS-Access VBA Class Object Arrays
  3. MS-Access Base Class and Derived Objects
  4. VBA Base Class and Derived Objects-2
  5. Base Class and Derived Object Variants
  6. Ms-Access Recordset and Class Module
  7. Access Class Module and Wrapper Classes
  8. Wrapper Class Functionality Transformation
  9. Ms-Access and Collection Object Basics
  10. Ms-Access Class Module and Collection Object
  11. Table Records in Collection Object and Form
  12. Dictionary Object Basics
  13. Dictionary Object Basics-2
  14. Sorting Dictionary Object Keys and Items
  15. Display Records from Dictionary to Form
  16. Add Class Objects as Dictionary Items
  17. Update Class Object Dictionary Item on Form

Share:

MS-Access Class Module and VBA

Introduction.

    Over the past few weeks, we explored how to work with User-Defined Types (UDTs) by creating complex data structures, gaining a fair understanding of their strengths and limitations. If you haven’t reviewed those articles yet, I recommend visiting them through the links below:

  • User-Defined Data Type-2
  • User-Defined Data Type-3

In Microsoft Access, there are two types of VBA Modules.

  1. Standard Modules
  2. Class Modules

We have worked with Class Modules in Forms and Reports in Microsoft Access. These modules typically contain small, event-driven routines—for example, handling button clicks, the BeforeUpdate event, or the Form/Report Current event.

For more complex data-processing tasks, the code is usually placed in Standard Modules. A single Standard Module can contain several unrelated procedures and functions to perform different operations.

Class Modules, however, are different. They are designed to define and manage custom objects, and each Class Module is dedicated to representing a single object.


The Basics of a Stand-alone Class Module.

  1. Let’s create a simple Class Module from scratch to understand the basics.

    1. Start Microsoft Access and open an existing database, or create a new one.

      • If you create a new database, save it in an existing Trusted Location (folder), or add the folder to your list of trusted locations.

      • To add a trusted location:

        • Click the Office ButtonAccess OptionsTrust CenterTrust Center Settings.

        • Add your database folder to the list and click OK.

    2. Open the VBA Editor by pressing Alt + F11.

    3. In the VBA window, click on the Insert menu and select Class Module.

      • A new Class Module will be added to your project, ready for use.

  2. In the left panel, locate the Class Modules folder icon. Beneath it, you will see a class module named Class1

  3. Click on Class1, then open the Properties window from the View menu. Change the name from Class1 to ClsArea—this will be the name of our custom object.

Note: The class name ClsArea becomes the object type. This means that whenever we use this object, we can declare it like a regular variable:

Dim xyz As ClsArea

This is similar to how we declare variables for user-defined data types.

Next, we will add three properties (variables) to the object: Description, Length, and Width. These should be placed at the top of the class module, immediately below the Option Compare Database  Option Explicit Lines. Enter the following lines into the class module:

Option Compare Database
Option Explicit

Public p_Desc as String
Public p_Length as Double
Public p_Width as Double

These variables are recognized as properties of the VBA custom class object and are declared in the global declaration area with Public scope. Save the class module.

Note: Our class module and its code will not remain this simple. Over time, it will evolve with more lines of code. It is important to follow each step carefully to understand the purpose behind each change. At this stage, we are creating a simple class for area calculation (Area = Length * Width). Gradually, we will enhance it, and you will learn why each modification is necessary.

Next, we will write a small program in a standard module to test our new class module. Insert a standard module via the Insert menu, then type or copy and paste the following code into the module, replacing any existing content:

Option Compare Database
Option Explicit

Public Function ClassTest1()
Dim oArea As ClsArea

Set oArea = New ClsArea

oArea.

Set oArea = Nothing

End Function 

The Dim statement is used to declare an object variable, similar to how we declare a regular variable—for example:

Dim Desc As String

However, in this case, we are not declaring an ordinary variable, like String or Integer. We are declaring an object variable oArea of our class ClsArea. Because it is an object, a simple Dim statement alone is not sufficient; it does not allocate memory for the object or its properties.

The member variables we previously declared in the ClsArea class are known as properties of the ClsArea object. To use these properties, the object itself must be loaded (instantiated) into memory. 

The Set statement, combined with the New keyword, is required to create an instance of the ClsArea class in memory, referenced by the object variable oArea. By doing this, we allocate memory and make the object ready for use.

Multiple instances of the same class can be created in this way, each independent of the others. (We will explore this in detail later.) Once instantiated, we can store values in its properties—p_Desc, p_Length, and p_Width.

 p_ The prefix is used here to indicate that these variables are intended as private members of the class (i.e., not visible outside the class module if declared with the Private keyword). However, in our current example, they are declared as Public, which makes them accessible outside the class. The actual variable names can be any valid identifiers; the prefix is simply a naming convention for clarity.

Note: At this stage, we have not yet declared the variables as Private. We will make that change later as the class evolves.

To create an object instance, use the Set statement. Immediately after the keyword 'Set', specify the local object name (you may choose any valid variable name, following standard naming rules), then an equal sign, the keyword 'New', and finally the class module name (ClsArea). This creates an instance of the ClsArea object in memory, complete with all its properties (variables).

There is also a shortcut. The two lines of code required for declaration and instantiation can be combined into a single statement, as shown below:

Dim oArea As ClsArea
Set oArea = New ClsArea

'the shortcut to the above two statements
Dim oArea As New ClsArea

When you type the next line of oArea followed by a dot (.) separator, the following display will appear to show the list of available Custom Object Properties to select from and assign appropriate values to them.

If the list of properties and methods does not appear as shown above, open the Options dialog from the Tools menu and, on the Edit tab, enable the Auto List Members option.

Before exiting the function, always include the statement:

Set oArea = Nothing

This explicitly releases the memory allocated to the ClsArea object, making it available for other uses.

All operations involving the instantiated custom object should be written between the first Set statement (where the object is created) and the final Set ... = Nothing statement (where the object is released).


The ClsArea Class Object Test Program.

The completed Class Testing Program Code is given below:

Option Compare Database
Option Explicit

Public Function ClassTest1()
Dim oArea As ClsArea

Set oArea = New ClsArea

oArea.p_Desc = "Carpet"
oArea.p_Length = 25
oArea.p_Width = 15

Debug.Print "Description", "Length", "Width"

Debug.Print oArea.p_Desc, oArea.p_Length, oArea.p_Width

Set oArea = Nothing

End Function

Click somewhere in the middle of the Code and press F5 to run the program. The Run of the program is given below for reference.

Description   Length        Width
Carpet         25            15 

Public|Private Scope of Object Properties.

Our simple class module object has a few drawbacks that we now need to address.

The first issue is that all variables (properties) have been declared with the Public scope. This makes them directly accessible from other VBA programs, meaning their values can be changed freely—something we do not want.

The second issue is the lack of validation. Currently, the class accepts any values, including invalid ones such as negative or zero. We must introduce validation checks on values entered before they are assigned to these variables.

The first problem can be solved easily by changing the variable declarations from Public to Private. However, once declared Private We cannot access them directly. Instead, we need an indirect mechanism to store and retrieve values. This is precisely what Property proceduresGet and Let—are designed for. We will create these procedures for each property of the object.

Let’s now make these changes in the class module.

Open the Class Module ClsArea. Change the scope from Public to Private for all three variables.

Creating Property Procedures

Select Procedure from the Insert Menu, type strDesc in the Name text control, select Property in the Type Option Group, and Public in the Scope option group.  Click OK to insert the Property Procedures for the Private p_Desc Variable (Property).

Option Compare Database
Option Explicit

Private p_Desc As String
Private p_Length As Double
Private p_Width As Double

Public Property Get strDesc() As String
   strDesc = p_Desc 'return the value from p_Desc
End Property

Public Property Let strDesc(ByVal strNewValue As String)
   p_Desc = strNewValue ‘store the value into p_Desc
End Property

Both the Get and Let procedures are declared as Public Property procedures, and both use the same name: strDesc. By default, the Get procedure returns a Variant type, and the parameter in the Let procedure is also created as a Variant. However, we can (and should) change these to more specific data types as needed. In our case, we changed them to the String type.

The naming convention also helps: the str prefix in strDesc indicates that this property expects a String value. Likewise, we should rename the parameter variable in the Let procedure from vNewValue to strNewValue for clarity.

When a property is added, Access inserts both the Get and Let procedures as a pair.

Now, let’s take a closer look at the code. In the Get procedure, the procedure name (strDesc) acts like a variable on the left side of the equals sign (=). It returns the value of the private variable p_Desc to the calling program.

In the Let procedure, strDesc accepts a String value through its parameter (strNewValue). The value passed in is then stored in the private property p_Desc.

The key point here is that there is no direct access to our private property 'p_Desc' from outside the class. All data transfer to and from p_Desc must go through the Get/Let procedures. The Let Property Procedure can perform validation checks before assigning values to private Properties (We will add these checks shortly).

Finally, remember that the Get or Let procedure is executed automatically, depending on whether you are reading from or writing to the property in your VBA code.

The Get procedure is executed when we use the Property Name in an expression in the following manner:

‘ Reads the value from p_Desc to Print

Debug.Print oArea.strDesc 'Property Get strDesc() is called

OR

‘ Reads the value from p_Desc and assigns it to the variable X

X = oArea.strDesc 'Property Get strDesc() is called for the value

The Let Property Procedure is run when we try to assign a value to the Property Name. Check the example expression in our Test Program below:

oArea.strDesc = “Carpet” 'Property Let strDesc() is executed

In earlier BASIC Language books, you can see the usage of the keyword LET.

LET X = 25 ‘ LET is optional

Since it was optional, the statement works without it, and I stopped using it.

If you are only reading value from a variable and not storing anything directly, then you can omit the Let Property Procedure and write only the Get Property Procedure.

This rule applies to the Let Property procedure.  You may use only the Let Procedure if you are assigning some value to a Private Variable, but not reading anything back from the same variable from outside, then omit the Get Procedure.

The Get and Let Property Procedures will run one after the other if our expression is something like the following:

oArea.strDesc = oArea.strDesc & “ – King Size.”

In the above expression, get the existing value from the Private Variable p_Desc, modify the description, and store it back into the same variable.  In short, in an expression, if you use the Property name to the right of the equal sign (=) the Get Property Procedure is called, and the Let Property Procedure is run when the object property procedure name appears to the left of the equal (=) sign.

The Let and Get Property Procedures should use the same Procedure Name:

Public Property Get strDesc() As String
   strDesc = p_Desc 'return the value from p_Desc
End Property

Public Property Let strDesc(ByVal strNewValue As String)
   p_Desc = strNewValue ‘store the value into p_Desc
End Property

Next, insert two additional sets of Property procedures for the variables p_Length and p_Width.

When assigning the procedure names in the Name control, use dblLength and dblWidth. The dbl prefix indicates to the user that these properties expect Double-precision numbers as input.

ClsArea Class Object with its Property Procedures.

The completed code so far, with dblLength and dblWidth Property Procedures, is given below for reference and to update your code.

Option Compare Database
Option Explicit

Private p_Desc As String
Private p_Length As Double
Private p_Width As Double

Public Property Get strDesc() As String
  strDesc = p_Desc 'copy the value from p_Desc
End Property

Public Property Let strDesc(ByVal strNewValue As String)
  p_Desc = strNewValue
End Property

Public Property Get dblLength() As Double
  dblLength = p_Length
End Property

Public Property Let dblLength(ByVal dblNewValue As Double)
  p_Length = dblNewValue
End Property

Public Property Get dblWidth() As Double
  dblWidth = p_Width
End Property

Public Property Let dblWidth(ByVal dblNewValue As Double)
  p_Width = dblNewValue
End Property

The Test Program with the Changes.

If you have completed updating the above code, then let us make changes to our test program to reflect the changes made here.  The modified sample code is given below.

Option Compare Database
Option Explicit

Public Function ClassTest1()
Dim oArea As ClsArea

Set oArea = New ClsArea

‘Property Let procedures called here

oArea.strDesc = "Carpet"
oArea.dblLength = 25
oArea.dblWidth = 15

Debug.Print "Description", "Length", "Width"

‘Property Get Procedures called here to print
Debug.Print oArea.strDesc, oArea.dblLength, oArea.dblWidth

Set oArea = Nothing

End Function

When you type a dot (.) immediately after the object name—for example, oArea.—VBA IntelliSense displays a list of the available property procedure names. From this list, you can simply select the required property instead of typing it manually.

The purpose of our class object is to calculate the area of something—such as a room, carpet, floor tile, or any other item that has measurable length and width. To achieve this, we need to add a Public Function to the class. This function will calculate the area based on the Length and Width values entered into the class object, while also making use of the descriptor value for context.

ClsArea Object Method: Area()

Here is the Code for the Public Function:

Public Function Area() As Double
   Area = Me.dblLength * Me.dblWidth
End Function

You can insert this function into the ClsArea class module using the Insert menu. In the Name control, enter Area; select Function in the Type option group; and choose Public as the scope. Then, complete the function by adding the calculation line inside it.

Since the Area() function resides within the class module, we could directly reference the private variables p_Length and p_Width in the calculation. However, we will take the proper approach and use the corresponding Get procedures (dblLength and dblWidth).

Notice the use of the keyword Me (as in Me.dblLength and Me.dblWidth). Just like in Form or Report class modules, 'Me' refers to the current object instance in memory. Because multiple instances of our custom class object can exist simultaneously, 'Me' ensures that the function works with the correct instance of the object.

The Test Function with Modification.

Modify our Test Function ClassTest1() to incorporate the Area() function output as below:

Option Compare Database
Option Explicit

Public Function ClassTest1()
Dim oArea As ClsArea

Set oArea = New ClsArea

oArea.strDesc = "Carpet"
oArea.dblLength = 25
oArea.dblWidth = 15

Debug.Print "Description", "Length", "Width", "Area"
Debug.Print oArea.strDesc, oArea.dblLength, oArea.dblWidth, oArea.Area

Set oArea = Nothing

End Function

The change is only in the Debug.Print statements.  Run the code and check the Debug Window for the result.

There are two Event Procedures required in the Custom Class Modules: Class_Initialize() and Class_Terminate()

Auto-Executing Methods.

The Class_Initialize() procedure is executed automatically whenever an object is instantiated with the New keyword. This procedure is often used to assign default values to variables or to instantiate other objects in memory. A class object can also make use of other classes as child objects, which may need to be instantiated during initialization. We will explore this aspect in more detail later.

The Class_Terminate() procedure is similar in concept to the Form_Unload() event procedure. It runs automatically before the class object is closed. Normally, the statement Set oArea = Nothing is sufficient to explicitly release a class object from memory.

As a good programming practice, always include it Set oArea = Nothing as the final executable statement in your procedures. This ensures that the object is properly cleared from memory and that system resources are released.

We will add the above programs to our Class Module.  Add the following code at the end of your Class Module:

Private Sub Class_Initialize()
   p_Length = 0
   p_Width = 0

   'MsgBox "Initialize.", vbInformation, "Class_Initialize()"
End Sub

Private Sub Class_Terminate()
   'MsgBox "Terminate.", vbInformation, "Class_Terminate()"
End Sub

If you would like to test these two subroutines, then remove the Comment symbol and make the MsgBox active.  Run your test program one more time.  You will find the Initialize message appears in the beginning (Click OK to continue)  and the Terminate message appears at the end of the Test Program.

I know what you have in your mind by now, like “So much code for multiplying two variables together”.  It is true from that perspective, but we will be writing code for similar problem-solving issues repeatedly every time, duplicating code for validation checks and for other logical error safeguards.

At this stage, we are not simply writing an ordinary program—we are developing a custom object. Once created, this object can be reused multiple times or even embedded within other objects, wherever needed, without the user having to worry about its internal workings.

This is the same principle behind Microsoft Access’s many built-in objects and functions. We use them every day—by setting their properties or passing parameters—to get work done, without needing to know how they are implemented behind the scenes. Our custom class object follows the same approach.

There is one more issue we need to address: adding validation checks to the values passed into the dblNewValue parameter of the Let property procedures for dblLength() and dblWidth(). These checks are necessary to ensure that only valid values are assigned to the object properties p_Length and p_Width.

Specifically, negative or zero values should be treated as invalid. To prevent this, we must add safeguards that ensure only valid, positive values are assigned to the object properties before any calculations are performed.

Performing Validation Checks.

The modified Let Property Procedure Code segments are given below.  Make changes in your code accordingly.

Public Property Let dblLength(ByVal dblNewValue As Double)
   Do While dblNewValue <= 0
      dblNewValue = InputBox("Negative/0 Values Invalid:", "dblLength()", 0)
   Loop

   p_Length = dblNewValue
End Property


Public Property Let dblWidth(ByVal dblNewValue As Double)
   Do While dblNewValue <= 0
       dblNewValue = InputBox("Negative/0 Values Invalid:", "dblwidth()", 0)
   Loop

   p_Width = dblNewValue
End Property

The Do While. . . Loop runs repeatedly till a valid value (greater than 0) is entered into dblNewValue by the User

Validation Checks in the Public Method: Area()

We need one more validation check in the Area() Function.  If the user calls the Area() function without entering valid values for Length and Width first, then the User must be informed about it.  We will check whether the p_Length and p_Width variables have valid values before running the expression for area calculation.  Here is the Code:

Public Function Area() As Double

   If (p_Length > 0) And (p_Width > 0) Then
       Area = p_Length * p_Width
   Else
       Area = 0
       MsgBox "Error: Length/Width Value(s) Invalid., Program aborted."
   End If

End Function

The Complete Code of ClsArea Object.

The fully completed Code of our Class Module ClsArea is given below:

Option Compare Database
Option Explicit

Private p_Desc As String
Private p_Length As Double
Private p_Width As Double

Public Property Get strDesc() As String
    strDesc = p_Desc 'copy the value from p_Desc
End Property

Public Property Let strDesc(ByVal strNewValue As String)
    p_Desc = strNewValue
End Property

Public Property Get dblLength() As Double
   dblLength = p_Length
End Property

Public Property Let dblLength(ByVal dblNewValue As Double)
    Do While dblNewValue <= 0
       dblNewValue = InputBox("Negative/0 Values Invalid:", "dblLength()", 0)
    Loop

    p_Length = dblNewValue
End Property

Public Property Get dblWidth() As Double
   dblWidth = p_Width
End Property

Public Property Let dblWidth(ByVal dblNewValue As Double)
    Do While dblNewValue <= 0
       dblNewValue = InputBox("Negative/0 Values Invalid:", "dblwidth()", 0)
    Loop

    p_Width = dblNewValue
End Property

Public Function Area() As Double

   If (p_Length > 0) And (p_Width > 0) Then
       Area = p_Length * p_Width
   Else
       Area = 0
       MsgBox "Error: Length/Width Value(s) Invalid., Program aborted."
   End If

End Function

Private Sub Class_Initialize()
   p_Length = 0
   p_Width = 0
End Sub

Private Sub Class_Terminate()
   'Exit
End Sub

Testing Property Procedures and Methods.

You may test our Custom Class Object by entering negative or 0 values as input to the dblLength and dblWidth Properties. 

In the Test Program, commented out the lines (oArea.dblLength=25 and oArea.dblWidth=15) to test the Area() function. It should display the Error message we have written within the function.

Our Area Calculation Class Object is now considered complete, and we have tested and found it working correctly, with the following VBA Function:

Public Function ClassTest2()
Dim oArea As ClsArea

Set oArea = New ClsArea

oArea.strDesc = "Carpet"
oArea.dblLength = 0
oArea.dblWidth = 15

Debug.Print "Description", "Length", "Width", "Area"
Debug.Print oArea.strDesc, oArea.dblLength, oArea.dblWidth, oArea.Area

Set oArea = Nothing

End Function

You may test it further for any logical errors that I have overlooked. If you come across anything that I didn't anticipate, please share it with me.

Future Plan for Testing.

So far, we have tested the class object with only a single item. In practice, however, we often need to calculate the area for multiple items—for example, five bedrooms or ten carpets of different sizes. Once a class object is developed, it can be instantiated multiple times in memory, with each instance holding its own unique set of values. This makes it possible to instantiate the same object multiple times, while keeping the values in each instance completely independent.

In addition, this object can serve as a building block for other objects we develop. By reusing the functionality already implemented in the ClsArea class module, we can minimize the amount of new code required and make our solutions both simpler to maintain and more efficient to build.

Next week, we will learn how to create an Array of Custom Objects to calculate the area of several items.

  1. MS-Access Class Module and VBA
  2. MS-Access VBA Class Object Arrays
  3. MS-Access Base Class and Derived Objects
  4. VBA Base Class and Derived Objects-2
  5. Base Class and Derived Object Variants
  6. Ms-Access Recordset and Class Module
  7. Access Class Module and Wrapper Classes
  8. Wrapper Class Functionality Transformation
  9. Ms-Access and Collection Object Basics
  10. Ms-Access Class Module and Collection Object
  11. Table Records in Collection Object and Form
  12. Dictionary Object Basics
  13. Dictionary Object Basics-2
  14. Sorting Dictionary Object Keys and Items
  15. Display Records from Dictionary to Form
  16. Add Class Objects as Dictionary Items
  17. Update Class Object Dictionary Item on Form

Share:

PRESENTATION: ACCESS USER GROUPS (EUROPE)

Translate

PageRank

Post Feed


Search

Popular Posts

Blog Archive

Powered by Blogger.

Labels

Forms Functions How Tos MS-Access Security Reports msaccess forms Animations msaccess animation Utilities msaccess controls Access and Internet MS-Access Scurity MS-Access and Internet Class Module External Links Queries Array msaccess reports Accesstips WithEvents msaccess tips Downloads Objects Menus and Toolbars Collection Object MsaccessLinks Process Controls Art Work Property msaccess How Tos Combo Boxes Dictionary Object ListView Control Query VBA msaccessQuery Calculation Event Graph Charts ImageList Control List Boxes TreeView Control Command Buttons Controls Data Emails and Alerts Form Custom Functions Custom Wizards DOS Commands Data Type Key Object Reference ms-access functions msaccess functions msaccess graphs msaccess reporttricks Command Button Report msaccess menus msaccessprocess security advanced Access Security Add Auto-Number Field Type Form Instances ImageList Item Macros Menus Nodes RaiseEvent Recordset Top Values Variables Wrapper Classes msaccess email progressmeter Access2007 Copy Excel Export Expression Fields Join Methods Microsoft Numbering System Records Security Split SubForm Table Tables Time Difference Utility WScript Workgroup database function msaccess wizards tutorial Access Emails and Alerts Access Fields Access How Tos Access Mail Merge Access2003 Accounting Year Action Animation Attachment Binary Numbers Bookmarks Budgeting ChDir Color Palette Common Controls Conditional Formatting Data Filtering Database Records Defining Pages Desktop Shortcuts Diagram Disk Dynamic Lookup Error Handler External Filter Formatting Groups Hexadecimal Numbers Import Labels List Logo Macro Mail Merge Main Form Memo Message Box Monitoring Octal Numbers Operating System Paste Primary-Key Product Rank Reading Remove Rich Text Sequence SetFocus Summary Tab-Page Union Query User Users Water-Mark Word automatically commands hyperlinks iSeries Date iif ms-access msaccess msaccess alerts pdf files reference restore switch text toolbar updating upload vba code