Practical Examples: Class Modules
To best understand how to benefit from the use of Class modules, it is beneficial to see them in action. This chapter covers three examples. The first shows the use of a file information class. Each instance of the class is used to house information about a particular file. The second illustrates how the use of a customer class facilitates the process of dealing with customer data. The third is a system information class. It retrieves and then provides information about the computer system. An additional example of the use of Class modules is found in Chapter 16. It shows how a custom error class facilitates the process of implementing error handling within your application.
The FileInformation Class
![]() | It is a common application requirement to be able to extract the drive, path, or short filename from a complete filename. Although you can obtain these pieces of information using a Standard Code module and functions, placing the functions in a Class module makes them easier to work with. The FileInformation class contained in the Chap13Ex sample database contains four properties: FullFileName, Drive, Path, and Name. The user of the class sets the FullFileName property. It contains the complete filename and path of the file whose parts the user wishes to extract. The Drive, Path, and Name properties of the class contain the drive, path, and name of the file specified in the FullFileName property. Listing 13.1 shows the Property Let and Property Get procedures, as well as the Private variables associated with these properties. |
Listing 13.1 The Property Declarations for the FileInformation Class
Private mstrFullFileName As String
Private mstrDrive As String
Private mstrPath As String
Private mstrName As String
Public Property Get FullFileName() As String
FullFileName = mstrFullFileName
End Property
Public Property Let FullFileName(ByVal strFileName As String)
Call GetDrive(strFileName)
Call GetPath(strFileName)
Call GetName(strFileName)
End Property
Public Property Get Drive() As String
Drive = mstrDrive
End Property
Public Property Get Path() As String
Path = mstrPath
End Property
Public Property Get Name() As String
Name = mstrName
End Property
Notice that the Drive, Path, and Name properties have no associated Property Let routines. This is because these properties are read-only properties from outside the class. When the code sets the FullFileName property, it executes the GetDrive, GetPath, and GetName routines. Each of these routines populates the appropriate Private variables so that they can be retrieved in the Property Get routines. Listing 13.2 shows the GetDrive, GetPath, and GetName subroutines.
Listing 13.2 The GetDrive, GetPath, and GetName Routines
Private Sub GetDrive(ByVal strFile As String)
'Everything before the : is the drive
mstrDrive = Left$(strFile, _
InStr(strFile, ":"))
End Sub
Private Sub GetPath(ByVal strFile As String)
'Everything up until the last backslash
'is the path
mstrPath = _
Mid(strFile, 1, InStrRev(strFile, "\"))
End Sub
Private Sub GetName(strFile)
'Everything after the last backslash
'is the name
mstrName = _
Mid$(strFile, InStrRev(strFile, "\") + 1)
End Sub
The GetDrive routine extracts the characters to the left of the colon, including the colon, thereby extracting the drive. The GetPath routine locates the last backslash in the filename. The string to the left of the last backslash contains the path name. Finally, the GetName routine extracts everything to the right of the last backslash.Note that the GetDrive, GetPath, and GetName routines are private to the Class module. This means that their code cannot be executed from outside the Class module. The code shown in Listing 13.3 illustrates how the code within the Class module is used.
Listing 13.3 Using the FileInformation Class
Private Sub cmdGetFileInfo_Click()
'Declare a FileInformation object
Dim objFile As FileInformation
'If the txtFullFileName text box is null,
'display a message and bail out
If IsNull(Me.txtFullFileName.Value) Then
MsgBox "File Name Must Be Entered"
'If the filename is entered, instantiate the
'FileInformation class
Else
Set objFile = New FileInformation
With objFile
'Set the FullFileName property of the class
'this causes the Drive, Path, and Name properties
'to be populated
.FullFileName = Me.txtFullFileName
'Extract the values of the Drive, Path, and Name
'properties and display them in text boxes
Me.txtDrive = .Drive
Me.txtPath = .Path
Me.txtName = .Name
End With
End If
End Sub
Using a Collection to Manipulate Multiple Instances of the FileInformation Class
The idea of using a collection to manipulate multiple instances of a class was discussed in the "Working with Custom Collections" section of this chapter. It is illustrated in Listing 13.4.
Listing 13.4 Using a Collection to Manipulate Multiple Instances of the FileInformation Class
Sub FileInfoCollection(strDirName As String)
'Declare a Collection object
Dim colFiles As Collection
'Declare a FileInformation object
Dim objFileInfo As FileInformation
Dim strFile As String
Dim vntFile As Variant
'Instantiate the Collection object
Set colFiles = New Collection
'Return the first file that meets the file spec
strFile = Dir(strDirName)
'Loop as long as files meet the file spec
Do Until Len(strFile) = 0
'Instantiate a FileInformation object
Set objFileInfo = New FileInformation
'Set its FullFileName property
objFileInfo.FullFileName = strDirName & strFile
'Add that instance of the FileInformation class
'to the Collection object
colFiles.Add objFileInfo
'Find the next file that meets the criteria
strFile = Dir()
Loop
'Loop through the collection, extracting the Drive,
'Path, and Name properties
For Each vntFile In colFiles
Debug.Print vntFile.Drive, vntFile.Path, vntFile.Name
Next vntFile
End Sub
The code receives a directory path (including a trailing backslash) as a parameter. It creates and instantiates a Collection object. It then executes the Dir function, which retrieves the name of the first file in the specified directory. As long as it finds at least one file, it executes the code within the Do Until loop. The code creates an instance of the FileInformation class. The FullFileName property of the instance is then set equal to the directory name concatenated with the filename. The most important line of code in the routine is then executed, adding the instance of the FileInformation class to the collection. This enables the instance to persist. The code calls the Dir function to retrieve the name of the next file in the specified directory, and the process is repeated until no additional filenames are located.After the code adds all the instances of the FileInformation class to the collection, it uses the For...Each loop to iterate through all items in the collection. It retrieves the Drive, Path, and Name properties of each item in the collection and prints them to the Debug window. Notice that the code uses a variant variable to iterate through the elements of the Collection object.
The Data Access Class
Building a data access class greatly facilitates the process of dealing with data, particularly when the data within a table is accessed from numerous forms or numerous databases. By encapsulating the data access activities into a Class module, you can better ensure that all the forms and applications treat the data consistently. Each field within the table becomes a property of the class. This is illustrated by the private declarations and Property Let and Property Get routines shown in Listing 13.5.
Listing 13.5 The Private Variables and Property Let and Property Get Routines Used by the Data Access Class
Private mlngClientID As Long
Private mstrCompanyName As String
Private mstrAddress As String
Private mstrCity As String
Private mconn As ADODB.Connection
Private mrst As ADODB.Recordset
Private mboolAddFlag As Boolean
Public Property Get ClientID() As Long
ClientID = mlngClientID
End Property
Public Property Get CompanyName() As String
CompanyName = mstrCompanyName
End Property
Public Property Let CompanyName(ByVal strCompanyName As String)
mstrCompanyName = strCompanyName
End Property
Public Property Get Address() As String
Address = mstrAddress
End Property
Public Property Let Address(ByVal strAddress As String)
mstrAddress = strAddress
End Property
Public Property Get City() As String
City = mstrCity
End Property
Public Property Let City(ByVal strCity As String)
mstrCity = strCity
End Property
Public Property Get AddFlag() As Boolean
AddFlag = mboolAddFlag
End Property
Public Property Let AddFlag(ByVal boolAddFlag As Boolean)
mboolAddFlag = boolAddFlag
End Property
The Initialize event of the class, shown in Chapter 14, "What Are ActiveX Data Objects and Data Access Objects, and Why Are They Important?" For now, it's only important to understand the basics. The example sets the LockType of the recordset to adLockOptimistic, and the CursorType of the recordset to adOpenDynamic. The combination of these two property settings renders the recordset's data updateable.
Listing 13.6 The Initialize Event of the Client Class
Private Sub Class_Initialize()
'Instantiate the Recordset object
Set mrst = New ADODB.Recordset
'Set the LockType and CursorType of the
'recordset to render it updateable
mrst.LockType = adLockOptimistic
mrst.CursorType = adOpenDynamic
'Open a recordset based on the tblClients table,
'utilizing the connection associated with the current project
mrst.Open "tblClients", _
CurrentProject.Connection, _
Options:=adCmdTable
'Call the Scatter routine to populate the controls on the form
'with the first row from the recordset
Call Scatter
End Sub
After the code opens the recordset, the contents of the first record in the recordset must be available as properties of the class. This is necessary so that the contents of the first record can be displayed in the frmClients form. The Scatter method, shown in Listing 13.7, accomplishes this task.
Listing 13.7 The Scatter Method of the Client Class
Public Sub Scatter()
'Take the field values from the current row
'and place them into private variables
With mrst
mlngClientID = !ClientID
mstrCompanyName = !CompanyName
mstrAddress = !Address
mstrCity = !City
End With
End Sub
The Scatter method simply takes the contents of the fields in the current record (in this case, the first record) and stores them in Private variables that are accessed by the Property Get and Property Let routines within the class. The variables are then used by the Form_Load event of the frmClients form, shown in Listing 13.8.
Listing 13.8 The Form_Load Routine of the frmClients Form
Private Sub Form_Load()
'Instantiate the Client Class
Set mobjClients = New Client
'Grab the values out of the class
'properties to populate the text boxes
With mobjClients
Me.txtClientID = .ClientID
Me.txtCompanyName = .CompanyName
Me.txtAddress = .Address
Me.txtCity = .City
End With
End Sub
The Form_Load event instantiates the Client class, causing the Initialize event of the class to execute. The Scatter method of the class executes, and then the code populates the text boxes on the form with the contents of the ClientID, CompanyName, Address, and City properties of the class. The frmClient form, populated with data from the first record in the tblClients table, is shown in Figure 13.3.
Figure 13.3. The frmClients form is used to display and manipulate data in the tblClients table.

After the code displays the first record, the user can opt to move to the next record in the recordset. Listing 13.9 shows the Click event of the cmdNext command button on the frmClients form, which calls the MoveNext method of the class and then displays the contents of the class's properties.
Listing 13.9 The Click Event of the cmdNext Command Button
Private Sub cmdNext_Click()
With mobjClients
'Execute the MoveNext method of the class
.MoveNext
'Populate the text boxes with the
'property values of the class
Me.txtClientID = .ClientID
Me.txtCompanyName = .CompanyName
Me.txtAddress = .Address
Me.txtCity = .City
End With
End Sub
The cmdNext click event calls the MoveNext method of the class. The MoveNext method is responsible for moving forward from record to record within the class. It uses the module-level recordset object set in the Initialize event of the class. This appears in Listing 13.10.
Listing 13.10 The MoveNext Method of the Client Class
Public Sub MoveNext()
With mrst
'Determine if at end of file
If Not .EOF Then
'If not at EOF, move next
.MoveNext
'Evalute to see if movement
'caused us to be at EOF
'If so, move back to the last row
If .EOF Then
.MoveLast
End If
End If
'Once on the correct row,
'call Scatter routine to populate the properties
Call Scatter
End With
End Sub
The MoveNext method first tests to see whether the end of the recordset has been reached. If not, the MoveNext method is used to move to the next record in the recordset. If the end of the recordset is encountered, the code moves back to the last record. The Scatter method is called to update the values of the module-level variables. The cmdNext Click event then retrieves these values via the Property Get routines, to update the data displayed on the form.The cmdPrevious Click event of the frmClients form is similar to the cmdNext Click event. In Listing 13.11, it calls the MovePrevious method of the class and then displays the contents of the class's properties.
Listing 13.11 The Click Event of the cmdPrevious Command Button
Private Sub cmdPrevious_Click()
With mobjClients
'Execute the MovePrevious method of the class
.MovePrevious
'Populate the text boxes with the
'property values of the class
Me.txtClientID = .ClientID
Me.txtCompanyName = .CompanyName
Me.txtAddress = .Address
Me.txtCity = .City
End With
End Sub
The Click event of the cmdPrevious command button first calls the MovePrevious method of the class. The MovePrevious method of the class is similar to the MoveNext method. In Listing 13.12, it moves to and displays the previous record in the recordset.
Listing 13.12 The MovePrevious Method
Public Sub MovePrevious()
With mrst
'Determine if at Beginning of file
If Not .BOF Then
'If not at BOF, move next
.MovePrevious
'Evalute to see if movement
'caused us to be at BOF
'If so, move back to the first row
If .BOF Then
.MoveFirst
End If
End If
'Once on the correct row,
'call Scatter routine to populate the properties
Call Scatter
End With
End Sub
The MovePrevious method first tests to see whether the record pointer is before the first record in the recordset. If not, the MovePrevious method moves to the previous record in the recordset. If it encounters the beginning of the recordset, the code moves forward to the first record. The code calls the Scatter method to update the values of the module-level variables. These variables are then retrieved by the Property Get routines initiated by the Click event of the cmdPrevious command button.The Client class enables the user to edit the data in the underlying recordset. The user simply enters data into the form's unbound text boxes. After users enters the data, they click Save. The Click event of the cmdSave command button saves the form's data to the underlying recordset, as shown in Listing 13.13.
Listing 13.13 The Click Event of the cmdSave Command Button
Private Sub cmdSave_Click()
'Ensure that the txtCompany text box is populated
If IsNull(Me.txtCompanyName.Value) Or _
Len(Me.txtCompanyName.Value) = 0 Then
MsgBox "Company Name Must be Filled In Before Proceeding"
Else
'If txtCompany text box is populated,
'populate the properties of the class
'with values in the text boxes
With mobjClients
.CompanyName = Me.txtCompanyName
.Address = Me.txtAddress
.City = Me.txtCity
'Execute the Save method of the class to write
'the record to disk
.Save
'Reset the Add Flag
.AddFlag = False
'Populate the txtClientId text box with the
'ClientID assigned by the Add method
Me.txtClientID = .ClientID
End With
End If
End Sub
The code in the Click event of the cmdSave command button first sets all the properties of the class to the corresponding text box values. It then executes the Save method of the class (see Listing 13.15).Before we look at the Save method, it is important to explore the code under the Click event of the cmdNew command button (see Listing 13.14). It's very simple: It clears the text boxes on the form, readying them for the entry of the new data. It then sets the value of the AddFlag to True.
Listing 13.14 The Click Event of the cmdNew Command Button
Private Sub cmdNew_Click()
'Clear the text box values
Me.txtClientID = "
Me.txtCompanyName = "
Me.txtAddress = "
Me.txtCity = "
'Set the Add flag
mobjClients.AddFlag = True
End Sub
In the case of either an edit or an add, the code in the Save method of the class actually writes the data from the new record to disk. This code is shown in Listing 13.15.
Listing 13.15 The Save Method of the Class
Public Sub Save()
'If add flag is true, call AddNew routine
'otherwise, call Edit routine
If mboolAddFlag Then
Call AddNew
Else
Call Edit
End If
End Sub
The Save method of the class first determines whether the user is adding or editing data. This is determined by evaluating the mboolAddFlag. The code sets the mboolAddFlag to True when the user clicks the Add button. When editing, the value of the variable is False. If the user is adding the record, the code executes the private routine called AddNew, which appears in Listing 13.16.
Listing 13.16 The AddNew Method of the Class
Private Sub AddNew()
With mrst
'Add a new row to the recordset,
'populating it with values from the
'class properties
.AddNew
!CompanyName = mstrCompanyName
!Address = mstrAddress
!City = mstrCity
.Update
'Set the ClientID property equal
'to the ClientID of the inserted row
mlngClientID = !ClientID
End Sub
The AddNew method of the class uses the AddNew method of an ADO recordset to populate a new record with the values contained in the Private variables. The Update method of the recordset object writes the new data to disk. When the Update method executes, the value of the AutoNumber field is assigned and stored in the variable called mlngClientID. This variable is retrieved in the Click event of cmdSave so that the txtClientID text box contains the appropriate value.Whereas the AddNew method of the class adds the record in the recordset, the Edit method of the class updates the data in an existing record. It appears in Listing 13.17.
Listing 13.17 The Edit Method of the Class
Private Sub Edit()
'Edit the current row, setting the field
'values equal to the values in the
'class properties
With mrst
!CompanyName = mstrCompanyName
!Address = mstrAddress
!City = mstrCity
.Update
End With
End Sub
The Edit method uses the Update method of the ADO recordset to take the values in the module-level variables and write them to disk.The last data task associated with the cmdClients form provides the user with the capability to delete a record from the recordset. The code behind the Click event of the cmdDelete command button appears in Listing 13.18.
Listing 13.18 The Click Event of the cmdDelete Command Button
Private Sub cmdDelete_Click()
With mobjClients
'Execute the Delete method of the class
.Delete
'Populate the controls on the form with
'the property values of the class
Me.txtClientID = .ClientID
Me.txtCompanyName = .CompanyName
Me.txtAddress = .Address
Me.txtCity = .City
End With
End Sub
This code executes the Delete method of the class, shown in Listing 13.19. It uses the Delete method of an ADO recordset to delete the current record from the recordset. After the deletion, the record pointer is sitting on the deleted record. The MoveNext method of the class moves the record pointer to the next valid record. The Click event of the cmdDelete command button then populates the text boxes on the form with the values of the record that the MoveNext method moved to.
Listing 13.19 The Delete Method of the Class
Public Sub Delete()
'Delete the current row
.Delete
'Move off the deleted row
Call MoveNext
End Sub
The SystemInformation Class
The process of obtaining system information, such as the amount of free drive space, is usually a somewhat tedious and difficult process. This information is generally available only through the Windows API, covered in Chapter 23, "Exploiting the Power of the Windows API." The execution of Windows API functions is best left to more advanced developers. So how can a junior developer access this important information? If the senior developer encapsulates the complex functionality of the Windows API calls in a Class module, the junior developer can obtain the system information as properties of the class.The class called SystemInformation is responsible for obtaining information about the hardware, operating system, and system resources. To obtain this information, the Declare statements, type structure declarations, and constant declarations are included in the General Declarations section of the Class module (see Listing 13.20).
Listing 13.20 The Private Variables and Type Structures Required by the SystemInformation Class
Private Declare Sub GlobalMemoryStatus _
Lib "Kernel32" (lpBuffer As MEMORYSTATUS)
Private mlngTotalMemory As Long
Private mlngAvailableMemory As Long
Private mstrOSVersion As String
Private msngOSBuild As Single
Private mstrOSPlatform As String
Private mlngProcessor As Long
Private Type MEMORYSTATUS
dwLength As Long
dwMemoryLoad As Long
dwTotalPhys As Long
dwAvailPhys As Long
dwTotalPageFile As Long
dwAvailPageFile As Long
dwTotalVirtual As Long
dwAvailVirtual As Long
End Type
Private Declare Function GetVersionEx Lib "Kernel32" _
Alias "GetVersionExA" (lpOSInfo As OSVERSIONINFO) As Boolean
Private Type OSVERSIONINFO
dwOSVersionInfoSize As Long
dwMajorVersion As Long
dwMinorVersion As Long
dwBuildNumber As Long
dwPlatformId As Long
strReserved As String * 128
End Type
Private Declare Sub GetSystemInfo Lib "Kernel32" _
(lpSystemInfo As SYSTEM_INFO)
Private Type SYSTEM_INFO
dwOemID As Long
dwPageSize As Long
lpMinimumApplicationAddress As Long
lpMaximumApplicationAddress As Long
dwActiveProcessorMask As Long
dwNumberOrfProcessors As Long
dwProcessorType As Long
dwAllocationGranularity As Long
dwReserved As Long
End Type
The SystemInformation class contains six read-only properties :
TotalMemory, AvailableMemory, OSVersion, OSBuild, OSPlatform, and Processor. These properties are set within the class and cannot be modified from outside the class. The Property Get functions for the six properties are shown in Listing 13.21.
Listing 13.21 The Property Get Routines Required by the SystemInformation Class
Public Property Get TotalMemory() As Long
TotalMemory = mlngTotalMemory
End Property
Public Property Get AvailableMemory() As Long
AvailableMemory = mlngAvailableMemory
End Property
Public Property Get OSVersion() As String
OSVersion = mstrOSVersion
End Property
Public Property Get OSBuild() As Single
OSBuild = msngOSBuild
End Property
Public Property Get OSPlatform() As String
OSPlatform = mstrOSPlatform
End Property
Public Property Get Processor() As Long
Processor = mlngProcessor
End Property
All the work is done in the Initialize event of the class. When the class is instantiated, the Initialize event executes all the Windows API functions necessary to obtain the required system information. The Initialize event of the class is shown in Listing 13.22.
Listing 13.22 The Initialize Event of the SystemInformation Class
Private Sub Class_Initialize()
'Get Free Memory
Dim MS As MEMORYSTATUS
MS.dwLength = Len(MS)
GlobalMemoryStatus MS
mlngTotalMemory = Format(MS.dwTotalPhys, "Standard")
mlngAvailableMemory = Format(MS.dwAvailPhys, "Standard")
'Get Version Information
Dim OSInfo As OSVERSIONINFO
OSInfo.dwOSVersionInfoSize = Len(OSInfo)
If GetVersionEx(OSInfo) Then
mstrOSVersion = OSInfo.dwMajorVersion & "." & _
OSInfo.dwMinorVersion
msngOSBuild = OSInfo.dwBuildNumber And &HFFFF&
If OSInfo.dwPlatformId = 0 Then
mstrOSPlatform = "Windows 95"
Else
mstrOSPlatform = "Windows NT"
End If
End If
'Get System Information
Dim SI As SYSTEM_INFO
GetSystemInfo SI
mlngProcessor = SI.dwProcessorType
End Sub
The GlobalMemoryStatus Windows API function populates the TotalMemory and AvailableMemory properties. The GetVersionEX function is used to set the OSVersion, OSBuild, and OSPlatform properties. Finally, the GetSystemInfo function populates the Processor property.