tag:blogger.com,1999:blog-74574179422818748102024-03-19T11:33:53.360+05:30LEARN MS-ACCESS TIPS AND TRICKSLearn Microsoft Access Advanced Programming Techniques, Tips and Tricks.a.p.r. pillaihttp://www.blogger.com/profile/02627590161919120214noreply@blogger.comBlogger407125tag:blogger.com,1999:blog-7457417942281874810.post-27435246329420074362024-02-29T13:43:00.000+05:302024-02-29T13:43:17.746+05:30Streamlining Code Database Compacting Utility<h3 style="text-align: left;"> Streamlining Form Module Code in Standalone Class Module.</h3><h3 style="text-align: left;">The Database Compact/Repair Utility.</h3><p>The '<b>Compact on Close</b>' Option in Microsoft Access. When enabled under File --> Current Database --> Application Options, this feature automatically compacts the database every time you open and close it. Additionally, you can manually select the Compact/Repair option from the File menu to Compact and Repair the active database. If you need to compact an external file, you can choose the Compact and Repair option from the Database Tools Menu.</p><div class="separator" style="clear: both;"><p>If you prefer to refrain from performing the Compact and Repair process daily, you can run the Compact/Repair Utility for multiple Databases together periodically, such as weekly or fortnightly. In this case, you can conveniently select those Databases from the <a href="https://www.msaccesstips.com/2022/05/get-disk-free-space-windows-api.html" target="_blank">Disk</a> and add them to a ListBox, as illustrated in the form image below.</p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEivnGfeCLtSqcXe0WElcadGd-8uAbEEMql9Mkl9DLBv54KNreDgU1XXJpVLaAiXCNse8amsOqfgQydJDNh8POIW7YtjCi_0N21QsN8YShXgrbNr6mSOl4ijRZQJCX4zvrfu7_J-MS7QKx3wi8X8sbL-qk1pO1QmNP1MxCeEe3s6X5TlEqDQpFSkFTGBHvvg/s762/CompactRepair.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="762" data-original-width="705" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEivnGfeCLtSqcXe0WElcadGd-8uAbEEMql9Mkl9DLBv54KNreDgU1XXJpVLaAiXCNse8amsOqfgQydJDNh8POIW7YtjCi_0N21QsN8YShXgrbNr6mSOl4ijRZQJCX4zvrfu7_J-MS7QKx3wi8X8sbL-qk1pO1QmNP1MxCeEe3s6X5TlEqDQpFSkFTGBHvvg/s320/CompactRepair.png" /></a></div>
<p>The ListBox is enabled with the Multi-select option and you may select the required databases from the list and run from the Compact/Repair Command Button. The Selected Databases will be Compacted individually and their File Size will be updated in the second column of the ListBox in Kilobytes.</p><h3 style="text-align: left;">The FileBrowser Control.</h3><p>The <b>'Add Databases'</b> <a href="https://www.msaccesstips.com/2019/07/withevents-textbox-commandbutton.html" target="_blank">Command Button</a> opens the File Browser Dialog Control and you can select one or more Databases from your disk and add them to the ListBox.</p>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEimR5olnFz6bU7DzIMl2v-zKgdVGRq_8DShVF8vHsR0D1Oz7WIDamYpjFdmnejl7zdZa6PPd0iHpFwK5Y0ZzBbCtn90mvfa1zjoK8yllq9TwDIkK-B0FD6-JtR8_RRHMEYIGkRsHG-nwgvdF_F0Qefo9BgQ_pbc3FtpGPlK_E29LVZuKMb6EjEkmS1zs71h/s943/CompactFileBrowser.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="698" data-original-width="943" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEimR5olnFz6bU7DzIMl2v-zKgdVGRq_8DShVF8vHsR0D1Oz7WIDamYpjFdmnejl7zdZa6PPd0iHpFwK5Y0ZzBbCtn90mvfa1zjoK8yllq9TwDIkK-B0FD6-JtR8_RRHMEYIGkRsHG-nwgvdF_F0Qefo9BgQ_pbc3FtpGPlK_E29LVZuKMb6EjEkmS1zs71h/s320/CompactFileBrowser.png" width="320" /></a></div>
<h3 style="text-align: left;">The Compact/Repair Function Running.</h3><p>The selected files from the Disk are added to the <b>DirectoryList</b> Table, the Source Table of the ListBox. As per your Compact/Repair schedule, you may open this Compact/Repair Utility, select the required files from the <a href="https://www.msaccesstips.com/2019/04/withevents-button-combo-list-textbox-tab.html " target="_blank">ListBox</a> then Click on the <b>'Compact/Repair'</b> Command Button.</p>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhVBXDoXwEETxvRCBlO9-lW-qlKPcaJrPX1knWd2kfAg2pJ8tRTPhveVP9FC22LPayiNqzbrhJb8zaDkWcO6rIxt9wLv7jhPS1CcsppTKH6Qfc1ab2TIxyCh79inbeo2Wjr1qbJWAwEOlk3a7MbT6q8chTQ5Cz0y6nHenDXGRIWmANgsIAb7ryeQA0_hyphenhyphenjT/s765/CompactList.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="765" data-original-width="702" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhVBXDoXwEETxvRCBlO9-lW-qlKPcaJrPX1knWd2kfAg2pJ8tRTPhveVP9FC22LPayiNqzbrhJb8zaDkWcO6rIxt9wLv7jhPS1CcsppTKH6Qfc1ab2TIxyCh79inbeo2Wjr1qbJWAwEOlk3a7MbT6q8chTQ5Cz0y6nHenDXGRIWmANgsIAb7ryeQA0_hyphenhyphenjT/s320/CompactList.png" /></a></div>
<h3 style="text-align: left;">Preparing for Compact/Repair.</h3><p>In the Compact/Repair Utility Program, the Database is first backed up to a temporary location. The default Backup Path shown in the TextBox above the <a href="https://www.msaccesstips.com/2006/09/command-button-animation.html " target="_blank">Command Buttons</a> is <b>D:\Tmp\</b>. It is defined in the Default Value Property of the <a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-seven.html" target="_blank">TextBox</a> and used by the Compact/Repair Utility Program. </p><p>If you would like to take any <a href="https://www.msaccesstips.com/2008/05/database-daily-backup.html" target="_blank">Database Backup</a> to a different location then change the path, like <b>C:\Backup\</b> in the TextBox before running the Compact/Repair option. </p><p>If you prefer a different location permanently then open the Form in Design View, display the Property Sheet of the TextBox, and change the Default Value Property to your preferred location like <b>D:\Backup\</b> and see that the last character in the path is a backslash. </p><p><b>Note:</b> When the same database is compacted again the old backup file will be replaced with the new one. Till that time the Backup File will remain safe in that location.</p><p>The Compacting Procedure goes through the following steps:</p><ol style="text-align: left;"><li>The Source File is Copied to the backup location, to keep a copy of the Database safe, before the Source file is Compacted.</li><li><p>The <b>DBEngine.CompactDatabase()</b> command is executed to perform Compact and Repair operations on the database and repairs the data if Database corruption is detected. In the event of data corruption, there is a potential risk of data loss, and the specific information regarding the errors encountered is preserved in the System Table <b>MSysCompactErrors</b>. To mitigate such situations, it is advisable to restore the data from previously created database backups, if available.</p></li><li>All objects from the source database, including both user-created and system objects, are transferred into a new database. This new temporary database name is "<b>db1.accdb</b>" (or "db1.mdb" depending on the file format) and is located in the designated backup path: <b>D:\tmp\db1.accdb</b>. The replication process excludes the system's temporary work files, ensuring a comprehensive transfer of objects while omitting non-essential temporary data.</li><li><p>Deletes the Source File from its home location.</p></li><li>The Compacted <b>D:\tmp\db1.accdb</b> file is transferred to the home location with its original Database name.</li></ol><p><b>Note:</b> The Access System goes through the same procedure, when you run the Compacting operation directly from the Access System, except the Database Backup procedure.</p><h3 style="text-align: left;">Streamlined VBA Coding in Standalone Class Module.</h3><p>Having gained insights into the utility highlighted in the previous introduction, it is now opportune to explore the <a href="https://www.msaccesstips.com/2023/06/streamlining-form-module-code-part-four.html" target="_blank">streamlined event subroutine</a> coding procedure implemented in this specific project. The necessary VBA codes for Event Subroutines are meticulously crafted within a standalone class module, enhancing code maintenance and debugging processes. This organized code structure within the standalone Class Module facilitates easy transportation to other projects, safeguarding valuable work from being entangled with less critical code in form modules. By adopting this streamlined coding approach, the practice of reusing identical code segments across controls of the same type within the form is encouraged, eliminating the need for duplicative coding efforts.</p><h3 style="text-align: left;">The Command Button Wrapper Class.</h3><div><div>There exists a singular Wrapper Class designated for the Command Button Controls, along with an additional Class Module dedicated to the Intermediary or Interface Class. The Interface Class is responsible for generating instances of the Command Button Wrapper Class and facilitating the Command Button Click Events. Customarily, a Collection Object is employed to manage all Command Button Wrapper Class instances. This arrangement allows for the monitoring and capturing of Command Button Click Events triggered within the Compact_Repair Form.</div></div><h4 style="text-align: left;">The Command Button Wrapper Class VBA Code.</h4>
<pre>Option Compare Database
Option Explicit
Private cmdfrm As Form
Private WithEvents cmd As CommandButton
Private strPath As String
Private bkupPath As String
Dim lst As ListBox
Dim lstcount As Integer
Dim xtn As String
'------------------------------------------------------
'Streamlining Form Module Code
'in Stand-alone Class Modules
'------------------------------------------------------
'Database Compact/Repair Utility
'Author: a.p.r. pillai
'Date : 20/02/2024
'Rights: All Rights(c) Reserved by www.msaccesstips.com
'------------------------------------------------------
'Form's Property GET/SET Procedures
Public Property Get cmd_Frm() As Form
Set cmd_Frm = cmdfrm
End Property
Public Property Set cmd_Frm(ByRef cfrm As Form)
Set cmdfrm = cfrm
End Property
'Command Button Property GET/SET Procedures
Public Property Get c_cmd() As CommandButton
Set c_cmd = cmd
End Property
Public Property Set c_cmd(ByRef pcmd As CommandButton)
Set cmd = pcmd
Call DefaultPath
End Property
'The Click Event Subroutines
Private Sub cmd_Click()
On Error GoTo cmd_Click_Err
Select Case cmd.Name
Case "cmdQuit"
If MsgBox("Close Compact_Repair Form?", vbOKCancel + vbQuestion, "cmd_Click") = vbOK Then
DoCmd.Close acForm, cmdfrm.Name
Exit Sub
End If
Case "cmdFileDialog"
Call FileDialog 'Display selected Path & files
cmdfrm.dbList.Requery
Case "cmdCompact"
Call DBPrepare
Case "cmdDelete"
Call DBDelete
End Select
cmd_Click_Exit:
Exit Sub
cmd_Click_Err:
MsgBox Err & " : " & Err.Description, , "cmd_Click()"
Resume cmd_Click_Exit
End Sub
Private Sub DBDelete()
'Delete the selected Items from the DirectoryList Table
Dim delCount As Integer
Dim j As Integer
Dim k As Integer
Dim DB As Database
Dim dbName As String
Dim msg As String
Dim Rst As Recordset
Dim opt As Integer
On Error GoTo DBDelete_Err
opt = 0
msg = "1. Delete Selected." & vbCr & vbCr _
& "2. Delete All from List." & vbCr & vbCr _
& "3. Cancel Deletion."
While opt < 1 Or opt > 3
opt = InputBox(msg, "Select Option.", 3)
Wend
Select Case opt
Case 1
GoTo Selected
Case 2
msg = "Empty the Database List...?"
If MsgBox(msg, vbYesNo + vbCritical, "DeleteList()") = vbNo Then
Exit Sub
Else
DoCmd.SetWarnings False
DoCmd.OpenQuery "DeleteAll_ListQ"
DoCmd.SetWarnings True
cmdfrm.dbList.Requery
cmdfrm.cmdDelete.eabled = False
Exit Sub
End If
Case 3
Exit Sub
End Select
Selected:
delCount = CheckList()
If delCount > 0 Then
msg = "Delete " & delCount & " Items." & vbCr & vbCr & "Proceed...?"
If MsgBox(msg, vbYesNo, "DBDelete()") = vbNo Then
Exit Sub
End If
Set DB = CurrentDb
Set Rst = DB.OpenRecordset("DirectoryList", dbOpenDynaset)
Set lst = cmdfrm.dbList
For j = 0 To lstcount
If lst.Selected(j) Then
dbName = lst.Column(0, j)
Rst.FindFirst "Path = '" & dbName & "'"
If Not Rst.NoMatch Then
Rst.Delete
Rst.Requery
End If
End If
Next
Rst.Close
Set Rst = Nothing
Set DB = Nothing
lst.Requery
MsgBox delCount & " Item(s) Deleted From List.", , "DBDelete()"
Else
MsgBox delCount & " Item(s) Selected for Deletion!", , "DBDelete()"
End If
DBDelete_Exit:
Exit Sub
DBDelete_Err:
MsgBox Err & " : " & Err.Description, , "DBDelete()"
Resume DBDelete_Exit
End Sub
Private Sub DBPrepare()
'Preparatory Procedure for Compacting
'the selected Databases individually
Dim xselcount As Integer
Dim dbName As String
Dim ldbName As String
Dim strTmp As String
Dim i As Integer
Dim j As Integer
Dim timr As Double
Dim fs, f
Dim lockfile As String
Dim msg As String
bkupPath = cmdfrm!BackupPath
'create a Backup Folder
On Error Resume Next
Set fs = CreateObject("Scripting.FileSystemObject")
Set f = fs.GetFolder(bkupPath)
If Err = 76 Or Err > 0 Then
Err.Clear
fs.createfolder (bkupPath)
End If
On Error GoTo DBPrepare_Err
'Remove existing workfiles from backup location
xselcount = CheckList()
If xselcount = 0 Then
msg = "Select Database(s) from List for Compacting!"
MsgBox msg, , "DBPrepare()"
Exit Sub
End If
'Ensure selected database is not active
msg = "Ensure that Selected Databases are not in Use. " _
& vbCrLf & "Proceed...?"
If MsgBox(msg, vbYesNo + vbDefaultButton2 + vbQuestion, _
"DBPrepare()") = vbNo Then
Exit Sub
End If
'Check the selected database is active or not
'if inactive then submit it to DBCompact() Program.
For j = 0 To lstcount
If lst.Selected(j) Then
dbName = Trim(lst.Column(0, j))
i = InStrRev(dbName, ".")
xtn = Mid(dbName, i) 'extract extension
lockfile = IIf(xtn = ".mdb", "ldb", "laccdb")
ldbName = Left(dbName, i)
ldbName = ldbName & lockfile 'for checking the presense of lock file.
If Len(Dir(ldbName)) > 0 Then 'database is active
MsgBox "Database: " & dbName & vbCrLf & "is active. Skipping to the Next in list."
GoTo nextstep
End If
'Prepare for Compacting and to display the status messages.
msg = "Compact/Repair: " & dbName & vbCrLf & "Proceed...?"
If MsgBox(msg, vbQuestion + vbDefaultButton2 + vbYesNo, "DBPrepare()") = vbYes Then
cmdfrm.lblNote.Visible = False
cmdfrm.lblStat.Caption = "Working, Please wait..."
DoEvents
Call DBCompact(dbName) 'Run Compacting
cmdfrm.lblStat.Caption = ""
DoEvents
nextstep:
Sleep 5
End If
End If
Next
msg = "Selected Database(s) Compacted Successfully."
MsgBox msg, , "DBPrepare()"
Sleep 3
cmdfrm.lblNote.Visible = True
cmdfrm.lblStat.Caption = ""
strTmp = bkupPath & "db1" & xtn 'Delete the temporary file
Call KillTempFile(strTmp)
Set fs = Nothing
Set f = Nothing
Set lst = Nothing
DBPrepare_Exit:
Exit Sub
DBPrepare_Err:
MsgBox Err.Description, , "DBPrepare()"
Resume DBPrepare_Exit
End Sub
Private Sub DBCompact(ByVal strdb As String)
'Compact/Repair Database received as Parameter
Dim t As Long
Dim xdir As String
Dim strbk As String
Dim strTmp As String
Dim tmp As String
Dim chkFile As String
Dim msg As String
On Error GoTo dbCompact_Err
tmp = cmdfrm!BackupPath
strTmp = tmp & "db1" & xtn
chkFile = strTmp
Call KillTempFile(chkFile)
t = InStrRev(strdb, "\")
If t > 0 Then
strbk = Mid(strdb, t + 1)
End If
strbk = tmp & strbk
chkFile = strbk
Call KillTempFile(chkFile)
'Make a Copy in d:\tmp folder for safe keep
msg = "Taking Backup of " & strdb & vbCrLf _
& "to " & tmp
cmdfrm.lblMsg.Caption = msg
DoEvents
'Take a Backup of Original File to the Backup Location
FileCopy strdb, strbk
msg = "Transferring Objects from " & strdb & vbCrLf _
& "to " & tmp & "db1" & xtn
cmdfrm.lblMsg.Caption = msg
DoEvents
'https://learn.microsoft.com/en-us/office/client-developer/access/desktop-database-reference/dbengine-compactdatabase-method-dao
'Compact Database to D:\tmp\db1.accdb
DBEngine.CompactDatabase strdb, strTmp
' Delete uncompacted Database and Copy Compacted db1.mdb with
' the Original Name
msg = "Creating " & strdb & " from " & tmp & "db1" & xtn
cmdfrm.lblMsg.Caption = msg
DoEvents
'Delete uncompacted file
chkFile = strdb
Call KillTempFile(chkFile)
'Create Compacted File with its original name in its home location
DBEngine.CompactDatabase strTmp, strdb
msg = strdb & " Compacted/Repaired Successfully."
cmdfrm.lblMsg.Caption = msg
DoEvents
Call dbListUpdate(strdb) 'Update the DirectoryList Table
dbCompact_Exit:
Exit Sub
dbCompact_Err:
MsgBox Err & " : " & Err.Description, , "dbCompact()"
Resume dbCompact_Exit
End Sub
Private Function CheckList() As Integer
'Take selected items Count
Dim k As Integer
Dim xcount As Integer
On Error GoTo CheckList_Err
Set lst = cmdfrm.dbList
lstcount = DCount("*", "DirectoryList")
xcount = 0
For k = 0 To lstcount
If lst.Selected(k) Then
xcount = xcount + 1
End If
Next
If xcount = 0 Then
MsgBox "No Database(s)Selected."
Exit Function
End If
CheckList = xcount
CheckList_Exit:
Exit Function
CheckList_Err:
MsgBox Err & ": " & Err.Description, , "CheckList()"
Resume CheckList_Exit
End Function
Private Sub dbListUpdate(ByVal cmpPath As String)
'Update the File Size of the Database after Compacting
On Error GoTo dbListUpdate_Err
Dim sPath As String
Dim i As Variant
Dim DB As Database
Dim Rst As Recordset
Set DB = CurrentDb
Set Rst = DB.OpenRecordset("DirectoryList", dbOpenDynaset)
Rst.MoveFirst
Rst.FindFirst "Path = '" & cmpPath & "'"
If Not Rst.NoMatch Then
sPath = Rst!Path
Rst.Edit
Rst!FileLengthKB = FileLen(sPath) / 1024 'Db size after compacting
Rst.Update
End If
Rst.Close
cmdfrm.dbList.Requery
dbListUpdate_Exit:
Set Rst = Nothing
Set DB = Nothing
Exit Sub
dbListUpdate_Err:
MsgBox Err & ": " & Err.Description, , "dbListUpdate()"
Resume dbListUpdate_Exit
End Sub
Private Sub DefaultPath()
Dim strLoc As String
'Default path for CommonDialog Control
strLoc = CurrentProject.Path & "\*.accdb"
strPath = strLoc 'Assign to Global Variable strPath
End Sub
Private Sub FileDialog()
On Error GoTo cmdFileDialog_Err
'Requires reference to Microsoft Office 12.0 Object Library.
Dim fDialog As Office.FileDialog
Dim DB As DAO.Database
Dim Rst As DAO.Recordset
Dim defPath As String
Dim varFile As Variant
Dim strfiles As String
'Set up the File Dialog.
Set fDialog = Application.FileDialog(msoFileDialogFilePicker)
With fDialog
'Allow user to make multiple selections of disk files.
.AllowMultiSelect = True
.InitialFileName = Dir(strPath)
.InitialView = msoFileDialogViewDetails
'Set the title of the dialog box.
.Title = "Please select one or more files"
'Clear out the current filters, and add our own.
.Filters.Clear
.Filters.Add "Access Databases", "*.mdb; *.accdb"
.Filters.Add "Access Projects", "*.adp"
.Filters.Add "All Files", "*.*"
.FilterIndex = 1
'.Execute
'Show the dialog box. If the .Show method returns True, the
'user picked at least one file. If the .Show method returns
'False, the user clicked Cancel.
If .Show = True Then
Set DB = CurrentDb
Set Rst = DB.OpenRecordset("DirectoryList", dbOpenDynaset)
'Add all selected files to the DirectoryList Table
defPath = ""
For Each varFile In .SelectedItems
If defPath = "" Then
defPath = Left(varFile, InStrRev(varFile, "\"))
defPath = defPath & "*.*"
strPath = defPath
End If
Rst.AddNew
Rst![Path] = varFile
Rst![FileLengthKB] = FileLen(varFile) / 1024
Rst.Update
Next
cmdfrm.cmdDelete.Enabled = True
Else
MsgBox "You clicked Cancel in the file dialog box."
End If
End With
cmdFileDialog_Exit:
Exit Sub
cmdFileDialog_Err:
MsgBox Err & " : " & Err.Description, , "cmdFileDialog_Click()"
Resume cmdFileDialog_Exit
End Sub
Private Sub KillTempFile(ByVal filename As String)
On Error GoTo KillTempFile_Err
'Manage Temporary Files
If Len(Dir(filename)) > 0 Then
Kill filename
End If
KillTempFile_Exit:
Exit Sub
KillTempFile_Err:
MsgBox Err & ": " & Err.Description, , "KillTempFile()"
Resume KillTempFile_Exit
End Sub
</pre>
<p>The Command Button Wrapper Class starts with the usual Properties the Form Object and Command Button Control declarations. The CommandButton Control is declared and qualified with the Keyword WithEvents for capturing the Click Events when Fired from the Form.</p><p>A few local Variables are also declared in the global area of the Class Module followed by the Form and Command Button <b>Get/Set</b> Property Procedures. </p><p>Despite four Command Buttons on the Form, a single Click Event Subroutine within the Command Button Wrapper Class suffices. This streamlined approach enables the capture of all four Command Button Clicks within the same Event Subroutine, allowing for the execution of their respective Event Procedures. This efficiency is achievable through the implementation of streamlined Event Procedure coding.</p><p><span face="Söhne, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Ubuntu, Cantarell, "Noto Sans", sans-serif, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"" style="background-color: white; color: #0d0d0d; font-size: 16px; white-space-collapse: preserve;">When examining the Event Subroutine Code in order of priority, the initial step involves adding the databases slated for the Compact/Repair procedure to the ListBox. This is accomplished through the Click Event of the Command Button labeled '<b>Add Databases</b>,' with the name '<b>CmdFileDialog</b>.' The Click Event, in turn, invokes the FileDialog() Subroutine.</span> The Code Segment is given below:</p>
<pre>Private Sub FileDialog()
On Error GoTo cmdFileDialog_Err
'Requires reference to Microsoft Office 12.0 Object Library.
Dim fDialog As Office.FileDialog
Dim db As DAO.Database
Dim rst As DAO.Recordset
Dim defPath As String
Dim varFile As Variant
Dim strfiles As String
'Set up the File Dialog.
Set fDialog = Application.FileDialog(msoFileDialogFilePicker)
With fDialog
'Allow user to make multiple selections of disk files.
.AllowMultiSelect = True
.InitialFileName = Dir(strPath)
.InitialView = msoFileDialogViewDetails
'Set the title of the dialog box.
.Title = "Please select one or more files"
'Clear out the current filters, and add our own.
.Filters.Clear
.Filters.Add "Access Databases", "*.mdb; *.accdb"
.Filters.Add "Access Projects", "*.adp"
.Filters.Add "All Files", "*.*"
.FilterIndex = 1
'.Execute
'Show the dialog box. If the .Show method returns True, the
'user picked at least one file. If the .Show method returns
'False, the user clicked Cancel.
If .Show = True Then
Set db = CurrentDb
Set rst = db.OpenRecordset("DirectoryList", dbOpenDynaset)
'Add all selected files to the DirectoryList Table
defPath = ""
For Each varFile In .SelectedItems
If defPath = "" Then
defPath = Left(varFile, InStrRev(varFile, "\"))
defPath = defPath & "*.*"
strPath = defPath
End If
rst.AddNew
rst![Path] = varFile
rst![FileLengthKB] = FileLen(varFile) / 1024
rst.Update
Next
cmdfrm.cmdDelete.Enabled = True
Else
MsgBox "You clicked Cancel in the file dialog box."
End If
End With
cmdFileDialog_Exit:
Exit Sub
cmdFileDialog_Err:
MsgBox Err & " : " & Err.Description, , "cmdFileDialog_Click()"
Resume cmdFileDialog_Exit
End Sub
</pre>
<p>This is the same <b>Office.FileDialog</b> Control (the File Browser Control) and Program we used in the earlier Episode with the Title <a href="https://www.msaccesstips.com/2023/10/streamlining-form-vba-external-files.html#FileBrowser" target="_blank">External Files' List in Hyperlinks</a> published earlier. If you click on this link you will be directed to the specific part of the Page that gives its function details.</p><p>In this scenario, we utilize the DirectoryList Table to store the databases selected from the disk, which subsequently populate the ListBox on the Form. The table encompasses two fields: <b>Path</b> and <b>FileLengthKB</b>. The former accommodates the full pathname of the database, while the latter calculates the file size in kilobytes when added to the table. Following the Compact/Repair operations, this table undergoes updates to reflect the altered file sizes.</p><p>Users have the flexibility to select one or more databases from the list and initiate the Compact/Repair process by clicking on the designated Command Button. This action triggers the execution of the <b>DBPrepare()</b> Subroutine, which in turn identifies the selected database(s) in the ListBox and passes them to the <b>DBCompact()</b> Subroutine for the Compact/Repair operation.</p><h3 style="text-align: left;">The DBPrepare() Subroutine VBA Code.</h3>
<pre>Private Sub DBPrepare()
'Preparatory Procedure for Compacting
'the selected Databases individually
Dim xselcount As Integer
Dim dbName As String
Dim ldbName As String
Dim strTmp As String
Dim i As Integer
Dim j As Integer
Dim timr As Double
Dim fs, f
Dim lockfile As String
Dim msg As String
bkupPath = cmdfrm!BackupPath
'create a Backup Folder
On Error Resume Next
Set fs = CreateObject("Scripting.FileSystemObject")
Set f = fs.GetFolder(bkupPath)
If Err = 76 Or Err > 0 Then
Err.Clear
fs.createfolder (bkupPath)
End If
On Error GoTo DBPrepare_Err
'Remove existing workfiles from backup location
xselcount = CheckList()
If xselcount = 0 Then
msg = "Select Database(s) from List for Compacting!"
MsgBox msg, , "DBPrepare()"
Exit Sub
End If
'Ensure selected database is not active
msg = "Ensure that Selected Databases are not in Use. " _
& vbCrLf & "Proceed...?"
If MsgBox(msg, vbYesNo + vbDefaultButton2 + vbQuestion, _
"DBPrepare()") = vbNo Then
Exit Sub
End If
'Check the selected database is active or not
'if inactive then submit it to DBCompact() Program.
For j = 0 To lstcount
If lst.Selected(j) Then
dbName = Trim(lst.Column(0, j))
i = InStrRev(dbName, ".")
xtn = Mid(dbName, i) 'extract extension
lockfile = IIf(xtn = ".mdb", "ldb", "laccdb")
ldbName = Left(dbName, i)
ldbName = ldbName & lockfile 'for checking the presense of lock file.
If Len(Dir(ldbName)) > 0 Then 'database is active
MsgBox "Database: " & dbName & vbCrLf & "is active. Skipping to the Next in list."
GoTo nextstep
End If
'Prepare for Compacting and to display the status messages.
msg = "Compact/Repair: " & dbName & vbCrLf & "Proceed...?"
If MsgBox(msg, vbQuestion + vbDefaultButton2 + vbYesNo, "DBPrepare()") = vbYes Then
cmdfrm.lblNote.Visible = False
cmdfrm.lblStat.Caption = "Working, Please wait..."
DoEvents
Call DBCompact(dbName) 'Run Compacting
cmdfrm.lblStat.Caption = ""
DoEvents
nextstep:
Sleep 5
End If
End If
Next
msg = "Selected Database(s) Compacted Successfully."
MsgBox msg, , "DBPrepare()"
Sleep 3
cmdfrm.lblNote.Visible = True
cmdfrm.lblStat.Caption = ""
strTmp = bkupPath & "db1" & xtn 'Delete the temporary file
Call KillTempFile(strTmp)
Set fs = Nothing
Set f = Nothing
Set lst = Nothing
DBPrepare_Exit:
Exit Sub
DBPrepare_Err:
MsgBox Err.Description, , "DBPrepare()"
Resume DBPrepare_Exit
End Sub
</pre>
<p>The above DBPrepare() Subroutine picks the User selected items individually and passes them to the actual Compacting Subroutine DBCompact() below for Compact/Repair operations and restoring the Compacted Database to its home location.</p>
<h4>The DBCompact Subroutine VBA Code.</h4>
<p>Private Sub DBCompact(ByVal strdb As String)</p><pre>'Compact/Repair Database received as Parameter
Dim t As Long
Dim xdir As String
Dim strbk As String
Dim strTmp As String
Dim tmp As String
Dim chkFile As String
Dim msg As String
On Error GoTo dbCompact_Err
tmp = cmdfrm!BackupPath
strTmp = tmp & "db1" & xtn
chkFile = strTmp
Call KillTempFile(chkFile)
t = InStrRev(strdb, "\")
If t > 0 Then
strbk = Mid(strdb, t + 1)
End If
strbk = tmp & strbk
chkFile = strbk
Call KillTempFile(chkFile)
'Make a Copy in d:\tmp folder for safe keep
msg = "Taking Backup of " & strdb & vbCrLf _
& "to " & tmp
cmdfrm.lblMsg.Caption = msg
DoEvents
'Take a Backup of Original File to the Backup Location
FileCopy strdb, strbk
msg = "Transferring Objects from " & strdb & vbCrLf _
& "to " & tmp & "db1" & xtn
cmdfrm.lblMsg.Caption = msg
DoEvents
'https://learn.microsoft.com/en-us/office/client-developer/access/desktop-database-reference/dbengine-compactdatabase-method-dao
'Compact Database to D:\tmp\db1.accdb
DBEngine.CompactDatabase strdb, strTmp
' Delete uncompacted Database and Copy Compacted db1.mdb with
' the Original Name
msg = "Creating " & strdb & " from " & tmp & "db1" & xtn
cmdfrm.lblMsg.Caption = msg
DoEvents
'Delete uncompacted file
chkFile = strdb
Call KillTempFile(chkFile)
'Create Compacted File with its original name in its home location
DBEngine.CompactDatabase strTmp, strdb
msg = strdb & " Compacted/Repaired Successfully."
cmdfrm.lblMsg.Caption = msg
DoEvents
Call dbListUpdate(strdb) 'Update the DirectoryList Table
dbCompact_Exit:
Exit Sub
dbCompact_Err:
MsgBox Err & " : " & Err.Description, , "dbCompact()"
Resume dbCompact_Exit
End Sub
</pre>
<p>There are three other small supporting Subroutines called from both the DBPrepare() and DBCompact() Subroutines.</p><p>The CheckList() Subroutine.</p>
<pre>Private Function CheckList() As Integer
'Take selected items Count
Dim k As Integer
Dim xcount As Integer
On Error GoTo CheckList_Err
Set lst = cmdfrm.dbList
lstcount = DCount("*", "DirectoryList")
xcount = 0
For k = 0 To lstcount
If lst.Selected(k) Then
xcount = xcount + 1
End If
Next
If xcount = 0 Then
MsgBox "No Database(s)Selected."
Exit Function
End If
CheckList = xcount
CheckList_Exit:
Exit Function
CheckList_Err:
MsgBox Err & ": " & Err.Description, , "CheckList()"
Resume CheckList_Exit
End Function
</pre>
<p>The above Subroutine checks whether any Item is selected in the ListBox and takes its count when the 'Compact/Repair' or 'Delete from List' Command Button is Clicked. If found selected then the selected operation is performed.</p>
<h3>The dbListUpdate() Subroutine VBA.</h3><pre>Private Sub dbListUpdate(ByVal cmpPath As String)
'Update the File Size of the Database after Compacting
On Error GoTo dbListUpdate_Err
Dim sPath As String
Dim i As Variant
Dim DB As Database
Dim Rst As Recordset
Set DB = CurrentDb
Set Rst = DB.OpenRecordset("DirectoryList", dbOpenDynaset)
Rst.MoveFirst
Rst.FindFirst "Path = '" & cmpPath & "'"
If Not Rst.NoMatch Then
sPath = Rst!Path
Rst.Edit
Rst!FileLengthKB = FileLen(sPath) / 1024 'Db size after compacting
Rst.Update
End If
Rst.Close
cmdfrm.dbList.Requery
dbListUpdate_Exit:
Set Rst = Nothing
Set DB = Nothing
Exit Sub
dbListUpdate_Err:
MsgBox Err & ": " & Err.Description, , "dbListUpdate()"
Resume dbListUpdate_Exit
End Sub
</pre>
<p>This Program is Called from the DBCompact() Subroutine to update the File Size in Kilobytes in the ListBox after Compacting the Database.</p>
<h3>The DBDelete() Subroutine.</h3>
<pre>Private Sub DBDelete()
'Delete the selected Items from the DirectoryList Table
Dim delCount As Integer
Dim j As Integer
Dim k As Integer
Dim DB As Database
Dim dbName As String
Dim msg As String
Dim Rst As Recordset
Dim opt As Integer
On Error GoTo DBDelete_Err
opt = 0
msg = "1. Delete Selected." & vbCr & vbCr _
& "2. Delete All from List." & vbCr & vbCr _
& "3. Cancel Deletion."
While opt < 1 Or opt > 3
opt = InputBox(msg, "Select Option.", 3)
Wend
Select Case opt
Case 1
GoTo Selected
Case 2
msg = "Empty the Database List...?"
If MsgBox(msg, vbYesNo + vbCritical, "DeleteList()") = vbNo Then
Exit Sub
Else
DoCmd.SetWarnings False
DoCmd.OpenQuery "DeleteAll_ListQ"
DoCmd.SetWarnings True
cmdfrm.dbList.Requery
cmdfrm.cmdDelete.eabled = False
Exit Sub
End If
Case 3
Exit Sub
End Select
Selected:
delCount = CheckList()
If delCount > 0 Then
msg = "Delete " & delCount & " Items." & vbCr & vbCr & "Proceed...?"
If MsgBox(msg, vbYesNo, "DBDelete()") = vbNo Then
Exit Sub
End If
Set DB = CurrentDb
Set Rst = DB.OpenRecordset("DirectoryList", dbOpenDynaset)
Set lst = cmdfrm.dbList
For j = 0 To lstcount
If lst.Selected(j) Then
dbName = lst.Column(0, j)
Rst.FindFirst "Path = '" & dbName & "'"
If Not Rst.NoMatch Then
Rst.Delete
Rst.Requery
End If
End If
Next
Rst.Close
Set Rst = Nothing
Set DB = Nothing
lst.Requery
MsgBox delCount & " Item(s) Deleted From List.", , "DBDelete()"
Else
MsgBox delCount & " Item(s) Selected for Deletion!", , "DBDelete()"
End If
DBDelete_Exit:
Exit Sub
DBDelete_Err:
MsgBox Err & " : " & Err.Description, , "DBDelete()"
Resume DBDelete_Exit
End Sub
</pre>
<p>To remove some databases from the ListBox, you must select them from the ListBox and Click the 'Delete from List' Command Button. The DBDelete() Subroutine is called and the selected items will be deleted from the <a href="https://www.msaccesstips.com/2023/10/streamlining-form-vba-external-files.html" target="_blank">DirectoryList</a> Table and refreshes the ListBox to reflect the change.</p><h3 style="text-align: left;"> The KillTempFile() Subroutine.</h3>
<pre>Private Sub KillTempFile(ByVal filename As String)
On Error GoTo KillTempFile_Err
'Manage Temporary Files
If Len(Dir(filename)) > 0 Then
Kill filename
End If
KillTempFile_Exit:
Exit Sub
KillTempFile_Err:
MsgBox Err & ": " & Err.Description, , "KillTempFile()"
Resume KillTempFile_Exit
End Sub
</pre>
<p>The Compact/Repair Program creates Temporary Databases for System use and deletes them using the above Subroutine. This Subroutine is called from within the DBPrepare() and DBCompact() Subroutines.</p>
<h3>The FLst_ObjInit Interface Class Module VBA Code. </h3>
<pre>Option Compare Database
Option Explicit
Private cmd As FLst_CmdButton
Private frm As Access.Form
Private Coll As New Collection
'------------------------------------------------------
'Streamlining Form Module Code
'in Stand-alone Class Modules
'------------------------------------------------------
'Database Compact/Repair Utility
'Author: a.p.r. pillai
'Date : 20/02/2024
'Rights: All Rights(c) Reserved by www.msaccesstips.com
'------------------------------------------------------
Public Property Get Ini_Frm() As Access.Form
Set Ini_Frm = frm.m_cFrm
End Property
Public Property Set Ini_Frm(ByRef pFrm As Access.Form)
Set frm = pFrm
Call Class_Init
End Property
Private Sub Class_Init()
On Error GoTo Class_Init_Err
Dim ctl As Control
Dim listcount As Long
Const EP = "[Event Procedure]"
listcount = DCount("*", "DirectoryList")
'If ListBox is empty then disable
'cmdDelete Command Button
If listcount = 0 Then
frm.cmdDelete.Enabled = False
Else
frm.cmdDelete.Enabled = True
End If
For Each ctl In frm.Controls
Select Case TypeName(ctl)
Case "CommandButton"
Select Case ctl.Name
Case "cmdFileDialog", "cmdCompact", _
"cmdDelete", "cmdQuit"
Set cmd = New FLst_CmdButton
Set cmd.cmd_Frm = frm
Set cmd.c_cmd = ctl
cmd.c_cmd.OnClick = EP
Coll.Add cmd
Set cmd = Nothing
End Select
End Select
Next
Class_Init_Exit:
Exit Sub
Class_Init_Err:
MsgBox Err & " : " & Err.Description, , "Class_Init()"
Resume Class_Init_Exit
End Sub
Private Sub Class_Terminate()
Do While Coll.Count > 0
Coll.Remove 1
Loop
End Sub
<span style="font-family: Times New Roman;"><span style="white-space: normal;">
</span></span></pre>
<p>Within the global declaration area, the FLst_CmdButton Class, the Form Object <b>frm</b>, and the Collection Object <b>Coll</b> are declared. This is succeeded by the inclusion of <b>Get/Set</b> Property Procedures for the <b>frm</b> property. In adherence to common practice, the active Form Object is passed from the Form_Load() Event Procedure into the <b>pFrm</b> parameter, subsequently being assigned to the Form Object <b>frm</b>.</p><p>Upon obtaining the reference to the active Form object within the Interface Class, in the subsequent phase of the Set Property Procedure, we invoke the Class_Init() Subroutine. </p><p>Within the Class_Init() Subroutine, a constant named "EP" is created to represent the <b>[Event Procedure]</b> text. Following this, a check is implemented to determine the status of the DirectoryList Table, which serves as the source data for the ListBox. If the DirectoryList table is empty, the <b>[Delete from List]</b> Command Button on the Form is disabled.</p><p>Within the subsequent For...Next Loop, the program iterates through the Command Buttons on the Form. When a <a href="https://www.msaccesstips.com/2012/09/command-button-color-change-on-mouse.html" target="_blank">Command Button</a> is identified, an individual instance of the Command Button Wrapper Class is instantiated. This instance is then assigned with the respective Control Reference, and the necessary Events, specifically the Click Events in this case, are enabled. These instances are subsequently stored in the <a href="https://www.msaccesstips.com/2018/12/ms-access-and-collection-object-basics.html" target="_blank">Collection Object</a>, for retaining them in memory.</p><p>You may take note of the following Statements:</p>
<pre> Set cmd = New FLst_CmdButton
Set cmd.cmd_Frm = frm
Set cmd.c_cmd = ctl
cmd.c_cmd.OnClick = EP
Coll.Add cmd
Set cmd = Nothing<span style="font-family: Times New Roman;"><span style="white-space: normal;">
</span></span></pre>
<p>The initial statement initiates the creation of an instance of the FLst_CmdButton Class in memory. Its <b>cmd_frm</b> Property is then configured with the active form object <b>frm</b>, and the current Command Button control Reference in <b>ctl</b> is transmitted to the <b>c_cmd</b> Property. When these two properties are armed with the references of the Form and Command Button, the resulting instance of the Command Button Wrapper Class effectively mirrors the properties and characteristics of the corresponding Command Button on the Form.</p><p>The subsequent statement, <b>cmd.c_cmd.OnClick = EP</b> is functionally equivalent to specifying the text <b>[Event Procedure]</b> in the OnClick Event Property of the Command Button. Following the activation of the Event Procedure, the current instance of the Wrapper Class is added to the Collection Object in memory. This enables the capturing of the Event when triggered from the Command Button, subsequently executing the associated Event Procedure in the Wrapper Class Module.</p>
<p>You should not ignore the next statement <b>Set cmd = Nothing</b>.</p>
<p>At this point you may be in doubt when we execute the above statement it will erase the Wrapper Class Instance we created in memory.</p><ol><li>While the resemblance may be apparent, there is a crucial distinction. The inclusion of this instance of the Wrapper Class Object in the Collection Object ensures that the Collection Object remains active, retaining the Wrapper Class Instance in memory until the Form is closed and subsequently cleared.</li>
<li><p>The reason we need to execute <b>Set cmd = Nothing</b> is to avoid creating the next CmdButton Wrapper Class Instance for another Command Button on the Form without clearing the previous one from memory. Without this step, attempting to create the second instance of the Command Button Wrapper Class could result in overwriting the earlier instance in the same memory location. Thus, resetting <b>cmd</b> ensures that a new instance can be created without interference with the earlier Instance of the Wrapper Class. </p></li><li><p>If we don't execute the <b>Set cmd = Nothing</b> then only the last Command Button's Event will remain valid and others will keep overwriting the earlier Instances.</p></li><li>Following the reset of the "cmd" object, the process of creating another instance of the Command Button Wrapper Class involves searching for an available memory area to instantiate a new instance of the Command Button Class. This ensures the proper allocation of memory for the new instance, preventing any potential interference with existing instances.</li><li><p>So, please don't ignore this statement. Since it is a logical issue you may need help finding it so easily when debugging.</p></li></ol><h3 style="text-align: left;">The <i>Compact_Repair</i> Form Module VBA Code.</h3>
<pre>Option Compare Database
Option Explicit
Dim Obj As New FLst_ObjInit
Private Sub Form_Load()
DoCmd.Restore
Set Obj.Ini_Frm = Me
End Sub
Private Sub Form_Unload(Cancel As Integer)
Set Obj = Nothing
End Sub
<span style="font-family: Times New Roman;"><span style="white-space: normal;">
</span></span></pre>
<p>As customary, we have instantiated the Interface Class <b>FLst_ObjInit</b> in the global declaration section of the Form's Class Module. This declaration ensures that all three sets of <a href="https://www.msaccesstips.com/2018/10/ms-access-class-module-and-vba.html" target="_blank">Class Modules</a>—Wrapper Class, Interface Class, and Form Class Module—are loaded into memory and poised for operation.</p><p>In the <b>Form_Load()</b> event procedure, the <b>obj.Ini_Frm</b> property of the Interface Class is assigned the reference to the active Form object, denoted by <b>Me</b>. This initiates a series of actions, and within moments, the system is primed to handle programmed events and their respective functions.</p><p>I hope this utility program proves to be a valuable tool for optimizing your Access applications over an extended period, and the best part is, that it comes at no cost.</p><h3>The Download Link for <i>Compact/Repair Utility</i> is given below:</h3>
<!--Download Link /downloads/2024/02/CompactingRepairing.zip-->
<div class="separator" style="clear: both; text-align: center;">
<a href="https://drive.google.com/file/d/1kH5GedUrb1L2PtiWcGGFixCm6jPMjALp/view?usp=sharing">
<img border="0" data-original-height="29" data-original-width="22" height="29" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFE4uDLH9dk3S6pEcfTkT6GW6r11XqBI4HQnLnhQFZMKFeeQt5ABV_NqvLHM68J7RacISsvbUEl0PlAq6V7i0t0mQF3iesrBuqGuvrf5CJV72OzrGZ7rdMxkxzw8FYlz-qniNySoqtqUN4/s320/zip.gif" width="22" />CompactingRepairing.zip</a>
</div>
<!--Download Link /downloads/2024/02/CompactingRepairing.zip-->
<br />
<ol>
<li><a href="https://www.msaccesstips.com/2023/04/reusing-form-module-vba-code-for-new.html" target="_Blank">Re-using Form Module VBA Code for New Projects.</a></li>
<li><a href="https://www.msaccesstips.com/2023/04/streamlining-form-module-code-part-two.html" target="_Blank">Streamlining Form Module Code Part-Two</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/05/streamlining-form-module-code-part-three.html" target="_Blank">Streamlining Form Module Code Part-Three</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/06/streamlining-form-module-code-part-four.html" target="_Blank">Streamlining Form Module Code Part-Four</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/06/streamlining-form-module-code-part-five.html" target="_Blank">Streamlining Form Module Code Part-Five</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-six.html" target="_Blank">Streamlining Form Module Code Part-Six</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-seven.html" target="_Blank">Streamlining Form Module Code Part-Seven</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-eight.html" target="_Blank">Streamlining Form Module Code Part-Eight</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part-nine.html" target="_Blank">Streamlining Form Module Code Part-Nine</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part-ten.html" target="_Blank">Streamlining Form Module Code Part-Ten</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part.html" target="_Blank">Streamlining Form Module Code Part-Eleven</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-report-module-code-in.html" target="_Blank">Streamlining Report Module Code in Class Module</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-report-module-in-class.html" target="_Blank">Streamlining Report Module Code in Class Module-2</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-form-module-code-part-14.html" target="_Blank">Streamlining Form Module Code Part-14:All Controls</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-custom-made-form-wizard.html" target="_Blank">Streamlining Custom Made Form Wizard-15</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-form-vba-custom-report.html" target="_Blank">Streamlining Custom Report Wizard-16</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-form-vba-external-files.html" target="_Blank">Streamlining Form VBA External File Browser-17</a></li>
<li><a href="https://www.msaccesstips.com/2023/11/streamlining-event-procedures.html" target="_Blank">Streamlining Event Procedures of 3D TextWizard-18</a></li>
<li><a href="https://www.msaccesstips.com/2023/11/streamlining-event-procedures-rgbcolor.html" target="_Blank">Streamlining Event Procedures RGB Color Wizard-19</a></li>
<li><a href="https://www.msaccesstips.com/2023/12/streamlining-numbers-to-words-converter.html" target="_Blank">Streamlining Event Procedures Number To Words-20</a></li>
<li><a href="https://www.msaccesstips.com/2024/01/streamlining-form-module-code.html" target="_Blank">Streamlining VBA Code Presentation at Access User Groups.org (Europe)</a></li>
<li><a href="https://www.msaccesstips.com/2024/01/the-event-firing-mechanism-in-access.html" target="_Blank">The Event-firing Mechanism in Microsoft Access</a></li>
<li><a href="https://www.msaccesstips.com/2024/02/one-textbox-and-three-wrapper-class.html" target="_Blank">One TextBox and Three Wrapper Class Instances.</a></li>
<li><a href="https://www.msaccesstips.com/2024/02/streamlining-code-synchronized-forms.html" target="_Blank">Streamlined Coding Synchronized Floating Popup Form.</a></li>
</ol>
<p><br /></p><p><br /></p><p><br /></p><span class="syn-mode" id="ginger-button-for-rephrase" style="left: 579px; position: fixed; top: 143.667px; visibility: visible; z-index: 51;" title="Synonyms/Definitions"></span>a.p.r. pillaihttp://www.blogger.com/profile/02627590161919120214noreply@blogger.com0tag:blogger.com,1999:blog-7457417942281874810.post-56589928615318694542024-02-14T21:35:00.006+05:302024-02-29T11:01:13.117+05:30Streamlining Code Synchronized Forms<h3>Streamlining Form Module Code in Standalone Class Module.
</h3>
<p>The article on the <a href="https://www.msaccesstips.com/2009/02/synchronized-floating-popup-form.html" target="_blank">Synchronized Floating Popup Form</a> was originally published in February 2009. The notable difference is that the Event Subroutine code, once written in the Form Module, is now executed from the Standalone <a href="https://www.msaccesstips.com/2018/10/ms-access-class-module-and-vba.html" target="_blank">Class Module</a>.</p><p>All events triggered from the form and controls, such as TextBoxes and <a href="https://www.msaccesstips.com/2006/09/command-button-animation.html" target="_blank">Command Buttons</a>, are now captured in the Standalone Class Module, with the corresponding event subroutines being executed outside the Form Module. This exemplifies a paradigm shift where Form Module VBA codes are run from the Standalone Class Module, resulting in significantly reduced VBA code compared to what is typically written in the Form Module. At this point, the form serves solely for interface design purposes.</p>
<!--Google In-Article Ads start-->
<script async="" src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle" data-ad-client="ca-pub-2376623373770548" data-ad-format="fluid" data-ad-layout="in-article" data-ad-slot="6147807460" style="display: block; text-align: center;"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
<!--Google In-Article Ads END-->
<p>VBA Coding, Code Management, and debugging can be done independently without going into the Form Design View and Control's Event Property to get to the required Code in the Form Module. You can always find your Object group-level streamlined Event Subroutine Code, in one place in the <a href="https://www.msaccesstips.com/2019/04/withevents-ms-access-class-module.html" target="_blank">Standalone Class Module</a>, which is like one BeforeUpdate() Event Subroutine is only needed for 25 TextBoxes on the Form, even if all 25 of them need different sets of Code. If you already visited the earlier Episodes of this Topic you already know it by now. </p>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg5SdBaIubpGvFt_fBlXLYZtDKvDM-ZBoJTv3rGZdyRdHFzh0KDhyphenhyphen3UjUIdSGaf4YaSHK0hjCAPj-XXSfkvlEpR-bHu9-kw7F06qjHjv-YupsLdhe4hxcZseXuXNxqNNdTeUD_7Ko952khCbU8CxZOJ4bFwxRmYhHPExCFf4q74mcjwpPg6_ucgpB1TBW2b/s1056/Synchronized2.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="Synchronized Popup Form" border="0" data-original-height="649" data-original-width="1056" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg5SdBaIubpGvFt_fBlXLYZtDKvDM-ZBoJTv3rGZdyRdHFzh0KDhyphenhyphen3UjUIdSGaf4YaSHK0hjCAPj-XXSfkvlEpR-bHu9-kw7F06qjHjv-YupsLdhe4hxcZseXuXNxqNNdTeUD_7Ko952khCbU8CxZOJ4bFwxRmYhHPExCFf4q74mcjwpPg6_ucgpB1TBW2b/s320/Synchronized2.png" width="320" /></a></div>
<h3 style="text-align: left;">Synchronized Floating Popup Forms.</h3><p>The Employee record is structured into two distinct logical sets of information. The initial part encompasses Official Information, while the subsequent part entails Personal Information, including details such as address and phone number. To enhance user experience, these two sets of information are displayed in two separate, independent forms. Notably, the Personal Information section is not mandated to remain visible on the screen at all times. Instead, it can be accessed and displayed dynamically by clicking on the "<b>Personal Info</b>" command button. </p><p>Moreover, as the navigation control advances to the next record on the first Form, it triggers an immediate update of the corresponding data on the second Form, guaranteeing seamless synchronization between the two forms. This ensures that the information displayed on both forms remains consistently aligned as the user navigates through the records.</p><p>The second form can be kept from displaying on the Screen all the time, by Clicking on the <b>Close</b> Command Button. It can be displayed by selecting the <b>Personal Info</b> Command Button when needed.</p><p>The <b>OnDirty</b> and <b>BeforeUpdate</b> Events are enabled for all TextBoxes and ComboBoxes to safeguard the data integrity. </p><p>The first Form has several TextBoxes, two Comboboxes, and two Command Buttons. The second Form has several TextBoxes and a Command Button.</p><p>To achieve the desired functionality, we need three Wrapper Classes—one for TextBoxes, one for Command Buttons, and one for <a href="https://www.msaccesstips.com/2009/01/combo-box-column-values.html" target="_blank">ComboBoxes</a>—along with the Intermediary Class Module (Interface Class). The Interface Class will be responsible for creating the Wrapper Class instances, initializing them with the corresponding Object references, and storing these instances dynamically in a Collection Object in memory. This approach ensures that when events are triggered on the form, they can be captured in the respective Wrapper Classes, allowing the execution of the required Event Procedures within the Wrapper Class Instances. This modular and organized structure enhances code manageability and promotes effective event handling across different types of controls on the form.</p><h3 style="text-align: left;">The TextBox Wrapper Class VBA Code.</h3>
<pre>Option Compare Database
Option Explicit
Private WithEvents Txt As Access.TextBox
Private tfrm As Access.Form
'------------------------------------------------------
'Streamlining Form Module Code
'in Stand-alone Class Modules
'------------------------------------------------------
'TextBox Events
'Author: a.p.r. pillai
'Date : 16/01/2024
'Rights: All Rights(c) Reserved by www.msaccesstips.com
'------------------------------------------------------
'======== Form Object Property Procedure =========
Public Property Get t_Frm() As Access.Form
Set t_Frm = tfrm
End Property
Public Property Set t_Frm(ByRef vFrm As Access.Form)
Set tfrm = vFrm
End Property
'======== TextBox Object Property Procedure =========
Public Property Get t_txt() As Access.TextBox
Set t_txt = Txt
End Property
Public Property Set t_txt(ByRef vtxt As Access.TextBox)
Set Txt = vtxt
End Property
'======== Event Subroutine =========
Private Sub txt_Dirty(cancel As Integer)
If MsgBox("Are you Editing " & Txt.Name & " Field?", vbYesNo + vbCritical, Txt.Name & " Dirty()") = vbNo Then
cancel = True
End If
End Sub
Private Sub txt_BeforeUpdate(cancel As Integer)
If MsgBox("Save the Changes " & Txt.Name & " Field?", vbYesNo + vbCritical, Txt.Name & " Dirty()") = vbNo Then
cancel = True
End If
End Sub<span style="font-family: Times New Roman;"><span style="white-space: normal;">
</span></span></pre>
<p>The TextBox Wrapper Class streamlines the process with just two Event Procedures, each containing a few lines of reusable code. This significantly simplifies the handling of all TextBoxes on both forms. Contrast this with the scenario where you'd have to repetitively write these concise lines of code for each TextBox in both Form Modules. The Wrapper Class approach not only promotes code efficiency but also enhances maintainability by consolidating common functionality in a centralized location.</p><p>A crucial aspect worth highlighting is the utilization of the same TextBoxWrapper Class Instances for all TextBoxes across two distinct forms. This showcases the versatility of the new coding approach. This flexibility is particularly beneficial in scenarios involving one or more SubForms within the Main Form, as it allows for a unified and efficient management of TextBox events and functionality, promoting a cohesive and streamlined development process.</p>
<!--Google In-Article Ads start-->
<script async="" src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle" data-ad-client="ca-pub-2376623373770548" data-ad-format="fluid" data-ad-layout="in-article" data-ad-slot="6147807460" style="display: block; text-align: center;"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
<!--Google In-Article Ads END-->
<h3 style="text-align: left;">The Command Button Wrapper Class VBA Code.</h3>
<pre>Option Compare Database
Option Explicit
Private WithEvents cmd As Access.CommandButton
Private cFrm As Access.Form
Private uFrm As Access.Form
Private strSQL As String
'------------------------------------------------------
'Streamlining Form Module Code
'in Stand-alone Class Modules
'------------------------------------------------------
'CommandButton Events
'Author: a.p.r. pillai
'Date : 16/01/2024
'Rights: All Rights(c) Reserved by www.msaccesstips.com
'------------------------------------------------------
Public Property Get c_Frm() As Access.Form
Set c_Frm = cFrm
End Property
Public Property Set c_Frm(ByRef vFrm As Access.Form)
Set cFrm = vFrm
Set uFrm = Forms("Employee_Sub")
End Property
Public Property Get c_cmd() As Access.CommandButton
Set c_cmd = cmd
End Property
Public Property Set c_cmd(ByRef vcmd As Access.CommandButton)
Set cmd = vcmd
End Property
'====Event Subroutines====
Private Sub cmd_Click()
Select Case cmd.Name
Case "cmdClose"
If MsgBox("Close the Main Form?", _
vbYesNo + vbCritical, cmd.Name & "_Click()") = vbNo Then
'Do Nothing
Else
DoCmd.Close acForm, "Employee_Sub"
DoCmd.Close acForm, cFrm.Name
End If
Case "cmdPersonalInfo"
strSQL = "SELECT Employees.* FROM Employees "
strSQL = strSQL & "WHERE ([EmployeeID] = " & cFrm![EmployeeID] & ");"
uFrm.RecordSource = strSQL
uFrm.Requery
uFrm.Visible = True
cFrm.ActiveControl.SetFocus
Case "cmdCloseSub"
uFrm.Visible = False
End Select
End Sub<span style="font-family: Times New Roman;"><span style="white-space: normal;">
</span></span></pre>
<p>Having two <a href="https://www.msaccesstips.com/2009/01/command-button-animation-2.html" target="_blank">Command Buttons</a> on the Main Form and an additional one on the Second Form to close it is a common scenario. Writing the Command Button Click Event Subroutines in the same Wrapper Class Module, as demonstrated above, streamlines the code organization. Each Command Button corresponds to a dedicated Wrapper Class Instance, and these instances are assigned their respective Control <a href="https://www.msaccesstips.com/2009/03/ms-access-and-reference-library.html" target="_blank">References</a> from both forms.</p><p>The use of control references as keys is crucial for the system to accurately identify the correct instance of the Wrapper Class, ensuring that the appropriate Event Subroutine is executed for each Command Button click. This approach enhances code clarity, maintainability, and reusability across different forms and controls.</p><h3 style="text-align: left;">The ComboBox Wrapper Class VBA Code.</h3>
<pre>Option Compare Database
Option Explicit
Private WithEvents cbo As Access.ComboBox
Private bFrm As Access.Form
'-------------------------------------------------------
'Streamlining Form Module Code
'in Stand-alone Class Modules
'-------------------------------------------------------
'CommandButton Events
'Author: a.p.r. pillai
'Date : 16/01/2024
'Rights: All Rights(c) Reserved by www.msaccesstips.com
'-------------------------------------------------------
Public Property Get b_Frm() As Access.Form
Set b_Frm = bFrm
End Property
Public Property Set b_Frm(ByRef vFrm As Access.Form)
Set bFrm = vFrm
End Property
Public Property Get b_cbo() As Access.ComboBox
Set b_cbo = cbo
End Property
Public Property Set b_cbo(ByRef vcbo As Access.ComboBox)
Set cbo = vcbo
End Property
'==== ComboBox Event Subroutine ====
Private Sub cbo_Dirty(cancel As Integer)
If MsgBox("Are you Editing " & cbo.Name & " Control?", _
vbYesNo + vbCritical, cbo.Name & " Dirty()") = vbNo Then
cancel = True
End If
End Sub
Private Sub cbo_BeforeUpdate(cancel As Integer)
If MsgBox("Save the Changes " & cbo.Name & " Control?", _
vbYesNo + vbCritical, cbo.Name & " Dirty()") = vbNo Then
cancel = True
End If
End Sub
</pre>
<p>There are two Combo Boxes on the Main Form. The Comboboxes are also enabled with the OnDirty() and BeforeUpdate() Event Subroutines to safeguard the data from unintentional changes or to apply changes with the consent of the User.</p><h3 style="text-align: left;">The Interface Class Module VBA Code.</h3>
<pre>Option Compare Database
Option Explicit
Private ocmd As ClsCmdButton 'Wrapper Class
Private oTxt As ClsTextBox 'Wrapper Class
Private ocbo As ClsCombo 'Wrapper Class
Private WithEvents Frm As Access.Form 'Employees_Main
Private sFrm As Access.Form 'Employee_Sub
Private coll As New Collection
Private strSQL As String
'------------------------------------------------------
'Streamlining Form Module Code
'in Stand-alone Class Modules
'------------------------------------------------------
'Wrapper Classes Initialization
'Author: a.p.r. pillai
'Date : 16/01/2024
'Rights: All Rights(c) Reserved by www.msaccesstips.com
'------------------------------------------------------
Public Property Get i_Frm() As Access.Form
Set i_Frm = Frm
End Property
Public Property Set i_Frm(ByRef vFrm As Access.Form)
Set Frm = vFrm
Call Class_Init
End Property
Private Sub Class_Init()
Dim ctl As Control
Const EP = "[Event Procedure]"
'Open the 2nd Form
DoCmd.OpenForm "Employee_Sub", , , , , acHidden
'Assign the Form Reference to sFrm Object
Set sFrm = Forms("Employee_Sub")
'Enable the OnCurrent Event of the Employees_Main Form
Frm.OnCurrent = EP
For Each ctl In Frm.Controls 'Employees_Main Form Controls
Select Case TypeName(ctl)
Case "CommandButton"
Select Case ctl.Name '
Case "cmdClose", "cmdPersonalInfo"
Set ocmd = New ClsCmdButton 'Create new instance
Set ocmd.c_Frm = Frm 'Assign Main Form Reference
Set ocmd.c_cmd = ctl 'Pass current CommandButton Object Reference
ocmd.c_cmd.OnClick = EP 'Enable OnClick Event
coll.Add ocmd 'Add the instance to Collection Object
Set ocmd = Nothing 'Reset Wrapper Class instance
End Select
Case "TextBox"
Set oTxt = New ClsTextBox
Set oTxt.t_Frm = Frm
Set oTxt.t_txt = ctl
oTxt.t_txt.OnDirty = EP
oTxt.t_txt.BeforeUpdate = EP
oTxt.t_txt.BackColor = RGB(&HFF, &HF2, &H0)
oTxt.t_txt.BackStyle = 0
coll.Add oTxt
Set oTxt = Nothing
Case "ComboBox"
Set ocbo = New ClsCombo
Set ocbo.b_Frm = Frm
Set ocbo.b_cbo = ctl
ocbo.b_cbo.OnDirty = EP
ocbo.b_cbo.BeforeUpdate = EP
ocbo.b_cbo.BackColor = RGB(&HFF, &HF2, &H0)
ocbo.b_cbo.BackStyle = 0
coll.Add ocbo
Set ocbo = Nothing
End Select
Next
For Each ctl In sFrm.Controls 'Employees_Sub Form Controls
Select Case TypeName(ctl)
Case "CommandButton"
Select Case ctl.Name
Case "cmdCloseSub"
Set ocmd = New ClsCmdButton 'Instantiate
Set ocmd.c_Frm = sFrm
Set ocmd.c_cmd = ctl
ocmd.c_cmd.OnClick = EP
coll.Add ocmd 'Add to Collection
Set ocmd = Nothing
End Select
Case "TextBox"
Set oTxt = New ClsTextBox 'Instantiate
Set oTxt.t_Frm = sFrm
Set oTxt.t_txt = ctl
oTxt.t_txt.OnDirty = EP
oTxt.t_txt.BeforeUpdate = EP
oTxt.t_txt.BackColor = RGB(&HFF, &HF2, &H0)
oTxt.t_txt.BackStyle = 0
coll.Add oTxt 'Add to Collection
Set oTxt = Nothing
End Select
Next
End Sub
Private Sub frm_Current()
If IsLoaded("Employee_Sub") Then
strSQL = "SELECT Employees.* FROM Employees "
strSQL = strSQL & "WHERE ([EmployeeID] = " & Frm![EmployeeID] & ");"
sFrm.RecordSource = strSQL
Frm.SetFocus
End If
Private Sub Class_Terminate()
Set Frm = Nothing
Set sFrm = Nothing
Do While coll.Count > 0
coll.Remove 1
Loop
End Sub
</pre><p>The Wrapper Classes are declared as Properties of the Interface Class. Two Form Object Properties are declared to scan for Controls on Employees_Main and Employee_Sub Forms. </p><p>The <b>Frm</b> Object is qualified with the Keyword <b><a href="https://www.msaccesstips.com/2019/05/withevents-in-class-module-and-data.html" target="_blank">WithEvents</a></b> to enable the <b>OnCurrent</b> Event of the Employees_Main Form to update the Employee_Sub Form data based on the movement of Records on the Main Form. </p><p>The Collection Object and a String Variable strSQL are also declared as Properties in the Global Declaration area. </p><p>The Property Procedures for the <b>Frm</b> Object are declared next to receive the Main From Object Reference from the Form_Load() Event Procedure of the Employees_Main Form. After assigning the Main Form Reference to the <b>Frm </b>Object<b> </b>the Class_Init Subroutine is called for creating the Wrapper Class Instances and Initializing Procedures.</p><p>At the beginning of the <b>Class_Init()</b> Subroutine the <b>Employee_Sub</b> Form is open in <i>Hidden</i> Mode and kept in memory. The <b>sFrm</b> Form object is assigned with the Reference of the Employee_Sub Form.</p><p>The following statement enables the Main Form's <b>OnCurrent </b>Event to fire the Event when the record is moved from one to the other through the Record Navigation Control:</p><pre>'Enable the OnCurrent Event of the Employees_Main Form
Frm.OnCurrent = EP</pre><p>There are three sets of Objects on the Employees_Main Form: TextBoxes, Command Buttons, and Combo Boxes. The Employee_Sub Form has a few TextBoxes and a single Command Button to create Wrapper Class Instances and assign their related Object References.</p><p>The following VBA Code Segment scans the Employees_Main Form for Textboxes, Command Buttons, and ComboBoxes:</p><pre>For Each ctl In Frm.Controls 'Employees_Main Form Controls
Select Case TypeName(ctl)
Case "CommandButton"
Select Case ctl.Name '
Case <span style="color: red;">"cmdClose", "cmdPersonalInfo"</span>
Set ocmd = New ClsCmdButton 'Create new instance
Set ocmd.c_Frm = Frm 'Assign Main Form Reference
Set ocmd.c_cmd = ctl 'Pass current CommandButton Object Reference
ocmd.c_cmd.OnClick = EP 'Enable OnClick Event
coll.Add ocmd 'Add the instance to Collection Object
Set ocmd = Nothing 'Reset Wrapper Class instance
End Select
Case <span style="color: red;">"TextBox"</span>
Set oTxt = New ClsTextBox
Set oTxt.t_Frm = Frm
Set oTxt.t_txt = ctl
oTxt.t_txt.OnDirty = EP
oTxt.t_txt.BeforeUpdate = EP
<span style="color: red;"> oTxt.t_txt.BackColor = RGB(255, 242,0) 'Yellow Color
oTxt.t_txt.BackStyle = 0</span>
coll.Add oTxt
Set oTxt = Nothing
Case <span style="color: red;">"ComboBox"</span>
Set ocbo = New ClsCombo
Set ocbo.b_Frm = Frm
Set ocbo.b_cbo = ctl
ocbo.b_cbo.OnDirty = EP
ocbo.b_cbo.BeforeUpdate = EP
<span style="color: red;">ocbo.b_cbo.BackColor = RGB(255, 242, 0) 'Yellow Color
ocbo.b_cbo.BackStyle = 0</span>
coll.Add ocbo
Set ocbo = Nothing
End Select
Next </pre>
<p>The provided code segment examines the existence of Command Button, TextBox, and ComboBox controls on the Employees_Main Form. It proceeds to create instances of the corresponding Wrapper Classes, sets the Wrapper Class property values, enables the controls' OnDirty and BeforeUpdate Events, and then adds these instances to the Collection Object in memory. This systematic approach ensures that the necessary controls are properly encapsulated within their respective Wrapper Class Instances and facilitates organized event handling through the centralized Collection.</p><p>The dynamic modification of TextBox and ComboBox controls' BackColor and BackStyle property values enhances user interaction on the form. Specifically, these properties are adjusted to highlight the background of the controls in yellow when they become active. The BackStyle property is set to Transparent, ensuring that the yellow background color is only displayed when the TextBox or ComboBox is in focus, contributing to a visually intuitive and user-friendly design.</p><h3 style="text-align: left;">The Employee_Sub Form Controls VBA Code Segment.</h3>
<pre>For Each ctl In sFrm.Controls 'Employees_Sub Form Controls
Select Case TypeName(ctl)
Case "CommandButton"
Select Case ctl.Name
Case "cmdCloseSub"
Set ocmd = New ClsCmdButton 'Instantiate
Set ocmd.c_Frm = sFrm
Set ocmd.c_cmd = ctl
ocmd.c_cmd.OnClick = EP
coll.Add ocmd 'Add to Collection
Set ocmd = Nothing
End Select
Case "TextBox"
Set oTxt = New ClsTextBox 'Instantiate
Set oTxt.t_Frm = sFrm
Set oTxt.t_txt = ctl
oTxt.t_txt.OnDirty = EP
oTxt.t_txt.BeforeUpdate = EP
oTxt.t_txt.BackColor = RGB(&HFF, &HF2, &H0)
oTxt.t_txt.BackStyle = 0
coll.Add oTxt 'Add to Collection
Set oTxt = Nothing
End Select
Next
</pre>
<p>The second form exclusively contains TextBoxes, each equipped with OnDirty() and BeforeUpdate() Event Procedures, akin to the Main Form. If additional Event Subroutines are deemed necessary for these TextBoxes, beyond OnDirty() and BeforeUpdate(), they can be conveniently authored within the same Wrapper Class. The beauty of this approach lies in the uniqueness of references assigned to each individual TextBox. This ensures that the references are tied to the respective objects, enabling the system to accurately locate and execute the correct Event Procedure for each TextBox through the associated Wrapper Class Instances.</p><h3 style="text-align: left;">The Form_Current() Event Subroutine.</h3>
<pre>Private Sub frm_Current()
If IsLoaded("Employee_Sub") Then
strSQL = "SELECT Employees.* FROM Employees "
strSQL = strSQL & "WHERE ([EmployeeID] = " & Frm![EmployeeID] & ");"
sFrm.RecordSource = strSQL
Frm.SetFocus
End If
End Sub
Private Sub Class_Terminate()
Set Frm = Nothing
Set sFrm = Nothing
Do While coll.Count > 0
coll.Remove 1
Loop
End Sub
</pre><p>When the Record Navigation Button is employed to navigate to the next or previous record on the Employees_Main Form, the Form_Current() Event is triggered and captured by the Subroutine frm_Current() provided above. In response, a Query SQL is formulated using the EmployeeID value as a key to filter the data from the employee record. Subsequently, this Query is utilized as the Record Source of the second form, Employee_Sub, ensuring the synchronization of the Personal Info Data with the record currently displayed on the Employees_Main Form. This approach facilitates seamless coordination between the two forms, providing updated and synchronized information based on the selected record.</p>
<p>The <i><b>Sub Class_Terminate()</b></i> Subroutine works like the Form_Unload() Event Subroutine on the Form. When you close the Form, the Form_Unload() Event Procedure fires, if it is present on the Form before the Form is actually closed. This Subroutine clears the Forms declarations, removes all the Wrapper Class Instances from the <a href="https://www.msaccesstips.com/2018/12/ms-access-and-collection-object-basics.html" target="_blank">Collection Object</a>, and finally removes the Collection Object too.</p>
<h3 style="text-align: left;">The Employees_Main Form Module Code.</h3>
<pre>Option Compare Database
Option Explicit
Private obj As New ClsObject_Init
Private Sub Form_Load()
Set obj.i_Frm = Me
End Sub
Private Sub Form_Unload(cancel As Integer)
Set obj = Nothing
If IsLoaded("Employee_Sub") Then
DoCmd.Close acForm, "Employee_Sub"
End If
End Sub
</pre>
<p>Declares the Interface Object <b>ClsObj_Init</b> Class Module and Instantiates it with the Object Name <b>obj</b> in the Global Declaration area of the Form Module. </p><p>In the <b>Form_Load()</b> Event Subroutine the Form Object <b>Me</b> is passed to the <b>Frm</b> Property of <b>ClsObj_Init</b> Interface Class through the Property Procedure <b>i_Frm()</b> in this statement <i><b>Set obj.i_frm = Me</b>.</i></p><p>In the Form_Unload() Event Procedure the statement <b>Set obj = Nothing</b> attempts to clear ClsObj_Init Interface Class from memory. This will trigger the <b>Sub Class_Terminate()</b> Subroutine we discussed above.</p><p>Hope you enjoyed the new way of Coding and your feedback is highly appreciated.</p>
<h3>Demo Database Download</h3>
<!--Download Link /downloads/2024/02/SynchronizedForms2K.zip-->
<div class="separator" style="clear: both; text-align: center;">
<a href="https://drive.google.com/file/d/1cY-htq0qLBzobUJ13KC8QWEOIODI9H1S/view?usp=sharing">
<img border="0" data-original-height="29" data-original-width="22" height="29" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFE4uDLH9dk3S6pEcfTkT6GW6r11XqBI4HQnLnhQFZMKFeeQt5ABV_NqvLHM68J7RacISsvbUEl0PlAq6V7i0t0mQF3iesrBuqGuvrf5CJV72OzrGZ7rdMxkxzw8FYlz-qniNySoqtqUN4/s320/zip.gif" width="22" />SynchronizedForms2K.zip</a>
</div>
<!--Download Link /downloads/2024/02/SynchronizedForms2K.zip-->
<br />
<ol>
<li><a href="https://www.msaccesstips.com/2023/04/reusing-form-module-vba-code-for-new.html" target="_Blank">Re-using Form Module VBA Code for New Projects.</a></li>
<li><a href="https://www.msaccesstips.com/2023/04/streamlining-form-module-code-part-two.html" target="_Blank">Streamlining Form Module Code Part-Two</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/05/streamlining-form-module-code-part-three.html" target="_Blank">Streamlining Form Module Code Part-Three</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/06/streamlining-form-module-code-part-four.html" target="_Blank">Streamlining Form Module Code Part-Four</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/06/streamlining-form-module-code-part-five.html" target="_Blank">Streamlining Form Module Code Part-Five</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-six.html" target="_Blank">Streamlining Form Module Code Part-Six</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-seven.html" target="_Blank">Streamlining Form Module Code Part-Seven</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-eight.html" target="_Blank">Streamlining Form Module Code Part-Eight</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part-nine.html" target="_Blank">Streamlining Form Module Code Part-Nine</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part-ten.html" target="_Blank">Streamlining Form Module Code Part-Ten</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part.html" target="_Blank">Streamlining Form Module Code Part-Eleven</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-report-module-code-in.html" target="_Blank">Streamlining Report Module Code in Class Module</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-report-module-in-class.html" target="_Blank">Streamlining Report Module Code in Class Module-2</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-form-module-code-part-14.html" target="_Blank">Streamlining Form Module Code Part-14:All Controls</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-custom-made-form-wizard.html" target="_Blank">Streamlining Custom Made Form Wizard-15</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-form-vba-custom-report.html" target="_Blank">Streamlining Custom Report Wizard-16</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-form-vba-external-files.html" target="_Blank">Streamlining Form VBA External File Browser-17</a></li>
<li><a href="https://www.msaccesstips.com/2023/11/streamlining-event-procedures.html" target="_Blank">Streamlining Event Procedures of 3D TextWizard-18</a></li>
<li><a href="https://www.msaccesstips.com/2023/11/streamlining-event-procedures-rgbcolor.html" target="_Blank">Streamlining Event Procedures RGB Color Wizard-19</a></li>
<li><a href="https://www.msaccesstips.com/2023/12/streamlining-numbers-to-words-converter.html" target="_Blank">Streamlining Event Procedures Number To Words-20</a></li>
<li><a href="https://www.msaccesstips.com/2024/01/streamlining-form-module-code.html" target="_Blank">Streamlining VBA Code Presentation at Access User Groups.org (Europe)</a></li>
<li><a href="https://www.msaccesstips.com/2024/01/the-event-firing-mechanism-in-access.html" target="_Blank">The Event-firing Mechanism in Microsoft Access</a></li>
<li><a href="https://www.msaccesstips.com/2024/02/one-textbox-and-three-wrapper-class.html" target="_Blank">One TextBox and Three Wrapper Class Instances.</a></li>
</ol>
<span class="syn-mode" id="ginger-button-for-rephrase" style="left: 287px; position: fixed; top: 151.781px; visibility: visible; z-index: 51;" title="Rephrase"></span>a.p.r. pillaihttp://www.blogger.com/profile/02627590161919120214noreply@blogger.com0tag:blogger.com,1999:blog-7457417942281874810.post-53353541548017107822024-02-01T17:26:00.008+05:302024-03-16T08:10:58.197+05:30One TextBox and Three Wrapper Class Instances.<h3 style="text-align: left;"> Streamlining Event Subroutines in Standalone Class Module.</h3><h4 style="text-align: left;">The Standalone Class Module Coding Rules overview.</h4><ol style="text-align: left;"><li>One <a href="https://www.msaccesstips.com/2023/05/streamlining-form-module-code-part-three.html" target="_blank">Wrapper Class Module</a> for several objects of the same Type (E.g.: TextBox) on the Form. <b>Example: ClsTextBox.</b></li><li><p>One Event Subroutine per Event for several Objects of the same type on the Form.</p><p>Example: </p>
<pre>Private Sub txt_AfterUpdate()
Select Case TypeName(Ctl)
Case "TextBox"
Select Case Ctl.Name
Case "Text0"
'Code
Case "Text2","Text4","Text6"
'Code
Case "Text8"
'Code
End Select
End Select
End Sub</pre>
</li>
<!--Google In-Article Ads start-->
<script async="" src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle" data-ad-client="ca-pub-2376623373770548" data-ad-format="fluid" data-ad-layout="in-article" data-ad-slot="6147807460" style="display: block; text-align: center;"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
<!--Google In-Article Ads END-->
<li><p>Wrapper Class instances are established, with an individual Instance designated for each TextBox on the form. Subsequently, each Instance is assigned its respective TextBox Reference.</p></li>
<li><p>Let us look at the Class_Init() Subroutine VBA Code and see how this process is initiated in the Intermediary (or Interface) Class Module - ClsObj_Init. </p>
<pre> Private Sub Class_Init()
Dim Ctl As Control
For Each Ctl In Frm.Controls
Select Case TypeName(Ctl)
Case "TextBox"
Set txt = New ClsTextBox
Set txt.m_frm = Frm
Set txt.m_txt = Ctl
txt.m_txt.AfterUpdate = "[Event Procedure]"
Coll.Add txt
Set txt = Nothing
End Select
Next
</pre>
</li><li>The above Subroutine modifies the AfterUpdate Event Property Values with the text <b>[Event Procedure]</b> option at run-time.</li><li><p>The Enter Key press, after typing something in the TextBox, fires the AfterUpdate Event. </p></li><li><p>If the AfterUpdate Property is assigned with a <b>Macro</b> or Public <b>Function Name</b> then the Event will call the Macro or Function directly. The Form doesn't need a Class Module to run these two options.</p></li><li><p>As we are all aware, typically, we write one Subroutine per Event and create one Wrapper Class Instance per <a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-seven.html" target="_blank">TextBox</a>. However, what happens if we deviate from this convention and create three Wrapper Class Instances, with AfterUpdate Event Subroutine, for a single TextBox (e.g., <b>Text0</b>) and assign all three Instances with the same Text0 <b>Reference </b>(the Memory Address of Text0 TextBox)<b>?</b></p></li></ol><ul style="text-align: left;"><li><p>How does the AfterUpdate Event fire from all three Instances? All of them together or one after the other? </p></li><li><p>What will happen if three different Macro Names or Function Names are assigned to the AfterUpdate Property, for all three Wrapper Class Instances, like the example Code segment shown below:</p>
<pre> For j = 1 To 3 'To create three Instances
Set ctxt = New ClsTextBox
Set ctxt.txt = ctl 'Text0 Reference
ctxt.param = j
'Macro1, Macro2, Macro3
ctxt.txt.AfterUpdate = "Macro" & CStr(j)
coll.Add ctxt
Set ctxt = Nothing
Next
</pre>
<p>To conduct this experiment and explore the outcomes, we require a TextBox on a Form named Text0, along with the TextBox Wrapper Class Module and the Intermediary (or Interface) Class Module to set up the necessary elements.</p></li></ul><p>The Image of the Form is given below:</p>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEifS5gYvmH4yjLc5sAJXQJvs59NqjjaqOHF_MixZO3kkq83i5VhvV8aZ2CetFEkNPGPGE1MMMxktc6ohRJlpz4pCs6RxaEKLnXYr-DOyCxbpjXRYkJssSizSiA1JbsMxU3F6hzDBRis9PpDxAZZZeCvKHIGmpLJI-hgF1wba5Wcdqfwb7ndDVpS6sfxTbkf/s1036/Text0Instances.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="Text0 Instances" border="0" data-original-height="833" data-original-width="1036" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEifS5gYvmH4yjLc5sAJXQJvs59NqjjaqOHF_MixZO3kkq83i5VhvV8aZ2CetFEkNPGPGE1MMMxktc6ohRJlpz4pCs6RxaEKLnXYr-DOyCxbpjXRYkJssSizSiA1JbsMxU3F6hzDBRis9PpDxAZZZeCvKHIGmpLJI-hgF1wba5Wcdqfwb7ndDVpS6sfxTbkf/s320/Text0Instances.png" width="320" /></a></div>
<h3>Wrapper Class Module: ClsTextBox VBA Code.</h3>
<pre style="text-align: left;">Option Compare Database
Option Explicit
Public WithEvents txt As Access.TextBox
Public param As Integer
Private Sub txt_AfterUpdate()
Dim x As Variant
Dim msg As String
msg = "INSTANCE OF " & UCase(txt.Name)
Select Case param
Case 1
'DoCmd.RunMacro "Macro1"
'x = DisplayText1()
MsgBox "1st " & msg
Case 2
'DoCmd.RunMacro "Macro2"
'x = DisplayText2()
MsgBox "2nd " & msg
Case 3
'DoCmd.RunMacro "Macro3"
'x = DisplayText3()
MsgBox "3rd " & msg
End Select
End Sub
</pre>
<h3>Review of Wrapper Class Code.</h3>
<p>The <a href="https://www.msaccesstips.com/2023/06/streamlining-form-module-code-part-four.html" target="_blank">TextBox</a> Object <b>Txt</b> is declared with Public Scope and qualified with the Keyword <b>WithEvents.</b> There is another Property <b>param</b> also declared with Public Scope. </p><p>Following this, the <b>Sub txt_AfterUpdate()</b> Event procedure will execute three times consecutively. This occurs for the same event procedure but originates from three distinct Wrapper Class instances. The execution order aligns with the sequential creation of the instances, starting with the first instance, followed by the second, and concluding with the third.</p>
<!--Google In-Article Ads start-->
<script async="" src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle" data-ad-client="ca-pub-2376623373770548" data-ad-format="fluid" data-ad-layout="in-article" data-ad-slot="6147807460" style="display: block; text-align: center;"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
<!--Google In-Article Ads END-->
<p>To discern the order of Wrapper Class instance creation and execution sequence, a sequence number is passed as a parameter to the param variable. When the AfterUpdate event subroutine is executed, the number within the param variable is displayed in a message text. This approach allows us to identify from which Wrapper Class instance the message is displayed and in what order.</p><p>The Macro and Function Name demo running <a href="https://www.msaccesstips.com/2018/10/ms-access-class-module-and-vba.html" target="_blank">VBA Code</a> lines are temporarily disabled for test-running the <b>[Event Procedure]</b> alone. </p><h3>The Intermediary or Interface Class Module ClsObj_Init VBA Code.</h3>
<pre>Option Compare Database
Option Explicit
Private ctxt As ClsTextBox
Private frm As Form
Private WithEvents cmd As CommandButton
Dim coll As New Collection
Public Property Get m_Frm() As Form
Set m_Frm = frm
End Property
Public Property Set m_Frm(ByVal vFrm As Form)
Set frm = vFrm
Call Class_Init
End Property
Private Sub Class_Init()
Dim ctl As Control
Dim j As Integer
Const EP = "[Event Procedure]"
For Each ctl In frm.Controls
Select Case TypeName(ctl)
Case "TextBox"
Select Case ctl.Name
Case "Text0" 'The Text0 Object Reference.
For j = 1 To 3
Set ctxt = New ClsTextBox
Set ctxt.txt = ctl
ctxt.param = j
'ctxt.txt.AfterUpdate = "Macro" & CStr(j) 'Macro1, 2, 3
'ctxt.txt.AfterUpdate = "=DisplayText" & CStr(j) & "()"
ctxt.txt.AfterUpdate = "[Event Procedure]"
coll.Add ctxt
Set ctxt = Nothing
Next
End Select
Case "CommandButton"
Select Case ctl.Name
Case "CmdClose"
Set cmd = frm.cmdClose
cmd.OnClick = "[Event Procedure]"
End Select
End Select
Next
End Sub
Private Sub cmd_Click()
DoCmd.Close acForm, frm.Name
End Sub
</pre>
<p>As usual, the first two lines of Code in the global declaration area, the TextBox Wrapper Class Object <b>ctxt,</b> and the Form object <b>frm</b> are declared.</p>
<p>A <a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part-ten.html" target="_blank">Command Button</a> on the form requires a Click Event to close the form. To capture the event in the Interface Class Module when fired on the form, a Command Button object is declared in the global area, qualified with the keyword <b>WithEvents</b>. It's important to note that since there is only one Command Button on the form, handling the Click Event in the Interface Class Module can be done directly without the need to create a Wrapper Class.</p><p>A <a href="https://www.msaccesstips.com/2018/12/ms-access-and-collection-object-basics.html" target="_blank">Collection object</a> is declared to store all TextBox object instances in memory, enabling the capture of events fired on the form and the execution of the corresponding event subroutines.</p>
<p>When the Form is open the Form Object is passed to the Form object's <i>Set Property Procedure</i>. The Object reference is assigned to the <b>frm</b> Property.</p><p>Next, the Class_Init() Subroutine is called.</p>
<p>In the For...Next loop, the Code scans the Form for the Text0 TextBox. Once found, the inner <b>For...Next</b> loop is configured to run for three cycles, creating three instances of the Wrapper Class Object <b>ctxt</b>. All three instances are assigned with the same <b>Text0</b> Control's reference. It's worth noting that this can be achieved without using the For...Next loop by duplicating the code three times. However, for consistency, we adhere to the coding style employed thus far.</p><p>When the Wrapper Class instances are created, the sequence number is passed to the Property <b>ctxt.param</b>. This number is then displayed in the MsgBox within the AfterUpdate Subroutine. This approach proves beneficial for identifying the execution order of the Event Subroutines, mirroring the sequence in which the instances are created.</p><p>If you manually create an AfterUpdate Event Subroutine in the Form Module in addition to the three instances, that Form Module Event Subroutine will execute first, followed by the Subroutines in the Wrapper Class instances. This sequence ensures that any manually added code in the Form Module takes precedence over the dynamically created instances.</p>
<p>As you have seen in the AfterUpdate Event Subroutine we can check the sequence number in the <b>param</b> Property (see the Code segment given below) and based on its sequential order it is possible to call three different Sets of Programs when a single AfterUpdate or similar Event fires.</p>
<pre>msg = "INSTANCE OF " & UCase(txt.Name)
Select Case param
Case 1
'DoCmd.RunMacro "Macro1"
'x = DisplayText1()
MsgBox "1st " & msg
Case 2
'DoCmd.RunMacro "Macro2"
'x = DisplayText2()
MsgBox "2nd " & msg
Case 3
'DoCmd.RunMacro "Macro3"
'x = DisplayText3()
MsgBox "3rd " & msg
End Select
</pre>
<p>The test run of the Event Subroutine Image (message from the first Instance) is given below:</p>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjzAKEd1XlqpbkienEJmCaY8Bvc1atLHo8km59pam0a2G06ZrKJEX1_qG7sYR2pzfZdAfflblxz3EUygvKebH8tb0DYrRliZRBB4ruU6-DLXf3hXEZKssNbowK8RAODHJCN3L-rx-_l2oIAh7o6ltz89-tho7oyYIEJJmYWlRX4Zg624mSVMOWyQo7tYFCL/s1034/Text0InstancesView.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="Event Procedure Message" border="0" data-original-height="831" data-original-width="1034" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjzAKEd1XlqpbkienEJmCaY8Bvc1atLHo8km59pam0a2G06ZrKJEX1_qG7sYR2pzfZdAfflblxz3EUygvKebH8tb0DYrRliZRBB4ruU6-DLXf3hXEZKssNbowK8RAODHJCN3L-rx-_l2oIAh7o6ltz89-tho7oyYIEJJmYWlRX4Zg624mSVMOWyQo7tYFCL/s320/Text0InstancesView.png" width="320" /></a></div>
<p>The provided screenshot captures the moment when the message is displayed from the AfterUpdate() Event Subroutine, indicating that the message originates from the first instance of the ClsTextBox Wrapper Class. Subsequently, two similar messages follow, each indicating the 2nd and 3rd instances, respectively.</p><h3 style="text-align: left;">Experiments with Macro Names: Macro1, Macro2, and Macro3</h3>
<p>In the upcoming experiment, we will replace the text <b>[Event Procedure] </b>with the Macro names <b>Macro1, Macro2, and Macro3</b> to trigger the AfterUpdate Event. It's important to note that no alterations will be made to the TextBox Wrapper Class VBA Code. Below is the modified code for the Class_Init() Subroutine in the Interface Module for your reference.</p>
<pre>Private Sub Class_Init()
Dim ctl As Control
Dim j As Integer
Const EP = "[Event Procedure]"
For Each ctl In frm.Controls
Select Case TypeName(ctl)
Case "TextBox"
Select Case ctl.Name
Case "Text0"
For j = 1 To 3
Set ctxt = New ClsTextBox
Set ctxt.txt = ctl
ctxt.param = j
'Macro1, Macro2, Macro3
ctxt.txt.AfterUpdate = "Macro" & CStr(j)
coll.Add ctxt
Set ctxt = Nothing
Next
End Select
Case "CommandButton"
Select Case ctl.Name
Case "CmdClose"
Set cmd = frm.cmdClose
cmd.OnClick = "[Event Procedure]"
End Select
End Select
Next
End Sub
</pre>
<p>All three Macros have a Message Box Function that displays a message with the Macro Version Number 1, 2, and 3 as shown below:</p>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiT6-I5e7LTFs7mm-Lm-hEjFGEfs3GOXM4KvPHT_MkC7zG0VBBHHzP58bxitKwSeOUSNtx5CFuq0UBDjMWckD12uBUeM632lFj01uLa58eiJSTsO3-pUZUS_0jgTlOTwgGKsPY0kSgohl3bEP2OGnshayv7OSBCFgtKXPWLTrl84ggAUijv0v24tg_BmB9J/s805/Mac123.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="639" data-original-width="805" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiT6-I5e7LTFs7mm-Lm-hEjFGEfs3GOXM4KvPHT_MkC7zG0VBBHHzP58bxitKwSeOUSNtx5CFuq0UBDjMWckD12uBUeM632lFj01uLa58eiJSTsO3-pUZUS_0jgTlOTwgGKsPY0kSgohl3bEP2OGnshayv7OSBCFgtKXPWLTrl84ggAUijv0v24tg_BmB9J/s320/Mac123.png" width="320" /></a></div>
<p>In the Initializing <b>Class_Init()</b> Subroutine, the AfterUpdate Property is assigned with the Macro Names: Macro1, Macro2, and Macro3 as we normally do with the <b>[Event Procedure] </b>Option. </p><p>Following the modifications in the VBA code, input some text into the TextBox and press the Enter key to trigger the AfterUpdate event. Subsequently, you will be greeted with a message from Macro3 exclusively. As previously emphasized, it's crucial to note that the Form Class Module or the Wrapper Class does not play a role in the invocation of the macro or function from the AfterUpdate or any other event property.</p><h4>Running Public Functions: =DisplayText1(), =DisplayText2(), =DisplayText3().</h4>
<p>It's worth noting that there are three functions in the standard module bearing the names mentioned above. Each of these functions utilizes the MsgBox function to display text, identifying the sequence number as depicted in their respective names.</p><p>If you substitute the AfterUpdate event-enabling line in the Class_Init() subroutine with the provided line of code and subsequently open the form, triggering the AfterUpdate event will reveal that the displayed message emanates solely from the function DisplayText3().</p>
<pre>ctxt.txt.AfterUpdate = "=DisplayText" & CStr(j) & "()"</pre>
<p>In contrast to the [Event Procedure] option, which invokes the RaiseEvent action, the macro and function name coding pertains to the direct modification of the AfterUpdate Event Property of the TextBox on the Property Sheet. This manual alteration involves replacing the existing name with three different names successively, with the final one prevailing.</p>
<h3>How to Run Macros or Functions.</h3>
<p>Indeed, the experimental approach undertaken served as an extraordinary demonstration, underscoring the crucial point that macro or function names coded in the Event Property are executed directly by the system. The involvement of the Wrapper Class Instance is entirely bypassed in this process. Whether the names are manually written in the Event Property or scripted through the <b>Class_Init()</b> Subroutine, the modifications are consistently made directly to the TextBox Property.</p>
<p>To alleviate potential confusion, a recommended approach is to consistently opt for the <b>[Event Procedure] </b>option and then call the macro or function, if needed, from within the TextBox Wrapper Class Instance-based subroutine. An illustrative example is provided below:</p>
<pre>Private Sub txt_AfterUpdate()
Dim x As Variant
Select Case txt.Name
Case "Text0"
'DoCmd.RunMacro "Macro1" ' Run Macro
'x = DisplayText1() ' Run Function
MsgBox "TextBox" & txt.Name
End Select
</pre>
<p>By adopting this method, clarity is enhanced, and the direct execution of macros or functions from the Event Property remains seamless within the designated TextBox Wrapper Class Instance. </p>
<h3>Demo Database Download</h3>
<!--Download Link /downloads/2024/02/MultipleInstances.zip-->
<div class="separator" style="clear: both; text-align: center;">
<a href="https://drive.google.com/file/d/14QcXRdn8X9-DKml3q9NLSsX-GzJtg0bU/view?usp=sharing">
<img border="0" data-original-height="29" data-original-width="22" height="29" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFE4uDLH9dk3S6pEcfTkT6GW6r11XqBI4HQnLnhQFZMKFeeQt5ABV_NqvLHM68J7RacISsvbUEl0PlAq6V7i0t0mQF3iesrBuqGuvrf5CJV72OzrGZ7rdMxkxzw8FYlz-qniNySoqtqUN4/s320/zip.gif" width="22" />MultipleInstances.zip</a>
</div>
<!--Download Link /downloads/2024/02/MultipleInstances.zip-->
<br />
<ol>
<li><a href="https://www.msaccesstips.com/2023/04/reusing-form-module-vba-code-for-new.html" target="_Blank">Re-using Form Module VBA Code for New Projects.</a></li>
<li><a href="https://www.msaccesstips.com/2023/04/streamlining-form-module-code-part-two.html" target="_Blank">Streamlining Form Module Code Part-Two</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/05/streamlining-form-module-code-part-three.html" target="_Blank">Streamlining Form Module Code Part-Three</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/06/streamlining-form-module-code-part-four.html" target="_Blank">Streamlining Form Module Code Part-Four</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/06/streamlining-form-module-code-part-five.html" target="_Blank">Streamlining Form Module Code Part-Five</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-six.html" target="_Blank">Streamlining Form Module Code Part-Six</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-seven.html" target="_Blank">Streamlining Form Module Code Part-Seven</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-eight.html" target="_Blank">Streamlining Form Module Code Part-Eight</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part-nine.html" target="_Blank">Streamlining Form Module Code Part-Nine</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part-ten.html" target="_Blank">Streamlining Form Module Code Part-Ten</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part.html" target="_Blank">Streamlining Form Module Code Part-Eleven</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-report-module-code-in.html" target="_Blank">Streamlining Report Module Code in Class Module</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-report-module-in-class.html" target="_Blank">Streamlining Report Module Code in Class Module-2</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-form-module-code-part-14.html" target="_Blank">Streamlining Form Module Code Part-14:All Controls</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-custom-made-form-wizard.html" target="_Blank">Streamlining Custom Made Form Wizard-15</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-form-vba-custom-report.html" target="_Blank">Streamlining Custom Report Wizard-16</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-form-vba-external-files.html" target="_Blank">Streamlining Form VBA External File Browser-17</a></li>
<li><a href="https://www.msaccesstips.com/2023/11/streamlining-event-procedures.html" target="_Blank">Streamlining Event Procedures of 3D TextWizard-18</a></li>
<li><a href="https://www.msaccesstips.com/2023/11/streamlining-event-procedures-rgbcolor.html" target="_Blank">Streamlining Event Procedures RGB Color Wizard-19</a></li>
<li><a href="https://www.msaccesstips.com/2023/12/streamlining-numbers-to-words-converter.html" target="_Blank">Streamlining Event Procedures Number To Words-20</a></li>
<li><a href="https://www.msaccesstips.com/2024/01/streamlining-form-module-code.html" target="_Blank">Streamlining VBA Code Presentation at Access User Groups.org (Europe)</a></li>
<li><a href="https://www.msaccesstips.com/2024/01/the-event-firing-mechanism-in-access.html" target="_Blank">The Event-firing Mechanism in Microsoft Access</a></li>
</ol>a.p.r. pillaihttp://www.blogger.com/profile/02627590161919120214noreply@blogger.com0tag:blogger.com,1999:blog-7457417942281874810.post-48140184682117904262024-01-20T20:42:00.001+05:302024-02-14T21:41:30.769+05:30The Event Firing Mechanism in Access Objects<h3 style="text-align: left;"> Streamlining Event Subroutine Code in Standalone Class Module.</h3><h4 style="text-align: left;">How Does the Event Firing Mechanism Work within Access Objects?</h4><p>This topic was briefly touched on during the Presentation of <a href="https://youtu.be/AjvjN3h1ipY" target="_blank">Streamlining Form Module Code</a> in the Standalone Class Module for <a href="https://accessusergroups.org/europe/" target="_blank">Access User Groups (Europe)</a> Chapter. </p><p>The Event-related Key Words: <b>Event</b>, <b>RaiseEvent</b>, and <b>WithEvents.</b></p><ol style="text-align: left;"><li><b>Event - </b>used to Define an Event.</li><li><b>RaiseEvent</b> - to Invoke the Event.</li><li><b>WithEvents</b> - to Capture the fired Event and execute the Event Subroutine Code.</li></ol>
<!--Google In-Article Ads start-->
<script async="" src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle" data-ad-client="ca-pub-2376623373770548" data-ad-format="fluid" data-ad-layout="in-article" data-ad-slot="6147807460" style="display: block; text-align: center;"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
<!--Google In-Article Ads END-->
<p>In the preceding articles, we gained insights into the utilization of Event-related Keywords and crafted <a href="https://www.msaccesstips.com/2023/06/streamlining-form-module-code-part-four.html" target="_blank">Event Subroutines </a>within Standalone Class Modules rather than within the Form Module. Notably, the Event and WithEvents keywords were prominently featured in the Object Browser, as illustrated below:</p>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhKeutMvjQFAWvdx0_OImSshjdj-c4UTG4LJRS3MiP38vddUrA1YMDFt54aJxIbJV_bpNzZNP2cxPwdIcGtVY-X8D1J03yIqib1VWdR24_OIRTHD3u94Kr5aGxbbzI6v06DNGKcsBUtEOkdOFHTB2VYUCfVzgiRY_9WLjkqENMEb-tsDnBBM1pMe-fAPVhq/s985/Slide4Image.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="528" data-original-width="985" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhKeutMvjQFAWvdx0_OImSshjdj-c4UTG4LJRS3MiP38vddUrA1YMDFt54aJxIbJV_bpNzZNP2cxPwdIcGtVY-X8D1J03yIqib1VWdR24_OIRTHD3u94Kr5aGxbbzI6v06DNGKcsBUtEOkdOFHTB2VYUCfVzgiRY_9WLjkqENMEb-tsDnBBM1pMe-fAPVhq/s320/Slide4Image.png" width="320" /></a></div>
<p>However, the <b>RaiseEvent</b> serves as an internal event-firing mechanism that accommodates multiple options within a dedicated event-related property. This Event is initiated from within the Class Object, functioning as a system program. It assesses the specified option in the event property and executes the selected choice, be it a macro, function (user-defined or built-in), or the text [Event Procedure]. This, in turn, triggers the <a href="https://www.msaccesstips.com/2023/04/reusing-form-module-vba-code-for-new.html" target="_blank">RaiseEvent</a>, like the functionality of the Call Statement in VBA.</p><p>The following Link gives the details about the RaiseEvent Statement: <a href="https://learn.microsoft.com/en-us/office/vba/language/reference/user-interface-help/raiseevent-statement" target="_blank">https://learn.microsoft.com/en-us/office/vba/language/reference/user-interface-help/raiseevent-statement</a></p><p>The authentic Event-firing mechanism is an internal system program intricately linked to each event of an object within the Access System. For instance, the AfterUpdate Event encompasses an AfterUpdateMacro, typically concealed from the Object Browser Window. To reveal it, one can simply right-click and select the option '<i>Show Hidden Members</i>'. Analogously, other Object Events, such as CommandButton Click, feature the OnClick Event property to specify the execution option. Furthermore, there's the OnClickMacro, which evaluates the provided value in the OnClick Property, facilitating the execution of the designated option. </p><h3 style="text-align: left;">Assumptions Based on Observation.</h3><p>Upon scrutinizing the execution pattern of the specified option within the Event Property, I found it worthwhile to attempt to create a straightforward subroutine that emulates the methodology employed by the Event mechanism. This endeavor aims to replicate the process by which the event mechanism executes the designated option specified in the Property Sheet.</p><p>An interesting observation emerges: when a macro name or function name is specified in the Event Property, it triggers the execution of the designated macro or built-in function, as well as user-defined functions in a standard module. Notably, for these two options, there is no requisite attachment of a <a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-seven.html" target="_blank">Class Module</a> to the Form, and the '<b>Has Module</b>' Property of the Form can be set to False.</p>
<!--Google In-Article Ads start-->
<script async="" src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle" data-ad-client="ca-pub-2376623373770548" data-ad-format="fluid" data-ad-layout="in-article" data-ad-slot="6147807460" style="display: block; text-align: center;"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
<!--Google In-Article Ads END-->
<p>However, opting for the <b>[Event Procedure]</b> choice in the Event Property triggers an automatic attachment of a Class Module to the Form. Simultaneously, an empty Event Subroutine Stub is seamlessly added to the <a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part-nine.html" target="_blank">Form Module</a>, serving as a canvas to inscribe the necessary subroutine code within this newly associated Class Module. </p><p>The observed behavior of the Event running mechanism implies that when a value is entered into the Event Property in the Property Sheet, the system intelligently identifies it. Each of the three options—Macro, Expression, or the text [Event Procedure]—bears distinctive markers, enabling the system to recognize and execute the corresponding event-related action accordingly. This nuanced recognition ensures that the system interprets and responds to the specified option accurately.</p><p><i>Expression</i>: Should start with an equal symbol (<b>=</b>) character followed by the Function Name and Parameters, if any, like <b>=MsgBox("Hello World")</b> or a User-Defined Function in the Standard Module.</p><p>Building upon these assumptions, I've crafted a subroutine designed to emulate this behavior when one of the three aforementioned options—Macro, Expression, or [Event Procedure]—is provided in a <a href="https://www.msaccesstips.com/2023/05/streamlining-form-module-code-part-three.html" target="_blank">TextBox Control</a>.</p><p>The Event Running Form Image is given below:</p>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiFFtW1k6CfShwXUDHTUCSaTxmJnzRdHDCcj3N7jPpAcM3dP24dL5Xwq5ejbOcY7fdvLOda1zsda3bH5WQ2BQg028SqLSt-uic8ceMze0_hdh2pTcdw1glZtyI6pCPKDFfQQOwEzYXrtEcz-Df623m_AFpOwNf-tftyA6lOgoSxFioQK9whTuvfyPH0DlDx/s1059/RaiseEventDemo.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="855" data-original-width="1059" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiFFtW1k6CfShwXUDHTUCSaTxmJnzRdHDCcj3N7jPpAcM3dP24dL5Xwq5ejbOcY7fdvLOda1zsda3bH5WQ2BQg028SqLSt-uic8ceMze0_hdh2pTcdw1glZtyI6pCPKDFfQQOwEzYXrtEcz-Df623m_AFpOwNf-tftyA6lOgoSxFioQK9whTuvfyPH0DlDx/s320/RaiseEventDemo.png" width="320" /></a></div>
<p>The above Form is divided into two Parts:</p><ol style="text-align: left;"><li>The upper portion of the form, delineated by the thick horizontal black line in the middle, serves as our experimental ground for exploring the event execution method. Here, we endeavor to unravel the intricacies of the actual event running mechanism within the Access System.</li><li><p>In the Section below the horizontal line, we will run the same Event Options as we normally do in the AfterUpdate Property of a TextBox.</p></li></ol><p></p><p>Within the initial section, a ListBox offers a range of options that can be swiftly selected by a simple click. Upon selection, the chosen option promptly populates the Text2 TextBox Control above. Analogous to the AfterUpdate Event Property in TextBox's Property Sheet, the specified option in the TextBox Control instantaneously executes within our Event Subroutine <b>AfterUpdateMac()</b>. </p><p>The Event running Subroutine AfterUpdateMac() is written within the Standalone Class Module <b>ClsAfterUpdateMacro</b>. </p><h3 style="text-align: left;">The ClsAfterUpdateMacro Class Module VBA Code.</h3><p>The Class Module with the Subroutine <b>AfterUpdateMac()</b> VBA Code is given below:</p>
<pre style="text-align: left;">Option Compare Database
Option Explicit
'User-Defined Event
Public Event AfterUpdat(ByVal txt As String)
Private After_Update As String
'Options: Macro,Function,"[Event Procedure]"
'------------------------------------------------------
'Streamlining Form Module Code
'in Stand-alone Class Modules
'------------------------------------------------------
'AfterUpdateMac() Event Processing Subroutine
'Author: a.p.r. pillai
'Date : 19/01/2024
'Rights: All Rights(c) Reserved by www.msaccesstips.com
'------------------------------------------------------
Public Property Get OnAfterUpdate() As String
OnAfterUpdate = After_Update
End Property
Public Property Let OnAfterUpdate(ByVal vNewValue As String)
After_Update = vNewValue
Call AfterUpdateMac
End Property
Private Sub AfterUpdateMac()
'Evaluate the given option
'and Run the Event action
Dim opt As String
Dim vx As Variant
On Error GoTo AfterUpdateMac_Err
opt = Nz(After_Update, "")
If Len(opt) = 0 Then
Exit Sub
ElseIf UCase(opt) = "[EVENT PROCEDURE]" Then
'RaiseEvent: Call Event Subroutine
RaiseEvent AfterUpdat("RaiseEvent MESSAGE TEXT")
ElseIf Left(opt, 1) = "=" Then
'Expression
opt = Mid(opt, 2)
vx = Eval(opt)
Else
'Run Macro
DoCmd.RunMacro opt
End If
AfterUpdateMac_Exit:
Exit Sub
AfterUpdateMac_Err:
MsgBox Err & ": " & Err.Description, , "AfterUpdateMac_Err()"
Resume AfterUpdateMac_Exit
End Sub
</pre>
<h3 style="text-align: left;">Review of the Class Module Code.</h3><p>In the Global declaration area, an <b>Event</b> is defined with the name <b>AfterUpdat(ByVal txt As String). </b>The letter <b>e</b> in AfterUpdate is omitted intentionally. Another Property <b>After_Update As String</b> is also declared for inserting the Event running option, analogous to the AfterUpdate Property of the TextBox.</p>
<p>Then the <b>Get/Let</b> Property Procedures to get the selected option from the Form and pass it on to the <b>AfterUpdateMac() </b>to execute the Option. The AfterUpdateMac() is trying to Mimic the action of the <b>AfterUpdateMacro</b> hidden Property/Procedure of the TextBox we saw in the Object Browser Image given at the top of this Page.</p><p>The options that we can normally insert into an Event Property are given in a ListBox. </p><p></p><ol style="text-align: left;"><li>A Macro with the name <b>Macro1</b></li><li>The Function/Expression <b>=DisplayText()</b> to call the Function in the Standard Module </li><li>The String <b>[Event Procedure]</b> to call the declared Event Subroutine in the Form Module.</li><li>Other built-in Functions like MsgBox(), and InputBox().</li></ol><p><span face=""Google Sans", "Helvetica Neue", sans-serif" style="background-color: white; color: #1f1f1f; font-size: 16px;">By clicking on an option, you effectively insert it into the After_Update Property and activate a process within the Class Module that evaluates and executes the chosen option.</span></p><h3 style="text-align: left;">The AfterUpdateMac() Subroutine VBA Code.</h3><p>Let us have a closer look at the AfterUpdateMac() Subroutine Code.</p>
<pre>Private Sub AfterUpdateMac()
'Evaluate the given option
'and Run the Event action
Dim opt As String
Dim vx As Variant
On Error GoTo AfterUpdateMac_Err
opt = Nz(After_Update, "")
If Len(opt) = 0 Then
Exit Sub
ElseIf UCase(opt) = "[EVENT PROCEDURE]" Then
'RaiseEvent: Call Event Subroutine
RaiseEvent AfterUpdat("RaiseEvent MESSAGE TEXT")
ElseIf Left(opt, 1) = "=" Then
'Expression
opt = Mid(opt, 2)
vx = Eval(opt)
Else
'Run Macro
DoCmd.RunMacro opt
End If
AfterUpdateMac_Exit:
Exit Sub
AfterUpdateMac_Err:
MsgBox Err & ": " & Err.Description, , "AfterUpdateMac_Err()"
Resume AfterUpdateMac_Exit
End Sub<span style="font-family: Times New Roman;"><span style="white-space: normal;">
</span></span></pre>
<p>Within the subroutine, two local variables, <b>opt</b> and <b>vx</b> are declared. The selected option, inserted into the Text2 TextBox Control on the form, is assigned to the After_Update Property declared in the global area of the Class Module. The statement <b>opt = Nz(After_Update, "")</b> checks whether the After_Update Property contains any value. If it does not, the subroutine gracefully exits.</p><p>Should the received value be the text <b>[Event Procedure]</b>, the Subroutine proceeds to trigger the user-defined Event <b>AfterUpdat()</b> with some sample text parameter. This event is captured in the Form Module, subsequently displaying the parameter text in a MessageBox.</p>
<p>In the scenario where the <b>opt</b> variable contains an expression (<b>Note:</b> an expression commences with an <b>'='</b> symbol), a check is made for the presence of the equal symbol as the first character. If detected, it is presumed to be a function or a valid expression. The expression is then passed to the <b>Eval()</b> function after removing the <b>'='</b> symbol.</p>
<p>If the value received in the After_Update Property doesn't satisfy any of the aforementioned criteria, it is assumed to be a macro name. Subsequently, the macro is executed using the <b>DoCmd.RunMacro</b> command.</p><p>If any Error is encountered then it shows an Error Message and will exit from the Program.</p><h3 style="text-align: left;">The Form Module Code is listed below:</h3>
<pre style="text-align: left;">Option Compare Database
Option Explicit
Private WithEvents C1 As ClsAfterUpdateMacro
Private Sub Form_Load()
Set C1 = New ClsAfterUpdateMacro
End Sub
Private Sub cmdClose_Click()
DoCmd.Close
End Sub
Private Sub List0_Click()
Me.Text2.Value = List0
C1.OnAfterUpdate = Me![Text2]
End Sub
'UserDefined Event Message
Private Sub C1_AfterUpdat(ByVal otxt As String)
MsgBox otxt
End Sub
'This is the Normal Procedure
'Executed by Access System.
Private Sub Text27_AfterUpdate()
MsgBox "AfterUpdate Event Subroutine Fired."
End Sub
</pre>
<h3 style="text-align: left;">The Form Module Code Review.</h3><p>The ClsAfterUpdateMacro Class is declared with the Object name <b>C1</b> in the Global declaration area.</p><p>In the Form_Load() Event Subroutine the C1 Object is instantiated and loaded into memory. </p><p>In the List0_Click() Event Procedure the selected ListBox option is assigned to the <b>After_Update</b> Property through the <b>C1.OnAfterUpdate</b> Property Procedure.</p><p>The following code segment represents the subsequent subroutine that captures the AfterUpdateMac() Event when triggered from the <a href="https://www.msaccesstips.com/2018/10/ms-access-class-module-and-vba.html" target="_blank">Class Module</a> using the <b>RaiseEvent</b> action, specifically when the option selected from the ListBox is <b>[Event Procedure]</b>.</p>
<pre style="text-align: left;">'UserDefined Event Subroutine
Private Sub C1_AfterUpdat(ByVal otxt As String)
MsgBox otxt
End Sub<span style="font-family: Times New Roman;"><span style="white-space: normal;">
</span></span></pre><p>This is used for the second part of this experiment for the Normal Form Module Coding and Event firing from the Access System.</p><pre>'This is the Normal Procedure
'Executed by Access System.
Private Sub Text27_AfterUpdate()
MsgBox "AfterUpdate Event Subroutine Fired."
End Sub
</pre>
<h3 style="text-align: left;">The Second Part of the Form.</h3><p>In the Second part of the Form, the same set of Options is typed in a Label Control so that when you are on the Form Design View you can highlight and Copy the required option from the Label Control and Paste it into the <b>AfterUpdate Event Property</b> of the Text27 TextBox Control. This is easier than typing them correctly in the <b>AfterUpdate Property</b>, without errors.</p><p>Then save the Form and open it in Normal View.</p><p>Type at least one character in the TextBox and Press Enter-Key to fire the AfterUpdate Event for the option inserted into the Property of TextBox Text27.</p><p>The AfterUpdate() Event Fires at this point, depending on the option given in the Property, and executes the action as we saw it in our own earlier experiment.</p><h3 style="text-align: left;">Event Properties and their related Macros.</h3>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg6qg9YrQL5VenHWPCkw0KQ4BZaGigdOxDOBdJhVsiuTzJsX3-lNBM5VasEA9Lhx_PQxXI6xWOwoU526ZYopxE-uaKVPDpJNwBewfzAh4maycehhLeh0P5aAZbJuB_DeF_PGuZR6wLHWyP5FE8lc7Hq4308979Pl91hYhwQmcj3oc8lglKar0iUl8S4gmCC/s600/ClassCommandButton.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="539" data-original-width="600" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg6qg9YrQL5VenHWPCkw0KQ4BZaGigdOxDOBdJhVsiuTzJsX3-lNBM5VasEA9Lhx_PQxXI6xWOwoU526ZYopxE-uaKVPDpJNwBewfzAh4maycehhLeh0P5aAZbJuB_DeF_PGuZR6wLHWyP5FE8lc7Hq4308979Pl91hYhwQmcj3oc8lglKar0iUl8S4gmCC/s320/ClassCommandButton.png" width="320" /></a></div>
<p>As depicted in the Object Browser image provided above, the left panel highlights the CommandButton Class selection, while the right panel showcases its event properties, including the concealed ones. Notably, all event option-setting properties are presented with an "On" prefix appended to the event name, such as OnClick. Beneath each of these properties, there exists another property or procedure bearing the same name, suffixed with the term "Macro," such as OnClickMacro. This signifies the internal program responsible for detecting the option entered within the OnClick property and subsequently executing the specified option. </p><p>It's highly plausible that these additional procedures attached to the event properties, matching their names with the "Macro" suffix, encapsulate a code structure similar to the one we experimented with in the initial section. The presence of analogous naming conventions suggests a consistent pattern in the internal program's structure, further affirming the feasibility of such a code structure.</p><p>The Demo Database with the above VBA Code and Form is attached for running your own experiments.</p>
<!--Download Link /downloads/2024/01/0_RaiseEvent2.zip-->
<div class="separator" style="clear: both; text-align: center;">
<a href="https://drive.google.com/file/d/1mGaZkQgcxcytFSm51zGQZpzfFU5le1Jk/view?usp=sharing">
<img border="0" data-original-height="29" data-original-width="22" height="29" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFE4uDLH9dk3S6pEcfTkT6GW6r11XqBI4HQnLnhQFZMKFeeQt5ABV_NqvLHM68J7RacISsvbUEl0PlAq6V7i0t0mQF3iesrBuqGuvrf5CJV72OzrGZ7rdMxkxzw8FYlz-qniNySoqtqUN4/s320/zip.gif" width="22" />0_RaiseEvent2.zip</a>
</div>
<!--Download Link /downloads/2024/01/0_RaiseEvent2.zip-->
<br />
<ol>
<li><a href="https://www.msaccesstips.com/2023/04/reusing-form-module-vba-code-for-new.html" target="_Blank">Re-using Form Module VBA Code for New Projects.</a></li>
<li><a href="https://www.msaccesstips.com/2023/04/streamlining-form-module-code-part-two.html" target="_Blank">Streamlining Form Module Code Part-Two</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/05/streamlining-form-module-code-part-three.html" target="_Blank">Streamlining Form Module Code Part-Three</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/06/streamlining-form-module-code-part-four.html" target="_Blank">Streamlining Form Module Code Part-Four</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/06/streamlining-form-module-code-part-five.html" target="_Blank">Streamlining Form Module Code Part-Five</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-six.html" target="_Blank">Streamlining Form Module Code Part-Six</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-seven.html" target="_Blank">Streamlining Form Module Code Part-Seven</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-eight.html" target="_Blank">Streamlining Form Module Code Part-Eight</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part-nine.html" target="_Blank">Streamlining Form Module Code Part-Nine</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part-ten.html" target="_Blank">Streamlining Form Module Code Part-Ten</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part.html" target="_Blank">Streamlining Form Module Code Part-Eleven</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-report-module-code-in.html" target="_Blank">Streamlining Report Module Code in Class Module</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-report-module-in-class.html" target="_Blank">Streamlining Report Module Code in Class Module-2</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-form-module-code-part-14.html" target="_Blank">Streamlining Form Module Code Part-14:All Controls</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-custom-made-form-wizard.html" target="_Blank">Streamlining Custom Made Form Wizard-15</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-form-vba-custom-report.html" target="_Blank">Streamlining Custom Report Wizard-16</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-form-vba-external-files.html" target="_Blank">Streamlining Form VBA External File Browser-17</a></li>
<li><a href="https://www.msaccesstips.com/2023/11/streamlining-event-procedures.html" target="_Blank">Streamlining Event Procedures of 3D TextWizard-18</a></li>
<li><a href="https://www.msaccesstips.com/2023/11/streamlining-event-procedures-rgbcolor.html" target="_Blank">Streamlining Event Procedures RGB Color Wizard-19</a></li>
<li><a href="https://www.msaccesstips.com/2023/12/streamlining-numbers-to-words-converter.html" target="_Blank">Streamlining Event Procedures Number To Words-20</a></li>
<li><a href="https://www.msaccesstips.com/2024/01/streamlining-form-module-code.html" target="_Blank">Streamlining VBA Code Presentation at Access User Groups.org (Europe)</a></li>
</ol>
a.p.r. pillaihttp://www.blogger.com/profile/02627590161919120214noreply@blogger.com0tag:blogger.com,1999:blog-7457417942281874810.post-29453687694141462482024-01-13T23:41:00.017+05:302024-02-15T11:49:36.057+05:30Streamlining Form Module Code Presentation<h3 style="text-align: left;">Streamlining Form Module Code in Standalone Class Module.</h3><div>On January 3, 2024, I presented a concise overview of the intricate topic: "Streamlining Form Module Code in Standalone Class Module" for the Access User Groups (Europe) Chapter. <a href="https://www.accessusergroups.org/europe/">https://accessusergroups.org/europe/</a> . </div>
<div class="separator" style="clear: both;"><a href="https://youtu.be/AjvjN3h1ipY" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="768" data-original-width="1358" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiiCiMJMHkFvrA1SYMmENzL2-tXLy5HcUotXZYzirfF2KxiYi19NO73DkFYQLOzxTcYc0s-4ck66fT6nyxN2OL2lIDiuGE6ZOeP0dt15CfaDpJO5PIwAjPikmGETyA8fwYZfmVBqZtnwDSJR5PBHov1kUNNb5nCwXUD7ZmNGMn4fORCqMruTQG-zYgYUNtS/s320/AEUMeeting.png" width="320" /></a></div>
<center><a href="https://youtu.be/AjvjN3h1ipY">https://youtu.be/AjvjN3h1ipY</a></center>
<p>The YouTube subtitles are available in eleven languages: Danish, Dutch, English, French, German, Italian, Spanish, Hindi, Malayalam, Bangla (India), and English (Autogenerated). Choose your preferred language subtitles from the Settings Menu. Experience the video, packed with technical details, all conveniently in one place. <a href="https://youtu.be/AjvjN3h1ipY" target="_blank">YouTube.com </a></p><p>PS: The Subtitle Translation may not be accurate.</p>
<!--Google In-Article Ads start-->
<script async="" src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle" data-ad-client="ca-pub-2376623373770548" data-ad-format="fluid" data-ad-layout="in-article" data-ad-slot="6147807460" style="display: block; text-align: center;"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
<!--Google In-Article Ads END-->
<h3>Download Demo Database</h3>
<!--Download Link /downloads/2023/07/Streamline7.zip-->
<div class="separator" style="clear: both; text-align: center;">
<a href="https://drive.google.com/file/d/1aKelFg1y4oSKdVqmFQk-91yQM37yCpTL/view?usp=sharing">
<img border="0" data-original-height="29" data-original-width="22" height="29" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFE4uDLH9dk3S6pEcfTkT6GW6r11XqBI4HQnLnhQFZMKFeeQt5ABV_NqvLHM68J7RacISsvbUEl0PlAq6V7i0t0mQF3iesrBuqGuvrf5CJV72OzrGZ7rdMxkxzw8FYlz-qniNySoqtqUN4/s320/zip.gif" width="22" />Streamline7.zip</a>
</div>
<h3>Streamlining Form Module Code in Standalone Class Module.</h3>
<ol>
<li><a href="https://www.msaccesstips.com/2023/04/reusing-form-module-vba-code-for-new.html" target="_Blank">Re-using Form Module VBA Code for New Projects.</a></li>
<li><a href="https://www.msaccesstips.com/2023/04/streamlining-form-module-code-part-two.html" target="_Blank">Streamlining Form Module Code Part-Two</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/05/streamlining-form-module-code-part-three.html" target="_Blank">Streamlining Form Module Code Part-Three</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/06/streamlining-form-module-code-part-four.html" target="_Blank">Streamlining Form Module Code Part-Four</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/06/streamlining-form-module-code-part-five.html" target="_Blank">Streamlining Form Module Code Part-Five</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-six.html" target="_Blank">Streamlining Form Module Code Part-Six</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-seven.html" target="_Blank">Streamlining Form Module Code Part-Seven</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-eight.html" target="_Blank">Streamlining Form Module Code Part-Eight</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part-nine.html" target="_Blank">Streamlining Form Module Code Part-Nine</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part-ten.html" target="_Blank">Streamlining Form Module Code Part-Ten</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part.html" target="_Blank">Streamlining Form Module Code Part-Eleven</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-report-module-code-in.html" target="_Blank">Streamlining Report Module Code in Class Module-12</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-report-module-in-class.html" target="_Blank">Streamlining Report Module Code in Class Module-2-13</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-form-module-code-part-14.html" target="_Blank">Streamlining Form Module Code Part-14:All Controls</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-custom-made-form-wizard.html" target="_Blank">Streamlining Custom Made Form Wizard-15</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-form-vba-custom-report.html" target="_Blank">Streamlining Custom Report Wizard-16</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-form-vba-external-files.html" target="_Blank">Streamlining Form VBA External File Browser-17</a></li>
<li><a href="https://www.msaccesstips.com/2023/11/streamlining-event-procedures.html" target="_Blank">Streamlining Event Procedures of 3D TextWizard-18</a></li>
<li><a href="https://www.msaccesstips.com/2023/11/streamlining-event-procedures-rgbcolor.html" target="_Blank">Streamlining Event Procedures RGB Color Wizard-19</a></li>
<li><a href="https://www.msaccesstips.com/2023/12/streamlining-numbers-to-words-converter.html" target="_Blank">Streamlining Event Procedures Number To Words-20</a></li>
</ol>
a.p.r. pillaihttp://www.blogger.com/profile/02627590161919120214noreply@blogger.com2tag:blogger.com,1999:blog-7457417942281874810.post-13614806485167487832023-12-03T12:31:00.007+05:302024-02-14T21:38:11.468+05:30Streamlining Numbers to Words Converter<h3 style="text-align: left;"> Numbers to Words Converter.</h3><p>While converting numbers to words is straightforward in Microsoft Word <a href="https://www.msaccesstips.com/2007/10/ms-access-and-mail-merge-3.html" target="_blank">Mail Merge</a>, such functionality is not readily available in MS Access. To address this limitation, we have developed a versatile function that can be employed wherever needed—whether within a <a href="https://www.msaccesstips.com/2008/01/progress-bar-on-form.html" target="_blank">Form</a> TextBox Control, on Report Summary Totals, in Printed Invoices, or any other desired location. Utilize the CardText() function by calling it with the desired number for conversion and displaying the result wherever necessary. It's as simple as that.</p>
<!--Google In-Article Ads start-->
<script async="" src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle" data-ad-client="ca-pub-2376623373770548" data-ad-format="fluid" data-ad-layout="in-article" data-ad-slot="6147807460" style="display: block; text-align: center;"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
<!--Google In-Article Ads END-->
<p>The Main Demo Form Image is given below:</p>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjS5HD4XHDVeHH3CpWg6EGAoAVMHelDrbjJV9Wsl-UjF14bDpnb15S2sPtZc4kjwq2aOZaMb3k61_-opzwaAciCQEDEq1XKPq7pP7NEODQ8cG1lT-BzRAyfE4qmJg7qX8VPG72Vqh37Kk_mVm2Jg7amoDmb0bKOVcRSuGCOmrdrZpFYbaA23EyXWUjfpkbe/s946/NumberToWords4.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="591" data-original-width="946" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjS5HD4XHDVeHH3CpWg6EGAoAVMHelDrbjJV9Wsl-UjF14bDpnb15S2sPtZc4kjwq2aOZaMb3k61_-opzwaAciCQEDEq1XKPq7pP7NEODQ8cG1lT-BzRAyfE4qmJg7qX8VPG72Vqh37Kk_mVm2Jg7amoDmb0bKOVcRSuGCOmrdrZpFYbaA23EyXWUjfpkbe/s320/NumberToWords4.png" width="320" /></a></div>
<p>The Demo Form in Design View.</p>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEihpB4dKXCbO7Z9ZTNvZfJFUlokf2hG9x2E3DEOy9kYdZ1zGdYRQumO110z3B54uQO8lqb_4QlSu-5eF5Qf9Srw_DFAO3Two6jUpwqsbcO2nRuMdRGE5bmvzppVo4gGWQ_iglHSR_EKvHCUOcrXkrBp9kpTwU1IUCA5IcS0DQoArq1IknFNQUycijpj_HKF/s974/CardTextDesign2.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="761" data-original-width="974" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEihpB4dKXCbO7Z9ZTNvZfJFUlokf2hG9x2E3DEOy9kYdZ1zGdYRQumO110z3B54uQO8lqb_4QlSu-5eF5Qf9Srw_DFAO3Two6jUpwqsbcO2nRuMdRGE5bmvzppVo4gGWQ_iglHSR_EKvHCUOcrXkrBp9kpTwU1IUCA5IcS0DQoArq1IknFNQUycijpj_HKF/s320/CardTextDesign2.png" width="320" /></a></div>
<p>Sample Report Image. The Group-level Subtotal Amount is printed in Words.</p>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg3wzqtsWvVtagO8UEHYrMYb8GdD7-vO891KlrvquzBMqu0L5EcKet56CX0GZVUcDQtm4uiAVUsm42WkM00PhTaTevalbm3OTFgXLOv4SmuDWQ8-mWwZGAEaJJAcd6KAXOVMG5JfOeNoqXg8RWm3OQt1xfj2VtGL2_9TA1Zj2yN8B6VK9KrgVDFXKAAsGS5/s1229/CardText_Report.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="803" data-original-width="1229" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg3wzqtsWvVtagO8UEHYrMYb8GdD7-vO891KlrvquzBMqu0L5EcKet56CX0GZVUcDQtm4uiAVUsm42WkM00PhTaTevalbm3OTFgXLOv4SmuDWQ8-mWwZGAEaJJAcd6KAXOVMG5JfOeNoqXg8RWm3OQt1xfj2VtGL2_9TA1Zj2yN8B6VK9KrgVDFXKAAsGS5/s320/CardText_Report.png" width="320" /></a></div>
<h3 style="text-align: left;">The CardText() Function VBA Code Listing.</h3>
<pre>Option Compare Database
Option Explicit
Public Function CardText(ByVal inNumber As Double, Optional ByVal precision As Integer = 2) As String
'------------------------------------------------------------------------
'Author : a.p.r. pillai
'Date : December 2008/2023
'URL : www.msaccesstips.com
'Version: 2.0
'All Rights Reserved by www.msaccesstips.com
'------------------------------------------------------------------------
Dim ctu, ctt, bmth
Dim strNum As String, j As Integer, k As Integer, fmt As String
Dim h As Integer, xten As Integer, yten As Integer
Dim cardseg(1 To 4) As String, txt As String, d As String, txt2 As String
Dim locn As Integer, xfract As String, xhundred As String
Dim xctu As String, xctt As String, xbmth As String
On Error GoTo CardText_Err
strNum = Trim(Str(inNumber))
locn = InStr(1, strNum, ".")
'Check Decimal Places and rounding
If locn > 0 Then
xfract = Mid(strNum, locn + 1)
strNum = Left(strNum, locn - 1)
If precision > 0 Then
If Len(xfract) < precision Then
xfract = xfract & String(precision - Len(xfract), "0")
ElseIf Len(xfract) > precision Then
xfract = Format(Int(Val(Left(xfract, precision + 1)) / 10 + 0.5), String(precision, "0"))
End If
xfract = IIf(Val(xfract) > 0, xfract & "/" & 10 ^ precision, "")
Else
strNum = Val(strNum) + Int(Val("." & xfract) + 0.5)
xfract = ""
End If
End If
h = Len(strNum)
If h > 12 Then
'if more than 12 digits take only 12 (max. 999 Billion)
'extra value will get truncated from left.
strNum = Right(strNum, 12)
Else
strNum = String(12 - h, "0") & strNum
End If
GoSub initSection
txt2 = ""
For j = 1 To 4
If Val(cardseg(j)) = 0 Then
GoTo NextStep
End If
txt = ""
For k = 3 To 1 Step -1
Select Case k
Case 3
xten = Val(Mid(cardseg(j), k - 1, 1))
If xten = 1 Then
txt = ctu(10 + Val(Mid(cardseg(j), k, 1)))
Else
txt = ctt(xten) & ctu(Val(Mid(cardseg(j), k, 1)))
End If
Case 1
yten = Val(Mid(cardseg(j), k, 1))
xhundred = ctu(yten) & IIf(yten > 0, bmth(1), "") & txt
Select Case j
Case 2
d = bmth(2)
Case 3
d = bmth(3)
Case 4
d = bmth(4)
End Select
txt2 = xhundred & d & txt2
End Select
Next
NextStep:
Next
If Len(txt2) = 0 And Len(xfract) > 0 Then
txt2 = xfract & " only. "
ElseIf Len(txt2) = 0 And Len(xfract) = 0 Then
txt2 = ""
Else
txt2 = txt2 & IIf(Len(xfract) > 0, " and " & xfract, "") & " only."
End If
CardText = txt2
CardText_Exit:
Exit Function
initSection:
'Units to 19
xctu = ", One, Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Eleven, Twelve,"
xctu = xctu & " Thirteen, Fourteen, Fifteen, Sixteen, Seventeen, Eighteen, Nineteen"
ctu = Split(xctu, ",")
'Tens
xctt = ", Ten, Twenty, Thirty, Fourty, Fifty, Sixty, Seventy, Eighty, Ninety"
ctt = Split(xctt, ",")
xbmth = ", Hundred, Thousand, Million, Billion"
bmth = Split(xbmth, ",")
k = 4
For j = 1 To 10 Step 3
cardseg(k) = Mid(strNum, j, 3)
k = k - 1
Next
Return
CardText_Err:
CardText = ""
MsgBox Err.Description, , "CardText()"
Resume CardText_Exit
End Function
</pre>
<p>The CardText() function, first written and <a href="https://www.msaccesstips.com/2009/01/cardinal-text-format-in-access.html%20%20" target="_blank">published in January 2009</a>, accepts two parameters. The initial parameter should be either a Decimal Number or a valid expression that resolves to a Decimal Number. The second parameter determines the precision of decimal digits during the rounding process of the fractional part of the value. Notably, the second parameter is optional and already seeded with a default value of 2. The precision setting can be modified when invoking the CardText() function to align with specific requirements.</p><h3 style="text-align: left;">How to Run the Function on the Form.</h3><p>Upon entering a numeric value in the first TextBox, you can execute the CardText() function by pressing the Enter key or by clicking on the Show <a href="https://www.msaccesstips.com/2006/09/command-button-animation.html" target="_blank">Command Button</a>. This operation converts the entered number into words and subsequently presents it in the Label Control situated below.</p><p>In addition to inputting a straightforward numeric value, you have the flexibility to compose an expression for calculation purposes. The CardText() function processes this expression, and the resulting value is transformed into words for display. A sample expression is demonstrated below:</p>
<pre>((625*25+0.75)*0.80)
</pre>
<p>There are two <a href="https://www.msaccesstips.com/2008/04/colorfull-command-buttons.html" target="_blank">Command Buttons</a>. One to run the Function and the other to close the Form. The Label Control displays the entered Number in Words. It is a simple interface to enter the Function Parameter values and Call the Function.</p><p>To illustrate the straightforward application of the CardText() function, two TextBox <a href="https://www.msaccesstips.com/2007/08/edit-data-in-zoom-in-control.html " target="_blank">Controls</a> have been incorporated beneath the Close Command Button. Specifically named "Calc," the first TextBox is unbound. Users can input a numeric value into this TextBox. The adjacent TextBox on the right side features an expression: "=CardText([Calc])." This expression employs the CardText() function to convert the value entered into the "Calc" TextBox, presenting it in words within the same TextBox on the right side. This intuitive setup demonstrates the seamless integration of the CardText() function for converting numeric inputs into their textual representations.</p><h3 style="text-align: left;">Preparing for the Streamlining VBA Code Procedure.</h3><p>Only one TextBox has the AfterUpdate Event, when fired it simply calls the <a href="https://www.msaccesstips.com/2008/04/transparent-command-button.html" target="_blank">Command Button</a> Click Event to take over the task of validating the value entered into the TextBox and to run the CardText() Function. </p><p>Given that the form boasts a straightforward interface with minimal events to manage, the streamlining procedure allows for handling these uncomplicated event procedures within the intermediary class module. Consequently, there is no imperative need for wrapper classes. This simplifies the structure and enhances efficiency by consolidating the handling of basic events directly within the intermediary class module, eliminating the necessity for additional layers of abstraction. This approach streamlines the code and contributes to a more concise and manageable implementation.</p><p>We need only one Instance of the TextBox Control and Instances of two Command Buttons with different Names in the Intermediary Class Module. All three Object Instances are declared with the keyword <a href="https://www.msaccesstips.com/2019/05/withevents-in-class-module-and-data.html" target="_blank">WithEvents</a> so that we can write their Event Procedures in the Card_ObjInit() Class.</p>
<p>In this particular scenario, opting for object-level Wrapper Classes might entail a more extensive VBA codebase to handle events. Following the prescribed guidelines would necessitate Wrapper Classes, particularly for the Command Buttons. Additionally, the use of a Collection object becomes imperative to keep the instances of wrapper classes alive in memory, ensuring the capture and execution of event procedures.</p>
<p>However, considering the simplicity of the form interface and the limited number of events to manage, this approach may introduce unnecessary resource overhead. If the streamlined solution in the intermediary class module proves sufficient for handling these events, it can lead to a more resource-efficient implementation. This decision should be based on a balance between adhering to coding standards and optimizing resource usage for the specific requirements of the form.</p><p>Therefore, in this case, we decided to go along with managing everything in the <b>Card_ObjInit</b> Class Module itself.</p><h3 style="text-align: left;">The Card_ObjInit Wrapper Class.</h3><p>The Card_ObjInit Class, this is a Wrapper Class too, VBA Code is listed below.</p>
<pre style="text-align: left;">Option Compare Database
Option Explicit
Private WithEvents txt As Access.TextBox
Private WithEvents cmdS As Access.CommandButton
Private WithEvents cmdE As Access.CommandButton
Private frm As Access.Form
Public Property Get m_Frm() As Access.Form
Set m_Frm = frm
End Property
Public Property Set m_Frm(ByRef vfrm As Access.Form)
Set frm = vfrm
Call Class_Init
End Property
Private Sub Class_Init()
Const EP = "[Event Procedure]"
Set cmdS = frm.cmdResult
cmdS.OnClick = EP
Set cmdE = frm.cmdClose
cmdE.OnClick = EP
Set txt = frm.Amt
txt.AfterUpdate = EP
End Sub
Private Sub cmdE_Click()
If MsgBox("Close the Form? ", vbYesNo + vbQuestion, "CmdClose_Click()") = vbYes Then
DoCmd.Close acForm, frm.Name
End If
End Sub
Private Sub txt_AfterUpdate()
Call cmdS_Click
End Sub
Private Sub cmdS_Click()
Dim tx As Variant
Dim t As Variant
Dim ctxt As String
Dim Rounding As Integer
Dim dblResult As Double
Dim msg As String
Dim fmt As String
On Error GoTo cmdResult_Click_Err
tx = frm!Amt
t = Replace(tx, ",", "")
tx = t
Rounding = frm!RoundTo
fmt = "#,##0." & String(Rounding, "0")
dblResult = Eval(tx)
If dblResult > (10 ^ 12 - 1) Then
msg = "Value: " & dblResult & " Exceeds permissible limit."
MsgBox msg, , "cmdResult_Click()"
Else
frm!Amt = Format(dblResult, fmt)
ctxt = CardText(dblResult, Rounding)
frm!Result.Caption = ctxt
End If
cmdResult_Click_Exit:
Exit Sub
cmdResult_Click_Err:
MsgBox Err & " : " & Err.Description, , "cmdResult_Click()"
Resume cmdResult_Click_Exit
End Sub
</pre>
<h3 style="text-align: left;">The Intermediary Class Module.</h3><p>At the global declaration area, the TextBox instance is defined with the object name <b>txt</b>, and two Command Button Control instances, <b>cmdS,</b> and <b>cmdE,</b> are also declared. Each of these instances is qualified with the 'WithEvents' keyword, empowering them to capture events triggered on the form.</p><p>At the onset of the <b>Class_Init()</b> Subroutine, the <b>cmdS</b> Command Button object, labeled <b>Show</b>, is linked to the <b>cmdResult</b> Command Button through the assignment. Simultaneously, the <b>cmdE</b> Command Button object is associated with the <b>cmdClose</b> Command Button. Both of these Command Button objects have their <b>OnClick()</b> events enabled. Additionally, the <b>txt</b> object is connected to the <b>Amt</b> TextBox, and its <b>AfterUpdate</b> event is activated.</p>
<p>The <b>cmdE</b> Click Event Subroutine Closes the Main Form.</p>
<p>In the <b>cmdS Click</b> Event Procedure, the entered value is validated, and the <b>CardText()</b> Function is invoked to convert the number to words. The resulting output is then displayed in the <a href="https://www.msaccesstips.com/2010/05/label-animation-zoom-in-style.html" target="_blank">Label Control</a> on the Form. If the input is an expression rather than a direct number, it is first evaluated to a number before calling the <b>CardText()</b> Function.</p>
<p>The Afterupdate() Event of the <b>Amt</b> TextBox calls the cmdS_Click() Event Subroutine to execute the validation check and to call the CardText() Public Function.</p>
<h3>Demo Database Download Link. </h3>
<!--Download Link /downloads/2023/12/CardText V2.zip-->
<div class="separator" style="clear: both; text-align: center;">
<a href="https://drive.google.com/file/d/1jZZVbvo41fJETovI_b0H7xjcsSk0maSW/view?usp=sharing">
<img border="0" data-original-height="29" data-original-width="22" height="29" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFE4uDLH9dk3S6pEcfTkT6GW6r11XqBI4HQnLnhQFZMKFeeQt5ABV_NqvLHM68J7RacISsvbUEl0PlAq6V7i0t0mQF3iesrBuqGuvrf5CJV72OzrGZ7rdMxkxzw8FYlz-qniNySoqtqUN4/s320/zip.gif" width="22" />CardText V2.zip</a>
</div>
<!--Download Link /downloads/2023/12/CardText V2.zip-->
<br />
<h3>Streamlining Form Module Code in Standalone Class Module.</h3>
<ol>
<li><a href="https://www.msaccesstips.com/2023/04/reusing-form-module-vba-code-for-new.html" target="_Blank">Re-using Form Module VBA Code for New Projects.</a></li>
<li><a href="https://www.msaccesstips.com/2023/04/streamlining-form-module-code-part-two.html" target="_Blank">Streamlining Form Module Code Part-Two</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/05/streamlining-form-module-code-part-three.html" target="_Blank">Streamlining Form Module Code Part-Three</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/06/streamlining-form-module-code-part-four.html" target="_Blank">Streamlining Form Module Code Part-Four</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/06/streamlining-form-module-code-part-five.html" target="_Blank">Streamlining Form Module Code Part-Five</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-six.html" target="_Blank">Streamlining Form Module Code Part-Six</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-seven.html" target="_Blank">Streamlining Form Module Code Part-Seven</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-eight.html" target="_Blank">Streamlining Form Module Code Part-Eight</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part-nine.html" target="_Blank">Streamlining Form Module Code Part-Nine</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part-ten.html" target="_Blank">Streamlining Form Module Code Part-Ten</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part.html" target="_Blank">Streamlining Form Module Code Part-Eleven</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-report-module-code-in.html" target="_Blank">Streamlining Report Module Code in Class Module-12</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-report-module-in-class.html" target="_Blank">Streamlining Report Module Code in Class Module-2-13</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-form-module-code-part-14.html" target="_Blank">Streamlining Form Module Code Part-14:All Controls</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-custom-made-form-wizard.html" target="_Blank">Streamlining Custom Made Form Wizard-15</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-form-vba-custom-report.html" target="_Blank">Streamlining Custom Report Wizard-16</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-form-vba-external-files.html" target="_Blank">Streamlining Form VBA External File Browser-17</a></li>
<li><a href="https://www.msaccesstips.com/2023/11/streamlining-event-procedures.html" target="_Blank">Streamlining Event Procedures of 3D TextWizard-18</a></li>
<li><a href="https://www.msaccesstips.com/2023/11/streamlining-event-procedures-rgbcolor.html" target="_Blank">Streamlining Event Procedures RGB Color Wizard-19</a></li>
<li><a href="https://www.msaccesstips.com/2023/12/streamlining-numbers-to-words-converter.html" target="_Blank">Streamlining Event Procedures Number To Words-20</a></li>
</ol>
a.p.r. pillaihttp://www.blogger.com/profile/02627590161919120214noreply@blogger.com0tag:blogger.com,1999:blog-7457417942281874810.post-90193730868001107692023-11-22T17:51:00.002+05:302024-01-07T23:13:29.500+05:30Streamlining Event Procedures RGBColor Wizard<h3 style="text-align: left;">RGB Color Wizard.</h3><h3 style="text-align: left;">Create your own RGB Color Palette for Form Design.</h3><p>This is a special episode focusing on streamlining the Form Module Code. For our RGB Color Wizard, we utilize an ActiveX ScrollBar Control. It's important to note that you cannot instantiate ActiveX controls such as Scrollbars, Sliders, <a href="https://www.msaccesstips.com/2020/09/microsoft-treeview-control-tutorial.html" target="_blank">TreeView</a>, and <a href="https://www.msaccesstips.com/2021/11/activex-listview-control-tutorial-01.html" target="_blank">ListView</a> Controls in the standalone class module. Therefore, all event procedures for these controls must be written in the Form Module exclusively.</p><p>We employ three ScrollBar Controls dedicated to Red, Green, and Blue color code values. Each control spans from 0 to 255, representing the RGB color settings. Collectively, these values are utilized to generate a wide range of colors in the Color Wizard, logically providing the capability to create up to 16.7 million colors.</p>
<!-- Google In-Article Ads start -->
<script async="" src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle" data-ad-client="ca-pub-2376623373770548" data-ad-format="fluid" data-ad-layout="in-article" data-ad-slot="6147807460" style="display: block; text-align: center;"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
<!-- Google In-Article Ads END -->
<h3 style="text-align: left;">The RGB Color Wizard Image is given below.</h3><p>Let us take a look at the User Interface of the Color Wizard.</p>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgE6T91r6BFz_F6P-P4savM15hK3hWMR9y5ek2pyIrAq9d6lFDtyqfDUd97h4Lkh4REsD0_sLw5pyKal-5TerI22ZQR5k_IXPL0UCBwyjv9CPi6-Nl0p4g8gGjRBq8rICJEWN8NxbyP7YAB_vQZun6Mrwdsf4Cd8LWwKGoxClckzvlGvBP18T9e4ZjZM5sA/s765/ColorWozard1.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="Color Wizard" border="0" data-original-height="765" data-original-width="631" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgE6T91r6BFz_F6P-P4savM15hK3hWMR9y5ek2pyIrAq9d6lFDtyqfDUd97h4Lkh4REsD0_sLw5pyKal-5TerI22ZQR5k_IXPL0UCBwyjv9CPi6-Nl0p4g8gGjRBq8rICJEWN8NxbyP7YAB_vQZun6Mrwdsf4Cd8LWwKGoxClckzvlGvBP18T9e4ZjZM5sA/s320/ColorWozard1.png" /></a></div>
<h3 style="text-align: left;">The Wizard Controls.</h3><p>To the left of the scrollbars, three textboxes are positioned. When you adjust the scrollbar slider to the right or left, the selected color number will be displayed in the textbox on the left side. The color range spans from 0 to 255 for red, green, and blue colors. Additionally, you have the option to manually input color numbers for a specific RGB color by typing them into the text boxes.</p><p>To the right of the scroll bars, three label controls display the intensity of the selected color number as you move the scrollbar, resembling a graph chart for Red, Green, and Blue. The RGB() function blends all three selected colors together, resulting in the generation of the RGB color. This final RGB color is prominently displayed in the large rectangle control below.</p><p>Once you are ready to save the generated new Color, click on the RGB Color rectangle Control to select the Color.</p><p>Next, Click on one of the 25 Color Boxes to save the selected Color. The existing Color in the color box will be replaced with the new one. The RGB Color Number is displayed in the RGBColor TextBox Control below the Colorbox Grid.</p><p>You can store and preserve up to 25 colors at any given time. When you wish to apply one of the saved colors to a control, such as a TextBox or Label Control, for properties like ForeColor, BackColor, or BorderColor, simply highlight the number in the RGB Color TextBox and copy-paste the color number into the desired color attribute property.</p><p>To copy the RGB Number to the Clipboard, Click on the Label, with the Caption 'Copy to Clipboard' below the RGB Color TextBox and paste the RGB Color number where you want it.</p><p>The Form Module Code with the ActiveX Control's OnChange() Event Procedures is given below.</p>
<pre style="text-align: left;">Option Compare Database
Option Explicit
Private CWiz As CWiz_ObjInit 'Intermediary Class
Const GraphFactor = (1 / 255) * 1440 'The Color Graph Width is 1 inch
Dim intR As Long, intG As Long, intB As Long
Dim cdb As Database, doc As Document
Private Sub Form_Load()
Set CWiz = New CWiz_ObjInit 'Instantiate CWiz_ObjInit Class
Set CWiz.o_Frm = Me 'Assign the Form object to its Property
End Sub
Private Sub form_Unload(Cancel As Integer)
Set CWiz = Nothing
End Sub
'ActiveX Control
Private Sub Ctl_B_Change() 'Blue Color ScrollBar Control
Set cdb = CurrentDb
Set doc = cdb.Containers("Forms").Documents("ColorPalette")
intB = Ctl_B.Value
With Me
![BN] = intB
.B.Width = GraphFactor * intB
.B.BackColor = RGB(0, 0, intB)
.Color.BackColor = RGB(intR, intG, intB)
.RGBColor = .Color.BackColor
'Save RGBColor and BN TextBoxes contents in Form Custom Properties
doc.Properties("RGBColor").Value = .Color.BackColor
doc.Properties("BN").Value = intB
.Controls("Color").SpecialEffect = 0
.CheckBox.Value = False
End With
End Sub
'ActiveX Control
Private Sub Ctl_G_Change()
Set cdb = CurrentDb
Set doc = cdb.Containers("Forms").Documents("ColorPalette")
intG = Me.Ctl_G.Value
With Me
intG = .Ctl_G.Value
![GN] = intG
.G.Width = GraphFactor * intG
.G.BackColor = RGB(0, intG, 0)
.Color.BackColor = RGB(intR, intG, intB)
.RGBColor = Color.BackColor
doc.Properties("RGBColor").Value = .Color.BackColor
doc.Properties("GN").Value = intG
.Controls("Color").SpecialEffect = 0
.CheckBox.Value = False
End With
End Sub
'ActiveX Control
Private Sub Ctl_R_Change()
Set cdb = CurrentDb
Set doc = cdb.Containers("Forms").Documents("ColorPalette")
intR = Me.Ctl_R.Value
With Me
![RN] = intR
.R.Width = GraphFactor * intR
.R.BackColor = RGB(intR, 0, 0)
.Color.BackColor = RGB(intR, intG, intB)
.RGBColor = Color.BackColor
doc.Properties("RGBColor").Value = .Color.BackColor
doc.Properties("RN").Value = intR
.Controls("Color").SpecialEffect = 0
.CheckBox.Value = False
End With
End Sub
</pre><h3 style="text-align: left;">The ScrollBar Controls.</h3><p>The three ScrollBars, named Ctl_R, Ctl_G, and Ctl_B, correspond to the Red, Green, and Blue colors. When the slider control on each is moved to adjust the color numbers within the range of 0 to 255, the Change Event is triggered. This event records these actions, updating other related controls, such as the <a href="https://www.msaccesstips.com/2008/10/textbox-and-label-inner-margins.html" target="_blank">TextBox</a> content on the left side. Additionally, it increases the width of the label controls, dynamically displaying color variations based on the selected color number range.</p><p>In addition, the Change Event updates the RGB color displayed in the large rectangular Label Control and the RGB color number in the TextBox located below the Colors Grid.</p><p>Since we cannot create an instance of the ScrollBar ActiveX Control in the standalone Class Module we are forced to write the Change Event Subroutines in the Form's Class Module.</p><h3 style="text-align: left;">The ColorWizard and Run-Time Data.</h3><p>Normally, we make changes to a control's ForeColor, BackColor, and Border Colors in the Design View of the Form, update the values in those Properties, and save them when the Form is closed. When we open the Form again it will display the changes as we made in the design view.</p><p>However, with this RGB Color Wizard, we are modifying control properties in the normal Form View Mode. Any alterations made in this mode are temporary and are not automatically saved, and they will be lost when the form is closed. While a logical solution might be to save all settings in a table, allowing them to be loaded the next time the form is opened.</p><p>Contrary to the conventional approach of using tables for everything, we're opting for a different method. In this case, we'll save the entire data within the form itself. While not a novel concept, this method is rarely employed due to its complexity. Specifically, we'll store the data in the form's custom-made properties, akin to the Tag property of a form or control. Creating these properties with VBA programs is possible, although the route taken for this process is somewhat uncommon.</p><p> You can see how these Custom Properties are addressed for storing/retrieving data in them. For an introduction to this method visit this Link: <a href="https://www.msaccesstips.com/2007/08/saving-data-on-forms-not-in-table.html" target="_blank">Saving Data on Forms, Not in Table</a> to see a simple practical use of this method.</p><p>To preserve data from the ColorGrid, <a href="https://www.msaccesstips.com/2019/10/date2text-and-text2date-functions.html" target="_blank">Text</a> Boxes, and other controls during changes or when closing, we implemented custom properties to store this information.</p><p>The saving of values to all custom properties occurs when the form is closed. Upon reopening the form, these values are read from the custom properties and displayed on the corresponding controls. These two event procedures are implemented in the <b>CWiz_ObjInit</b> Class Module.</p><p>Regarding the ScrollBar Change Event Subroutine, pay attention to the subsequent lines responsible for updating custom properties and the method employed to address and store values into them:</p>
<pre>Set cdb = CurrentDb
Set doc = cdb.Containers("Forms").Documents("ColorPalette")
.
.
.
doc.Properties("RGBColor").Value = .Color.BackColor
doc.Properties("BN").Value = intB
.
</pre>
<p>Before saving the values into the Custom Properties we must create the Properties on the Form. This is a one-time exercise.</p><h3 style="text-align: left;">Sample Custom Property Management VBA Code.</h3><p>Let us see an example of creating a Custom Property to save an Employee's Name in <b>Form1</b>. Sample VBA Code is given below:</p>
<pre>'Create a Custom Property in Form1
Private Sub CreateProperty()
Dim db As Database
Dim doc As Document
Dim prp As Property
Set db = CurrentDb
Set doc = db.Containers("Forms").Documents("Form1")
Set prp = doc.CreateProperty("EmpName", dbText, "SampleText")
doc.Properties.Append prp
Set prp = Nothing
Set doc = Nothing
Set db = Nothing
End Sub
'Assign a value to Custom Property in Form1
Private Sub AssignPropertyValue()
Dim db As Database
Dim doc As Document
Set db = CurrentDb
Set doc = db.Containers("Forms").Documents("Form1")
doc.Properties("EmpName").Value = "Michael Colins"
Set doc = Nothing
Set db = Nothing
End Sub
'Create a Custom Property in Form1
Private Sub ReadPropertyValue()
Dim db As Database
Dim doc As Document
Dim strName As String
Set db = CurrentDb
Set doc = db.Containers("Forms").Documents("Form1")
strName = doc.Properties("EmpName").Value
MsgBox "Name: " & UCase(strName)
Set doc = Nothing
Set db = Nothing
End Sub
'Create a Custom Property in Form1
Private Sub DeleteProperty()
Dim db As Database
Dim doc As Document
Set db = CurrentDb
Set doc = db.Containers("Forms").Documents("Form1")
doc.Properties.Delete "EmpName"
Set doc = Nothing
Set db = Nothing
End Sub
</pre>
<p>All the procedures for creating a custom property to save an employee's name, assigning a name to the property, reading it back, displaying it in a message box, and deleting the custom property from <b>Form1</b> are outlined in the individual subroutines above.</p><h3 style="text-align: left;">The CWiz_TextBox Wrapper Class.</h3><p>The <b>CWiz_TextBox</b> Wrapper Class manages the AfterUpdate() Event, allowing for direct entry of color numbers for Red, Green, and Blue into the TextBoxes named RN, GN, and BN. The subsequent changes are seamlessly reflected in the ScrollBars, the color graphs situated to the right of the ScrollBars, the new color showcased in the large rectangle label background, and the RGB color number displayed in the TextBox.</p><p>You may save your new Color in the Color Grid as explained earlier. </p><p>The CWiz_TextBox Wrapper Class Module Code is given below:</p>
<pre style="text-align: left;">Option Compare Database
Option Explicit
Private WithEvents ctxt As Access.TextBox
Private cFrm As Form
Const GraphFactor = (1 / 255) * 1440
Private db As Database
Private doc As Document
Public Property Get c_Frm() As Form
Set c_Frm = cFrm
End Property
Public Property Set c_Frm(ByRef vcFrm As Form)
Set cFrm = vcFrm
End Property
Public Property Get c_txt() As Access.TextBox
Set c_txt = ctxt
End Property
Public Property Set c_txt(ByRef vctxt As Access.TextBox)
Set ctxt = vctxt
Set db = CurrentDb
Set doc = db.Containers("Forms").Documents("ColorPalette")
End Property
Private Sub ctxt_AfterUpdate()
With cFrm
Select Case ctxt.Name
Case "RN"
.Ctl_R.Value = cFrm![RN]
doc.Properties("RN").Value = cFrm!RN
.CheckBox.Value = False
Case "GN"
.Ctl_G.Value = cFrm![GN]
doc.Properties("GN").Value = cFrm!GN
.CheckBox.Value = False
Case "BN"
.Ctl_B.Value = cFrm![BN]
doc.Properties("BN").Value = cFrm!BN
.CheckBox.Value = False
End Select
End With
End Sub
</pre>
<h3 style="text-align: left;">The CWiz_Label Wrapper Class.</h3><p>The <a href="https://www.msaccesstips.com/2010/04/label-animation-style-1.html " target="_blank">Labels</a> in the ColorGrid, the Color <a href="https://www.msaccesstips.com/2007/09/ms-access-and-graph-charts2.html" target="_blank">Graph</a> Labels to the right side of the ScrollBars, and the RGB Color Display Label and all their Click Event Subroutines are kept in the CWiz_Label Wrapper Class. </p><p>The CWiz_Label Wrapper Class Module Event Procedure Code is given below:</p>
<pre>Option Compare Database
Option Explicit
Private WithEvents clbl As Access.Label
Private sFrm As Form
Const GraphFactor = (1 / 255) * 1440
Private db As Database
Private doc As Document
Private selflag As Boolean
Private lngColor As Long
Public Property Get s_Frm() As Form
Set s_Frm = sFrm
End Property
Public Property Set s_Frm(ByRef vsFrm As Form)
Set sFrm = vsFrm
End Property
Public Property Get s_clbl() As Access.Label
Set s_clbl = clbl
End Property
Public Property Set s_clbl(ByRef vclbl As Access.Label)
Set clbl = vclbl
Set db = CurrentDb
Set doc = db.Containers("Forms").Documents("ColorPalette")
End Property
Private Sub clbl_Click()
Dim I As Integer
If Val(Mid(clbl.Name, 5)) > 0 Then
I = Val(Mid(clbl.Name, 5))
End If
Select Case I
Case 1 To 25
Call Boxes(I) 'Click on Color Grid
End Select
Select Case clbl.Name
Case "Color"
Call ColorClick 'Click on the RGB Color Display Label
Case "Clip"
Call ClipClick 'Click on this Labek to Copy RGB Color number to ClipBoard
End Select
End Sub
Private Sub ColorClick()
With sFrm
lngColor = .Color.BackColor
!RGBColor = .Controls("Color").BackColor
.Controls("Color").SpecialEffect = 2
'Copy the created color to the grid
!CheckBox.Value = True
End With
End Sub
Private Sub ClipClick()
If Not IsNull(sFrm![RGBColor]) Then
' Copy the TextBox contents to the clipboard
sFrm.RGBColor.SetFocus
DoCmd.RunCommand acCmdCopy
MsgBox "RGB Color Number Copied to Clipboard!", vbInformation
Else
' Display a message if the TextBox is empty
MsgBox "RGBColor is empty!", vbExclamation
End If
End Sub
Private Sub Boxes(ByVal bx As Integer)
Dim j As Integer
Dim ctl As String
Dim Colr As Long
Dim intR As Integer
Dim intG As Integer
Dim intB As Integer
selflag = sFrm!CheckBox.Value
For j = 1 To 25
If j = bx Then
If selflag Then
With sFrm
ctl = "lblC" & j
.Controls(ctl).SpecialEffect = 2
.Controls(ctl).BackColor = .Color.BackColor
doc.Properties("Selected").Value = .Controls(ctl).BackColor
!RGBColor = .Controls(ctl).BackColor
!CheckBox.Value = False
ctl = "C" & j
doc.Properties(ctl).Value = .Color.BackColor
doc.Properties("Selctl").Value = "C" & j
End With
Else
With sFrm
ctl = "lblC" & j
!RGBColor = .Controls(ctl).BackColor
.Controls(ctl).SpecialEffect = 2
doc.Properties("Selected").Value = .Controls(ctl).BackColor
doc.Properties("Selctl").Value = "C" & j
End With
Colr = sFrm!RGBColor
'Split into R,G,B
intR = Colr Mod 256
intG = Colr \ 256 Mod 256
intB = Colr \ 256 \ 256 Mod 256
With sFrm
!RN = intR
.Ctl_R.Value = sFrm!RN
!GN = intG
.Ctl_G.Value = sFrm!GN
!BN = intB
.Ctl_B.Value = sFrm!BN
.R.Width = GraphFactor * intR
.G.Width = GraphFactor * intG
.B.Width = GraphFactor * intB
.R.BackColor = RGB(intR, 0, 0)
.G.BackColor = RGB(0, intG, 0)
.B.BackColor = RGB(0, 0, intB)
.Color.BackColor = RGB(intR, intG, intB)
End With
With doc
.Properties("RGBColor").Value = sFrm.Color.BackColor
.Properties("RN").Value = intR
.Properties("GN").Value = intG
.Properties("BN").Value = intB
End With
End If
Else
ctl = "lblC" & j
sFrm.Controls(ctl).SpecialEffect = 0
End If
Next
End Sub
</pre>
<p>The Intermediary Class Module <b>CWiz_ObjInit</b> VBA Code is given below:</p>
<pre>Option Compare Database
Option Explicit
Private cw As CWiz_Label
Private txt As CWiz_TextBox
Private WithEvents cmd As CommandButton
Private WithEvents frm As Form
Private coll As New Collection
Const GraphFactor = (1 / 255) * 1440
Const MaxColor = 25
Private cdb As Database, ctr As Container, doc As Document
Public Property Get o_Frm() As Form
Set o_Frm = frm
End Property
Public Property Set o_Frm(ByRef voFrm As Form)
Set frm = voFrm
Call Class_Init
End Property
Private Sub Class_Init()
Dim ctl As Control
Const EP = "[Event Procedure]"
Dim I As Integer
Call ColorPalette_Init 'Initialize
Set cmd = frm.cmdClose
cmd.OnClick = EP
For Each ctl In frm.Controls
I = Val(Mid(ctl.Name, 5))
Select Case TypeName(ctl)
Case "Label"
Select Case I
Case 1 To 25
Set cw = New CWiz_Label
Set cw.s_Frm = frm
Set cw.s_clbl = ctl
cw.s_clbl.OnClick = EP
coll.Add cw
Set cw = Nothing
End Select
Select Case ctl.Name
Case "Color"
Set cw = New CWiz_Label
Set cw.s_Frm = frm
Set cw.s_clbl = ctl
cw.s_clbl.OnClick = EP
coll.Add cw
Set cw = Nothing
Case "Clip"
Set cw = New CWiz_Label
Set cw.s_Frm = frm
Set cw.s_clbl = ctl
cw.s_clbl.OnClick = EP
coll.Add cw
Set cw = Nothing
End Select
Case "TextBox"
Select Case ctl.Name
Case "RN", "GN", "BN"
Set txt = New CWiz_TextBox
Set txt.c_Frm = frm
Set txt.c_txt = ctl
txt.c_txt.AfterUpdate = EP
coll.Add txt
Set txt = Nothing
Case "RGBColor"
Set txt = New CWiz_TextBox
Set txt.c_Frm = frm
Set txt.c_txt = ctl
txt.c_txt.OnGotFocus = EP
coll.Add txt
Set txt = Nothing
End Select
End Select
Next
End Sub
Private Sub ColorPalette_Init()
Dim xRN As Integer
Dim xGN As Integer
Dim xBN As Integer
Dim xRGBColor As Long
Dim j As Integer
Dim cdb As Database
Dim ctr As Container
Dim doc As Document
Dim strctl As String
Set cdb = CurrentDb
Set ctr = cdb.Containers("Forms")
Set doc = ctr.Documents("ColorPalette")
xRN = doc.Properties("RN").Value
xGN = doc.Properties("GN").Value
xBN = doc.Properties("BN").Value
xRGBColor = doc.Properties("RGBColor").Value
With frm
![RN] = xRN
![GN] = xGN
![BN] = xBN
.R.Width = xRN * GraphFactor
.R.BackColor = RGB(xRN, 0, 0)
.G.Width = xGN * GraphFactor
.G.BackColor = RGB(0, xGN, 0)
.B.Width = xBN * GraphFactor
.B.BackColor = RGB(0, 0, xBN)
.Ctl_R.Value = xRN
.Ctl_G.Value = xGN
.Ctl_B.Value = xBN
.Color.BackColor = RGB(xRN, xGN, xBN)
.RGBColor = .Color.BackColor
End With
For j = 1 To MaxColor
strctl = "lblC" & j
frm.Controls(strctl).BackColor = doc.Properties("C" & j).Value
If ("C" & j) = doc.Properties("Selctl").Value Then
frm.Controls(strctl).SpecialEffect = 2
End If
Next j
Form_Load_Exit:
Exit Sub
Form_Load_Err:
MsgBox Err.Description, , "Form_Load"
Resume Form_Load_Exit
End Sub
Private Sub cmd_Click()
Dim msg As String
Dim ctl As String, strC1 As String, j As Integer
msg = "Close the Color Wizard?"
If MsgBox(msg, vbYesNo + vbQuestion, "cmd_Click()") = vbYes Then
Set cdb = CurrentDb
Set ctr = cdb.Containers("Forms")
Set doc = ctr.Documents("ColorPalette")
For j = 1 To MaxColor
ctl = "lblC" & j
strC1 = "C" & j
doc.Properties(strC1).Value = frm.Controls(ctl).BackColor
If frm.Controls(ctl).SpecialEffect = 2 Then
doc.Properties("Selected").Value = frm.Controls(ctl).BackColor
doc.Properties("SelCtl").Value = strC1
End If
Next
doc.Properties("RGBColor").Value = Nz(frm.Controls("RGBColor").Value, 0)
DoCmd.Close acForm, frm.Name
End If
End Sub
Private Sub Class_Terminate()
Do While coll.Count > 1
coll.Remove 1
Loop
End Sub
</pre>
<p> When the form is opened, the form object is passed to the intermediary class module, initiating the execution of the Class_Init subroutine. The first subroutine, ColorPalette_Init, is invoked from within the Class_Init subroutine. This procedure retrieves all the values saved in the form's custom properties and assigns them to the labels, scroll bars, and text boxes on the form.</p><p>This procedure is normally run in the Form_Load() Event Procedure and the current Values on the Form Controls are saved when the <a href="https://www.msaccesstips.com/2023/10/streamlining-custom-made-form-wizard.html" target="_blank">Form</a> is Closed.</p><p>There is a single command button to close the form. A singular command button object instance is created in the Intermediary Class Module, and its Click Event is enabled. Consequently, when the cmdClose command button is clicked, the form close event procedure is executed in the <b>CWiz_ObjInit</b> Module. Before closing the form, all the values of the Color Wizard form controls are saved in the form's custom properties.</p><p>This topic was initially <a href="https://www.msaccesstips.com/2010/10/create-your-own-color-palette.html" target="_blank">published in October 2010</a>, featuring a color palette of 15 colors, and all the wizard VBA code was implemented in the Form Module. The older version of the wizard form is included in the demo database, labeled ColorPaletteOld. Feel free to open and review the code, assessing how it has been transformed into a form that can now be executed from the standalone class module, excluding the VBA code related to the ActiveX Control ScrollBar.</p>
<h3>Demo Database Download Link. </h3>
<!--Download Link /downloads/2023/11/RGB_Colors.zip-->
<div class="separator" style="clear: both; text-align: center;">
<a href="https://drive.google.com/file/d/1luKsL4QIUbhl9u7bhQmT5PewNMr5L6J9/view?usp=sharing">
<img border="0" data-original-height="29" data-original-width="22" height="29" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFE4uDLH9dk3S6pEcfTkT6GW6r11XqBI4HQnLnhQFZMKFeeQt5ABV_NqvLHM68J7RacISsvbUEl0PlAq6V7i0t0mQF3iesrBuqGuvrf5CJV72OzrGZ7rdMxkxzw8FYlz-qniNySoqtqUN4/s320/zip.gif" width="22" />RGB_Colors.zip</a>
</div>
<!--Download Link /downloads/2023/11/RGB_Colors.zip-->
<h3>Streamlining Form Module Code in Standalone Class Module.</h3>
<ol>
<li><a href="https://www.msaccesstips.com/2023/04/reusing-form-module-vba-code-for-new.html" target="_Blank">Re-using Form Module VBA Code for New Projects.</a></li>
<li><a href="https://www.msaccesstips.com/2023/04/streamlining-form-module-code-part-two.html" target="_Blank">Streamlining Form Module Code Part-Two</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/05/streamlining-form-module-code-part-three.html" target="_Blank">Streamlining Form Module Code Part-Three</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/06/streamlining-form-module-code-part-four.html" target="_Blank">Streamlining Form Module Code Part-Four</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/06/streamlining-form-module-code-part-five.html" target="_Blank">Streamlining Form Module Code Part-Five</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-six.html" target="_Blank">Streamlining Form Module Code Part-Six</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-seven.html" target="_Blank">Streamlining Form Module Code Part-Seven</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-eight.html" target="_Blank">Streamlining Form Module Code Part-Eight</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part-nine.html" target="_Blank">Streamlining Form Module Code Part-Nine</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part-ten.html" target="_Blank">Streamlining Form Module Code Part-Ten</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part.html" target="_Blank">Streamlining Form Module Code Part-Eleven</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-report-module-code-in.html" target="_Blank">Streamlining Report Module Code in Class Module</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-report-module-in-class.html" target="_Blank">Streamlining Report Module Code in Class Module-2</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-form-module-code-part-14.html" target="_Blank">Streamlining Form Module Code Part-14:All Controls</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-custom-made-form-wizard.html" target="_Blank">Streamlining Custom Made Form Wizard-15</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-form-vba-custom-report.html" target="_Blank">Streamlining Custom Report Wizard-16</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-form-vba-external-files.html" target="_Blank">Streamlining Form VBA External File Browser-17</a></li>
<li><a href="https://www.msaccesstips.com/2023/11/streamlining-event-procedures.html" target="_Blank">Streamlining Event Procedures of 3D TextWizard-18</a></li>
<li><a href="https://www.msaccesstips.com/2023/11/streamlining-event-procedures-rgbcolor.html" target="_Blank">Streamlining Event Procedures RGB Color Wizard-19</a></li>
<li><a href="https://www.msaccesstips.com/2023/12/streamlining-numbers-to-words-converter.html" target="_Blank">Streamlining Event Procedures Number To Words-20</a></li>
</ol>
a.p.r. pillaihttp://www.blogger.com/profile/02627590161919120214noreply@blogger.com0tag:blogger.com,1999:blog-7457417942281874810.post-41642120988195351332023-11-11T11:17:00.007+05:302024-01-07T23:12:58.864+05:30Streamlining Event Procedures 3DTextWizard<!--Episode 18-->
<h3 style="text-align: left;"> The 3D Heading Creation Wizard for Form and Report.</h3><p>The contents of the 3D Text Wizard were originally published in a series of articles back in September 2006. I created this website to share some tips, many of which I had implemented in my projects while working in an Automotive Company in the Sultanate of Oman. These articles, featuring the QBColor version, aimed to provide insights and practical knowledge based on real-world experiences.</p><p>The 3D Text Wizard now offers a broader spectrum of colors in RGB format for users to choose from. To enhance customization, a <a href="https://www.msaccesstips.com/2010/10/colors-24-bits-and-binary-conversion.html" target="_blank">ColorList</a> table has been introduced, allowing users to add additional colors. This feature proves beneficial for creating three-dimensional text, such as Form headings or displaying data field values like employee names or product names, especially when viewing them from a distance.</p>
<!-- Google In-Article Ads start -->
<script async="" src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle" data-ad-client="ca-pub-2376623373770548" data-ad-format="fluid" data-ad-layout="in-article" data-ad-slot="6147807460" style="display: block; text-align: center;"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
<!-- Google In-Article Ads END -->
<h3 style="text-align: left;">Example-1: Employee Name.</h3>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgTS-L5TR7N2qcVDKPKs5MiJXhFis-KsAV0yyDF3SNoou6EtvEAepF6ygNA_uffHVHj6PyHgp_aUOywekgj7c5H58DZ-IYDnVL9wD-LrLeMzf-5lOJzQDPjjt0Xy9eRLfMuFHOMEd34YKAA-TwILnG2IT69YnaSnOL2ahDjav2XQhywne81wpRsCEP8x0aM/s840/Anne%20Hellung.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="575" data-original-width="840" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgTS-L5TR7N2qcVDKPKs5MiJXhFis-KsAV0yyDF3SNoou6EtvEAepF6ygNA_uffHVHj6PyHgp_aUOywekgj7c5H58DZ-IYDnVL9wD-LrLeMzf-5lOJzQDPjjt0Xy9eRLfMuFHOMEd34YKAA-TwILnG2IT69YnaSnOL2ahDjav2XQhywne81wpRsCEP8x0aM/s320/Anne%20Hellung.png" width="320" /></a></div>
<h3>Example-2:Order Details Form View-2</h3>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhjGAfPGMu18gtb3PCoWGniCGGxUG84HYnm0L_9vXI9V1ZKCC2RmhzqQi3vHWA3dCenQlK3sErV-jzZFmz8cH-xY6ETMxUadar4LWK_Nax6qogSDI0fYvxqa6-NxPnrFWkcmbfTWZxeJI5CY0EkEwCO6bKPykrlzAtm3uo210lUIEKJzF67PwhQfTypKcwz/s823/Cajun%20Seasoning.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="611" data-original-width="823" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhjGAfPGMu18gtb3PCoWGniCGGxUG84HYnm0L_9vXI9V1ZKCC2RmhzqQi3vHWA3dCenQlK3sErV-jzZFmz8cH-xY6ETMxUadar4LWK_Nax6qogSDI0fYvxqa6-NxPnrFWkcmbfTWZxeJI5CY0EkEwCO6bKPykrlzAtm3uo210lUIEKJzF67PwhQfTypKcwz/s320/Cajun%20Seasoning.png" width="320" /></a></div>
<h3>The 3D Text Wizard Image is given below:</h3>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXzsh40T14X7zKqOWCurFkn5SI3YeIMqwbA1QXYQRodoK-ItsnQdEwmIMJZxRtZQ-zvr7nyvPYcKIn3nP7vP1f-Fe5bYLFoy9S454LwaUecmZd9ncOyRBD2koVJoJeISm229LxEAxWnKj1Oxz4bHvTx2-yByVUjJcR2M-ZbTFeKJDp5M489WWVtc7O58uN/s786/3DTextWizard.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="700" data-original-width="786" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXzsh40T14X7zKqOWCurFkn5SI3YeIMqwbA1QXYQRodoK-ItsnQdEwmIMJZxRtZQ-zvr7nyvPYcKIn3nP7vP1f-Fe5bYLFoy9S454LwaUecmZd9ncOyRBD2koVJoJeISm229LxEAxWnKj1Oxz4bHvTx2-yByVUjJcR2M-ZbTFeKJDp5M489WWVtc7O58uN/s320/3DTextWizard.png" width="320" /></a></div>
<p>The event procedures and functions within the 3D Text Wizard have been streamlined in adherence to the new Event Procedure Coding Rules established in the Standalone Class Module. This approach enhances code organization and readability while promoting efficient maintenance and development practices.</p><h3 style="text-align: left;">3D Text Creation Technique.</h3><p>The 3D Text is crafted through layers of Label Controls or <a href="https://www.msaccesstips.com/2008/10/textbox-and-label-inner-margins.html" target="_blank">Text Boxes</a>, each containing the same text. Alternatively, this effect can be manually achieved by strategically placing layers with an attractive color on top and additional layers with a darker forecolor behind, slightly shifted to one of the four corners—top-left, bottom-left, top-right, or bottom-right. This technique imparts a shadow effect, enhancing the visual appeal of the text.</p><p>Performing this task manually each time can be a time-consuming exercise. As you may have observed in earlier episodes, I created headings for my Forms using only two labels, achieving a 3D-like appearance effortlessly.</p><p>To the left of the Text Wizard user interface, you'll find a ListBox displaying a variety of colors. This ListBox is linked to a table named 'Colors,' providing the flexibility to add additional color codes as needed.</p><p>On the right side of the Colors List, there are two OptionGroup controls. The top one features two options. When the first radio button is selected, the color chosen from the ListBox will be displayed in the top rectangle control. This selected color will then be applied to the topmost label's caption or the font color of the TextBox used for the 3D Text.</p><p>If the second radio button is selected, the color chosen from the <a href="https://www.msaccesstips.com/2008/05/create-list-from-another-listbox.html" target="_blank">ListBox</a> is applied to the border color of the first two Wizards, namely Border 2D and Border 3D, within the Options Group Control below. It's worth noting that other Text Style Wizard options exclusively utilize the ForeColor option.</p><h3 style="text-align: left;">The 3D Text Shadow Positions.</h3><p>At the top right side, there is a <a href="https://www.msaccesstips.com/2009/09/dynamic-listbox-combobox-contents.html" target="_blank">ComboBox</a> with four options (0-3) to specify the positions of the light and shadow for the 3D Text.</p><p>Shadow Positions:</p><p>0 - Left Top Corner.</p><p>1 - Left Bottom Corner.</p><p>2 - Right Top Corner</p><p>3 - Right Bottom Corner </p><p>The first text style, 2D, creates a white border around the text and doesn't require additional settings. For both 2D and 3D text styles, the wizard achieves the effect by creating five or seven labels with the same text and different ForeColor, positioning them underneath the top label and slightly adjusting their placement towards the shadow position. In the case of the 2D border design, the other labels are moved towards the four corners of the top label.</p><h3 style="text-align: left;">3D Text Control Types.</h3><p>The <a href="https://www.msaccesstips.com/2013/12/updating-combobox-when-not-in-list-is.html" target="_blank">ComboBox</a> positioned below the Shadow Style ComboBox offers two options and utilizes two types of controls to generate the 3D Text.</p><p>1 - Label </p><p>2 - TextBox</p><p>The first option is good for creating Static Headings on Forms or Reports.</p>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEizE4iGlkXz-CRn3cZPIvgj-Php4RZHeKtgZ4pLPkv_GZUV0ecrxfhDIb6hufWuHJGgQr9VFIltr9CVCc0zw2KygIvb6K5qgx6vICrqS7dSJbwGCNqkW7HcftYVAYhlvnLz_qOiSJQEakov/s1600/report_title.jpg" style="clear: left; float: center; margin-bottom: 1em; margin-right: 1em;"><img border="0" data-original-height="285" data-original-width="683" height="134" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEizE4iGlkXz-CRn3cZPIvgj-Php4RZHeKtgZ4pLPkv_GZUV0ecrxfhDIb6hufWuHJGgQr9VFIltr9CVCc0zw2KygIvb6K5qgx6vICrqS7dSJbwGCNqkW7HcftYVAYhlvnLz_qOiSJQEakov/s320/report_title.jpg" width="320" /></a></div>
<p>The second option is TextBox-based 3D Text. It is good for displaying data from Form or Report Field(s) by creating Expressions, like the Images given at the top of this Page.</p><p>After selecting the desired options, click on the 'Create 3D Text' <a href="https://www.msaccesstips.com/2008/04/colorfull-command-buttons.html" target="_blank">Command Button</a> to generate the 3D Text. The resulting 3D Text is displayed in the Detail Section of a new Form. Beneath the 3D Text, a label control provides instructions on how to modify the text, font, font size, and font styles, such as bold, italics, and underline, if necessary. Users can carefully select the top label or TextBox without shifting it from its original position to change the label caption or TextBox contents.</p><p>After making changes, select all the Labels/TextBoxes Controls together by clicking near the controls and dragging over all the labels. Copy and paste them to the desired location where you want them to appear.</p><p>To display the form field(s) data in the TextBox controls, write the expression, such as =[First Name] & " " & [Last Name], in the Control Source Property after selecting all the text boxes together for the 3D Text.</p><p>After generating a text style, you have the option to preserve it within the 3D Text Wizard. You can then import it into other projects, making it available for use with modifications.</p><p>With this streamlined event procedure coding in the standalone class module, only a single Wrapper class is needed for Command Buttons on the form.</p><p>There's only one ListBox control on the form, and its Click-Event can be captured in a subroutine within the intermediary Class Module.</p><p>Similarly, there are two Option Group Controls on the form—one with the Style of 3D Text options and the other managing Fore-Color and Back-Color parameter selection activities. The Back-Color option specifically applies to the first two text styles, for 2D and 3D Border Color Options. When either of these two options is selected, the Back-Color option will be in an enabled state; otherwise, it will be disabled.</p><p>Since both of these actions can be controlled from the TextStyle Selection Option Group Control, a separate wrapper class module is not required. In both the ListBox and Options Group Control, we declared the object Instances of the ListBox and Options Group Control in the Intermediary Class Module, qualified with the keyword WithEvents. The two ComboBoxes on the Form are used for setting the required 3D Text Shadow Options and the other ComboBox is for selecting the Label or Text Control option. There are no Event Procedures to run for these two Controls. </p><h3 style="text-align: left;">The Form Module VBA Code.</h3><p>Both the ListBox and the Option Group Control's Click Events are enabled in the <b>Class_Init()</b> subroutine, and corresponding subroutines are written in this module. First, the Form Module Code is listed below:</p>
<pre>Option Compare Database
Option Explicit
Private W As TWiz_Obj_Init
Private Sub Form_Load()
Set W = New TWiz_Obj_Init
Set W.w_frm = Me
End Sub
Private Sub Form_Unload(Cancel As Integer)
Set W = Nothing
End Sub
</pre>
<p>The TWiz_Obj_Init class is declared in the global area of the module with the object name <b>'W.'</b> In the Form_Load() event procedure, the object is instantiated, and the current form object (Me) is passed to the <b>W.w_frm()</b> Property Procedure. When the form is closed, the class object <b>'W'</b> is released from memory.</p>
<h3>The TWiz_Obj_Init Class Module Code is Listed Below.</h3>
<pre>Option Compare Database
Option Explicit
Private wcmd As TWiz_CmdButton
Private WithEvents lst As Access.ListBox
Private WithEvents opt As Access.OptionGroup
Private wfrm As Access.Form
Private Coll As New Collection
Public Property Get w_frm() As Form
Set w_frm = wfrm
End Property
Public Property Set w_frm(ByRef vfrm As Form)
Set wfrm = vfrm
DoCmd.Restore
Call Class_Init
End Property
Private Sub Class_Init()
Dim ctl As Control
Const EP = "[Event Procedure]"
Set opt = wfrm.TxtStyle '3D Text Styles
opt.OnClick = EP
Set lst = wfrm.ColorList 'List of Colors
lst.OnClick = EP
For Each ctl In wfrm.Controls
Select Case TypeName(ctl)
Case "CommandButton"
Select Case ctl.Name
Case "cmd3D", "cmdClose"
Set wcmd = New TWiz_CmdButton
Set wcmd.c_Frm = wfrm
Set wcmd.c_cmd = ctl
wcmd.c_cmd.OnClick = EP
Coll.Add wcmd
Set wcmd = Nothing
End Select
End Select
Next
End Sub
Private Sub lst_Click()
Dim cl As Long
cl = lst.Value
Select Case lst.Name
Case "ColorList"
If wfrm.FBack = 1 Then
wfrm.Fore.BackColor = cl
wfrm.CFore = cl
Else
wfrm.Back.BackColor = cl
wfrm.CBack = cl
End If
End Select
End Sub
Private Sub opt_Click()
Dim opval As Integer
Select Case opt.Name
Case "TxtStyle"
opval = opt.Value
With wfrm.cboStyle
If opval > 1 Then
.Enabled = True
Else
.Enabled = False
End If
End With
With wfrm.Opt2
Select Case opval
Case 1, 2
.Enabled = True
Case Else
.Enabled = False
End Select
End With
End Select
End Sub
Private Sub Class_Terminate()
Do While Coll.Count > 0
Coll.Remove 1
Loop
End Sub
</pre>
<p>The following ListBox and OptionGroup Controls declarations are placed in the Global area of the Class Module.</p>
<pre>
Private WithEvents lst As Access.ListBox
Private WithEvents opt As Access.OptionGroup
</pre>
<p>The following statements in the Class_Init() Subroutine assign the <a href="https://www.msaccesstips.com/2013/08/external-references-in-conditional.html" target="_blank">References</a> from these Objects in the Form and enable their Click Events, by assigning the "[Event Procedure]" text in their Event Properties:</p>
<pre>Set opt = wfrm.TxtStyle '3D Text Styles
opt.OnClick = EP
Set lst = wfrm.ColorList 'List of Colors
lst.OnClick = EP
</pre>
<p>Both these objects Sub lst_Click() and Sub opt_Click() Event Subroutines are written below the Sub Class_Int() Procedure.</p>
<h3>The Command Button Wrapper Class Module.</h3>
<p>There is only one Wrapper Class for both the Command Buttons on the Form. All the Wizard Functions are called from the Command Button with the Caption 'Create 3D Text' Click Event Procedure, depending on the 3D Text Style Option selected.</p><p>The Command Button Wrapper Class Subroutine that Calls the Wizard Functions is listed below for reference.</p>
<pre>Option Compare Database
Option Explicit
Private WithEvents cmd As Access.CommandButton
Private cfrm As Access.Form
Public Property Get c_Frm() As Form
Set c_Frm = cfrm
End Property
Public Property Set c_Frm(ByRef vfrm As Form)
Set cfrm = vfrm
End Property
Public Property Get c_cmd() As CommandButton
Set c_cmd = cmd
End Property
Public Property Set c_cmd(ByRef vcmd As CommandButton)
Set cmd = vcmd
End Property
Private Sub cmd_Click()
Select Case cmd.Name
Case "cmd3D"
Call Create3D(cfrm) 'Call the 3D Text Wizard
Case "cmdClose"
If MsgBox("Close the 3DTextWizard? ", vbYesNo + vbQuestion, "cmdClose_Click()") = vbYes Then
DoCmd.Close acForm, cfrm.Name
End If
End Select
End Sub
</pre>
<p>The <b>Cmd3D</b> Click Event Subroutine invokes the <b>Create3D(cfrm)</b> Subroutine and passes the Form Object as a Parameter. This Subroutine in the Standard Module gathers the 3D Text Wizard option settings from the Form into related variables and then calls the wizard function based on the selected text style. Each Wizard function, such as <b>Border2D</b>, and others, calls three different programs to create the 3D Text.</p><p>For example, the Border2D Wizard calls the following three Functions to complete the full task of creating the 3D Text:</p>
<ol><li>FormTxtLabels() ' Creates a Form and the Label or Text Controls</li><li><p>Validate_Dup() ' Performs a Validation check.</p></li><li><p>MsgLabel() 'Creates a Label control with instructions to use the 3D Text.</p></li></ol>
<p>Listing all the Wizard VBA codes here is not feasible due to its large volume. However, the 3DTextWizard Demo Database is attached with all the code. You can download it from the link provided at the end of this Page. All the Wizard VBA codes are available in the TxtWizard Standard Module.</p><p>Visit the following Links of Articles published earlier for more details on the Wizard Functions:</p>
<ol>
<li><a href="https://www.msaccesstips.com/2006/09/create-3d-headings-on-forms.html" target="_Blank">Create 3D Headings on Form</a></li>
<li><a href="https://www.msaccesstips.com/2006/09/border2d-heading-text.html" target="_Blank">Border 2D Heading Text</a></li>
<li><a href="https://www.msaccesstips.com/2006/09/border3d-heading.html" target="_Blank">Border3D Heading Style</a></li>
<li><a href="https://www.msaccesstips.com/2006/09/shadow3d-heading-style.html" target="_Blank">Shadow3D Heading Style</a></li>
</ol>
<h3>Demo Database Download Link. </h3>
<!--Download Link /downloads/2023/11/3DTextWizard.zip-->
<div class="separator" style="clear: both; text-align: center;">
<a href="https://drive.google.com/file/d/1UU2F8M_QLaX6KqyF_wo9fBMlkWXYxtPV/view?usp=sharing">
<img border="0" data-original-height="29" data-original-width="22" height="29" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFE4uDLH9dk3S6pEcfTkT6GW6r11XqBI4HQnLnhQFZMKFeeQt5ABV_NqvLHM68J7RacISsvbUEl0PlAq6V7i0t0mQF3iesrBuqGuvrf5CJV72OzrGZ7rdMxkxzw8FYlz-qniNySoqtqUN4/s320/zip.gif" width="22" />3DTextWizard.zip</a>
</div>
<!--Download Link /downloads/2023/11/3DTextWizard.zip-->
<h3>Streamlining Form Module Code in Standalone Class Module.</h3>
<ol>
<li><a href="https://www.msaccesstips.com/2023/04/reusing-form-module-vba-code-for-new.html" target="_Blank">Re-using Form Module VBA Code for New Projects.</a></li>
<li><a href="https://www.msaccesstips.com/2023/04/streamlining-form-module-code-part-two.html" target="_Blank">Streamlining Form Module Code Part-Two</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/05/streamlining-form-module-code-part-three.html" target="_Blank">Streamlining Form Module Code Part-Three</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/06/streamlining-form-module-code-part-four.html" target="_Blank">Streamlining Form Module Code Part-Four</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/06/streamlining-form-module-code-part-five.html" target="_Blank">Streamlining Form Module Code Part-Five</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-six.html" target="_Blank">Streamlining Form Module Code Part-Six</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-seven.html" target="_Blank">Streamlining Form Module Code Part-Seven</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-eight.html" target="_Blank">Streamlining Form Module Code Part-Eight</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part-nine.html" target="_Blank">Streamlining Form Module Code Part-Nine</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part-ten.html" target="_Blank">Streamlining Form Module Code Part-Ten</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part.html" target="_Blank">Streamlining Form Module Code Part-Eleven</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-report-module-code-in.html" target="_Blank">Streamlining Report Module Code in Class Module</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-report-module-in-class.html" target="_Blank">Streamlining Report Module Code in Class Module-2</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-form-module-code-part-14.html" target="_Blank">Streamlining Form Module Code Part-14:All Controls</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-custom-made-form-wizard.html" target="_Blank">Streamlining Custom Made Form Wizard-15</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-form-vba-custom-report.html" target="_Blank">Streamlining Custom Report Wizard-16</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-form-vba-external-files.html" target="_Blank">Streamlining Form VBA External File Browser-17</a></li>
<li><a href="https://www.msaccesstips.com/2023/11/streamlining-event-procedures.html" target="_Blank">Streamlining Event Procedures of 3D TextWizard-18</a></li>
<li><a href="https://www.msaccesstips.com/2023/11/streamlining-event-procedures-rgbcolor.html" target="_Blank">Streamlining Event Procedures RGB Color Wizard-19</a></li>
<li><a href="https://www.msaccesstips.com/2023/12/streamlining-numbers-to-words-converter.html" target="_Blank">Streamlining Event Procedures Number To Words-20</a></li>
</ol>
a.p.r. pillaihttp://www.blogger.com/profile/02627590161919120214noreply@blogger.com0tag:blogger.com,1999:blog-7457417942281874810.post-80069731031623791092023-10-30T17:47:00.016+05:302024-02-27T18:37:31.718+05:30Streamlining Form VBA External Files List HyperLinks<!--Episode 17-->
<h3 style="text-align: left;"> External Files List in Hyperlinks in Form.</h3><h3 style="text-align: left;">The Office.FileDialog Control </h3><p>The FileDialog Control displays files from the selected folder as <a href="https://www.msaccesstips.com/2007/05/open-forms-with-hyperlinks-in-listbox.html" target="_blank">hyperlinks</a> in the form. Clicking on a hyperlink will open the file in its native application, if available.</p><p>The File Dialog control features user-defined filters that allow users to display specific categories of files, such as <a href="https://www.msaccesstips.com/2011/04/invoke-word-mail-merge-from-access2007.html" target="_blank">Word Documents</a>, Excel Worksheet files, and Access Databases, or view all files within a folder. Select the required files and click on the 'Create File Link' <a href="https://www.msaccesstips.com/2006/09/command-button-animation.html" target="_blank">Command Button</a> to add the selected files to a table and display them in the form in Datasheet View as hyperlinks. The full file pathnames are shown in the second column for reference.</p>
<!--Google In-Article Ads start-->
<script async="" src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle" data-ad-client="ca-pub-2376623373770548" data-ad-format="fluid" data-ad-layout="in-article" data-ad-slot="6147807460" style="display: block; text-align: center;"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
<!--Google In-Article Ads END-->
<h3 style="text-align: left;">Files' List Display Image.</h3>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg965DLUJWqwKnIrHUVhED7S3hr3NC8rIhal9Ku6SZPxJyb5Hiwi2QGH3YXsXXZQ3wzHejcQtNfMlsWfixCEsHe9LbofmOmh5z2mhhVK5bUGPGNenkPS9sScZRP08fJC1ZdXtlB8X4pBvWlq1hgWYvPeDK9CfsY5J60n7tFJ8daddzHlQy-iuauqZoENvh2/s967/FileDialog3.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="File Browser" border="0" data-original-height="875" data-original-width="967" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg965DLUJWqwKnIrHUVhED7S3hr3NC8rIhal9Ku6SZPxJyb5Hiwi2QGH3YXsXXZQ3wzHejcQtNfMlsWfixCEsHe9LbofmOmh5z2mhhVK5bUGPGNenkPS9sScZRP08fJC1ZdXtlB8X4pBvWlq1hgWYvPeDK9CfsY5J60n7tFJ8daddzHlQy-iuauqZoENvh2/s320/FileDialog3.png" width="320" /></a></div>
<p>After setting the File Filter in the TextBox with the name Pathname, Click on the <b>Create File Links</b> Command Button to open the following File Browser control to display the Files and Folders. </p>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiUVGeDa5Khs6WTzr040YvI5fFrCA26NPj5bEa4o-ZxXVVu5VVLqlQmmEtSCc-3KSD9gvbR8erNK8snfe95OuRtJAftuYHpN7WKyS_r_hOzkRmY5ynz-v29S92D29OruOM_4HAjiQaiunt1dYfHX8cSEkrcnAeiF_RtoR9FLwe1ISecR96U_X4x7blDjEoZ/s848/FileDialog2.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="File List View" border="0" data-original-height="579" data-original-width="848" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiUVGeDa5Khs6WTzr040YvI5fFrCA26NPj5bEa4o-ZxXVVu5VVLqlQmmEtSCc-3KSD9gvbR8erNK8snfe95OuRtJAftuYHpN7WKyS_r_hOzkRmY5ynz-v29S92D29OruOM_4HAjiQaiunt1dYfHX8cSEkrcnAeiF_RtoR9FLwe1ISecR96U_X4x7blDjEoZ/s320/FileDialog2.png" width="320" /></a></div>
<p>At this point, you can select any folder to search for files if needed. To select several adjoining files, click on the first file, hold the Shift key, click on the last file, and then click the '<b>Open</b>' command button. The selected files will appear in the list, as shown in the first image.</p><h3 style="text-align: left;">The Form Module VBA Code.</h3>
<pre>Option Compare Database
Option Explicit
Private FD As New FLst_Object_Init
Private Sub Form_Load()
<span> </span>Set FD.fl_Frm = Me
End Sub
Private Sub Form_Unload(Cancel As Integer)
Set FD = Nothing
End Sub
</pre>
<p>
</p><p>In the Global Declaration area, the Class_Init Class Module with the name <b>FLst_Object_Init </b>Object is declared with the name FD. An Instance of the Object is created in memory using the keyword <b>New</b> in the declaration.</p><p>In the Form_Load() Event Procedure the current Form Object is passed to the <b>FD.fl_Frm</b> Property to the FD Object Instance.</p><h3>The FLst_Object_Init Class Module Code.</h3><p>The FLst_Object_Init with the <b>Class_Init()</b> Subroutine VBA Code is given below:</p>
<pre>Option Compare Database
Option Explicit
Private cmd As FLst_CmdButton
Private frm As Access.Form
Private Coll As New Collection
'------------------------------------------------------
'Streamlining Form Module Code
'in Stand-alone Class Modules
'------------------------------------------------------
'Disk Directory Listing in Hyperlinks
'Author: a.p.r. pillai
'Date : 25/10/2023
'Rights: All Rights(c) Reserved by www.msaccesstips.com
'------------------------------------------------------
Public Property Get fl_Frm() As Access.Form
Set fl_Frm = frm.m_cFrm
End Property
Public Property Set fl_Frm(ByRef pNewValue As Access.Form)
Set frm = pNewValue
Call Class_Init
End Property
Private Sub Class_Init()
Dim ctl As Control
Dim listcount As Long
Const EP = "[Event Procedure]"
<span style="color: red;">'=============================</span>
'Calling the Public Function ButtonStatus() From <span style="color: red;">FLst_CmdButton</span> Class
'from the <b>Flst_CmdButton</b> Class directly,
<span style="color: red;">Set cmd = New FLst_CmdButton </span>'Create a separate instance<span style="color: red;">
Set cmd.cmd_Frm = frm </span>'Pass the Form Object to the Property<span style="color: red;">
Call cmd.ButtonStatus </span>'Call the Public Function, with Param, if any<span style="color: red;">
Set cmd = Nothing </span>'Remove the instance
<span style="color: red;">'=============================
</span>
For Each ctl In frm.Controls
Select Case TypeName(ctl)
Case "CommandButton"
Select Case ctl.Name
Case "cmdHelp", "cmdFileDialog", _
"cmdDelLink", "cmdDelFile", _
"cmdClose", "cmdDelAll"
Set cmd = New FLst_CmdButton
Set cmd.cmd_Frm = frm
Set cmd.c_cmd = ctl
cmd.c_cmd.OnClick = EP
Coll.Add cmd
Set cmd = Nothing
End Select
End Select
Next
End Sub
Private Sub Class_Terminate()
Do While Coll.Count > 0
Coll.Remove 1
Loop
End Sub
</pre>
<p>The following two Subroutines, if present in the Class Module, run automatically. </p>
<ol>
<li><p>Class_Initialize()</p></li><li><p>Class_Terminate()</p></li>
</ol>
<p>Assume that we have both the above Subroutines in ClassA.</p><p>You create an Instance of ClassA in ClassB, like <i><b>Dim A As ClassA, Set A = New ClassA</b></i>. The Class_Initialize() Subroutine in ClassA runs, if present. You can put any initializing steps of Code within this Subroutine.</p><p>When you execute the Statement: Set A = Nothing or ClassB object Unloads the Class_Terminate() Subroutine in ClassA executes. You can do the clean-up work, like Set Obj = Nothing, in this Subroutine to clear memory for other use.</p><p>This is useful when other objects like Collection Object, Dictionary Object, or other Class Module declarations are present in the Class Module. The following Class1 Module is Instantiated in the Form Module.</p>
<pre>'Class1
Dim DT As ClsDateTime
Private Sub Class_Initialize()
Set DT = New ClsDateTime
Forms("Form2").Text2 = DT.DateTime
End Sub
Private Sub Class_Terminate()
Set DT = Nothing
End Sub
</pre>
<p>The Class_Initialize() Subroutine, if present in the class module, runs immediately upon instantiating the Class Object. However, in our streamlined VBA coding, we are unable to utilize this feature. This limitation arises because we need to obtain the <a href="https://www.msaccesstips.com/2007/08/saving-data-on-forms-not-in-table.html " target="_blank">Form</a> Object in the Class Module before executing the Class_Init() Subroutine. Consequently, we cannot use the Class_Initialize Subroutine. Instead, we call the Class_Init() Subroutine immediately after acquiring the Form Object in the Set Property Procedure of the Form Object.</p><p>That doesn't mean that we cannot use it at all. We can use it to instantiate the Collection Object or any other Objects used in this Class Module, like the following example:</p>
<pre>Private Sub Class_Initialize()
Set Coll = New Collection
End Sub
</pre>
<p>The <a href="https://www.msaccesstips.com/2018/12/ms-access-and-collection-object-basics.html" target="_blank">Collection</a> Object declaration is made in the Global declaration area of the Class Module. Since we used the <b>New</b> keyword in the Declaration Statement these extra lines of Code are not required within the Class_Initialize() Subroutine.</p><p>The Class_Terminate() Subroutine is useful to clean up memory and works like the Form_Unload() Event Procedure.</p>
<pre>Private Sub Class_Terminate()
Do While Coll.Count > 0
Coll.Remove 1
Loop
End Sub
</pre>
<p>The above Code clears the Collection Object contents when the FLst_Object_Init Class Module Unloads from memory.</p>
<p>In this Project, we need only a single <a href="https://www.msaccesstips.com/2018/12/access-class-module-and-wrapper-classes.html" target="_blank">Wrapper Class</a> Module for the Command Buttons on the Form, besides the Class Module FLst_Object_Init with the Class_Init() Subroutine.</p><p>The Command Button Class <b>FLst_CmdButton</b> has several subroutines and all of them are called individually from under each command Button Name from the Click Event Subroutine for clarity, rather than writing the entire Code directly under the Command Button name.</p>
<h3>The FLst_CmdButton Class Module Code.</h3>
<pre>'The Click Event Subroutines
Private Sub cmd_Click()
Select Case cmd.Name
Case "cmdClose"
If MsgBox("Close this Form?", vbOKCancel + vbQuestion, "cmd_Click") = vbOK Then
DoCmd.Close acForm, cmdfrm.Name
Exit Sub
End If
Case "cmdFileDialog"
Call cmdFileDialog 'Display selected Path & files
Case "cmdDelLink"
Call cmdDelLink 'Delete Selected Link from list
Case "cmdDelAll"
Call cmdDelAll 'Delete All Links from list
Case "cmdDelFile"
Call cmdDelFile 'Delete Link and File from Disk
Case "cmdHelp"
DoCmd.OpenForm "Help", acNormal 'Show help Form
End Select
End Sub
</pre>
<h4 id="FileBrowser">The cmdFileDialog() Subroutine.</h4>
<p>This Subroutine is run by clicking on the Command Button with the Caption <b>Create File Links</b>.</p>
<pre>Private Sub cmdFileDialog()
On Error GoTo cmdFileDialog_Click_Err
'Requires reference to Microsoft Office 12.0 Object Library.
Dim fDialog As office.FileDialog
Dim db As DAO.Database
Dim rst As DAO.Recordset
Dim defPath As String
Dim varFile As Variant
Dim strfiles As String
'Set up the File Dialog.
Set fDialog = Application.FileDialog(msoFileDialogFilePicker)
With fDialog
'Allow user to make multiple selections of disk files.
.AllowMultiSelect = True
.InitialFileName = Dir(strPath)
.InitialView = msoFileDialogViewDetails
'Set the title of the dialog box.
.Title = "Please select one or more files"
'Clear out the current filters, and add our own.
.Filters.Clear
.Filters.Add "Access Databases", "*.mdb; *.accdb"
.Filters.Add "Excel WorkBooks", "*.xlsx; *.xlsm; *.xls; *.csv"
.Filters.Add "Word Documents", "*.docx; *.doc"
.Filters.Add "Access Projects", "*.adp"
.Filters.Add "All Files", "*.*"
.FilterIndex = 1
'.Execute
'Show the dialog box. If the .Show method returns True, the
'user picked at least one file. If the .Show method returns
'False, the user clicked Cancel.
If .Show = True Then
Set db = CurrentDb
Set rst = db.OpenRecordset("DirectoryList", dbOpenDynaset)
'Add all selected files to the DirectoryList Table
defPath = ""
For Each varFile In .SelectedItems
If defPath = "" Then
defPath = Left(varFile, InStrRev(varFile, "\"))
defPath = defPath & "*.*"
cmdfrm.PathName = defPath
cmdfrm.PathName.Requery
strPath = defPath
End If
rst.AddNew
'Create Hyperlink in 4 segments
'1st segment: only the File Name
strfiles = Mid(varFile, InStrRev(varFile, "\") + 1)
'2nd segment:Full File PathName,3rd Empty,4th TipText
strfiles = strfiles & "#" & varFile & "##Click"
rst![FileLinks] = strfiles
rst![Path] = varFile
rst.Update
Next
Call ButtonStatus
Else
MsgBox "You clicked Cancel in the file dialog box."
End If
End With
cmdFileDialog_Click_Exit:
Exit Sub
cmdFileDialog_Click_Err:
MsgBox Err & " : " & Err.Description, , "cmdFileDialog_Click()"
Resume cmdFileDialog_Click_Exit
End Sub
</pre>
<p>The statement '<b>Set fDialog = Application.FileDialog(msoFileDialogFilePicker)</b>' opens the File Browser Dialog Control and initializes its various properties. Within this control, we can define file type filters, allowing users to select specific file types from the filter display when the FileDialog control is open and displaying files from the default path setting. If there are any uncertainties about the file selection procedure, users can click on the Help Command Button located on the top right side of the form. This button provides detailed information about the functions of each command button on the form, explaining what they do and how to select files in different ways.</p>
<p>There is a table named <i>DirectoryList</i> designed to store the selected files in hyperlink format in the table's first column. The second column displays the full path of the files for reference. Clicking on the hyperlink will open the file in its native application, such as MS Word or Excel.</p>
<p>The Call ButtonStatus() statement invokes the ButtonStatus() subroutine, which checks whether the DirectoryList table is empty or not. If the table is empty, all command buttons except the 'Create File Links' and '<b>Help</b>' buttons are disabled. This subroutine is called from within other subroutines and from the FLst_Object_Init class module. Please refer to the VBA code highlighted in red inside the Class_Init() subroutine above.</p><p>If you create a function within a stand-alone class module with public scope, it becomes accessible across other class modules or standard modules within the program. This allows you to call and utilize this function from outside the class module. </p><p>We will do some trial runs to learn how to call a Function from another Class Module, from the Standard Module, and from the Form Module after this Subroutines review. </p>
<h4>The cmdDelLink Subroutine.</h4>
<p>Select a single Record by clicking on the Record Selector Button, to delete it from the Hyperlink List, and click on the <b>Delete Link</b> Command Button. Before deleting the record a message is displayed to reconfirm the action or to Cancel it.</p>
<pre>'Delete the Link From the List
Private Sub cmdDelLink()
On Error GoTo cmdDelLink_Click_Err
Dim db As DAO.Database
Dim rst As DAO.Recordset
Dim strFile As String
Dim msg As String
'Read the current record Pathname
strFile = cmdfrm.DirectoryList.Form!Path
Set db = CurrentDb
Set rst = db.OpenRecordset("DirectoryList", dbOpenDynaset)
rst.FindFirst "Path = '" & strFile & "'"
If Not rst.NoMatch Then
msg = UCase("Link: " & strFile & vbCr & "DELETE from above List?")
If MsgBox(msg, vbQuestion + vbYesNo, "cmddelLink_Click()") = vbYes Then
rst.Delete
rst.Requery
cmdfrm.DirectoryList.Form.Requery
MsgBox UCase("File Link: " & strFile & " Deleted.")
End If
Else
MsgBox UCase("Link: " & strFile & " Not Found!!")
End If
Call ButtonStatus
rst.Close
Set rst = Nothing
Set db = Nothing
cmdDelLink_Click_Exit:
Exit Sub
cmdDelLink_Click_Err:
MsgBox Err & " : " & Err.Description, , "cmdDelLink_Click()"
Resume cmdDelLink_Click_Exit
End Sub
</pre>
<h4>The cmdDelAll() Subroutine.</h4>
<p>This Subroutine Deletes all the Records from the <a href="https://www.msaccesstips.com/2022/05/get-disk-free-space-windows-api.html" target="_blank">DirectoryList</a> Table. All three Command Buttons with Delete actions are disabled and remain disabled till at least one file is added to the Hyperlink List.</p>
<pre>Private Sub cmdDelAll()
Dim msg As String
Dim yn As Integer
Dim listcount As Long
On Error GoTo cmdDelAll_Click_Err
listcount = DCount("*", "DirectoryList")
If listcount = 0 Then
cmdfrm.cmdDelAll.Enabled = False
Exit Sub
Else
cmdfrm.cmdDelAll.Enabled = True
End If
msg = "All File Links in the List will be Deleted!"
msg = msg & vbCr & "Are You sure?"
If MsgBox(msg, vbYesNo + vbCritical, "cmdDelAll()") = vbYes Then
If MsgBox("Deleting All File Links?", vbOKCancel + vbInformation, "cmdDelAll()") = vbOK Then
DoCmd.SetWarnings False
DoCmd.OpenQuery "DeleteAll_LinksQ", acViewNormal
DoCmd.SetWarnings True
cmdfrm.DirectoryList.Form.Requery
cmdfrm.cmdDelAll.Enabled = False
End If
End If
Call ButtonStatus
cmdDelAll_Click_Exit:
Exit Sub
cmdDelAll_Click_Err:
MsgBox Err & " : " & Err.Description, , "cmdDelAll_Click()"
Resume cmdDelAll_Click_Exit
End Sub
</pre>
<h4>The cmdDelFile() Subroutine.</h4>
<h3>Caution:</h3>
<p>Be careful before using this Command Button. This Button Click will delete the File from the Disk and the Link from the list. Use this Command Button only to delete the file from the Disk.</p>
<pre>'Caution: Deletes the File from Disk
'1. Delete the File from Disk
'2. Remove selected link from List
Private Sub cmdDelFile()
On Error GoTo cmdDelFile_Click_Err
Dim db As DAO.Database
Dim rst As DAO.Recordset
Dim strFile As String
Dim msg As String
'Read selected Record Pathinfo
strFile = cmdfrm.DirectoryList.Form!Path
Set db = CurrentDb
Set rst = db.OpenRecordset("DirectoryList", dbOpenDynaset)
rst.FindFirst "Path = '" & strFile & "'"
If Not rst.NoMatch Then
msg = UCase("File: " & strFile & vbCr & "DELETE from Disk?")
If MsgBox(msg, vbQuestion + vbYesNo, "cmdDelFile_Click") = vbYes Then
If MsgBox(UCase("Are you sure you want to Delete") & vbCr _
& UCase(rst!Path & " File from DISK?"), vbCritical + vbYesNo, "cmdDelFile_Click()") = vbNo Then
GoTo cmdDelFile_Click_Exit
End If
'Delete record entry from Table DirectoryList
rst.Delete
rst.Requery
Call ButtonStatus
'Delete file from Disk
If Len(Dir(strFile)) > 0 Then
Kill strFile
MsgBox "File: " & strFile & " Deleted."
Else
MsgBox "File: " & strFile & vbCr & "Not Found on Disk!"
End If
End If
Else
MsgBox "File: " & strFile & " Not Found!!"
End If
cmdDelFile_Click_Exit:
rst.Close
Set rst = Nothing
Set db = Nothing
Exit Sub
cmdDelFile_Click_Err:
MsgBox Err & " : " & Err.Description, , "cmdDelFile_Click()"
Resume cmdDelFile_Click_Exit
End Sub
</pre>
<h4>The ButtonStatus()</h4>
<p>All three Delete Subroutines in the Flst_CmdButton and also from the FLst_Object_Init Class call this Public Subroutine ButtonStatus() to Disable the Command Buttons if the DirectoryList Table is Empty.</p>
<pre>Public Sub ButtonStatus()
Dim listcount As Long
On Error GoTo ButtonsStatus_Err:
listcount = DCount("*", "DirectoryList")
cmdfrm.DirectoryList.Form.Requery
If listcount = 0 Then
cmdfrm.cmdDelLink.Enabled = False
cmdfrm.cmdDelAll.Enabled = False
cmdfrm.cmdDelFile.Enabled = False
Else
cmdfrm.cmdDelLink.Enabled = True
cmdfrm.cmdDelAll.Enabled = True
cmdfrm.cmdDelFile.Enabled = True
End If
ButtonsStatus_Exit:
Exit Sub
ButtonsStatus_Err:
MsgBox Err & " : " & Err.Description, , "ButtonsStatus()"
Resume ButtonsStatus_Exit
End Sub
</pre>
<h3>Calling Public Function from Class Module.</h3>
<ol>
<li><p>Create a Class Module with the name ClsDateTime.</p></li>
<li><p>Copy and Paste the following Function Code into the Class Module:</p>
<pre>Option Compare Database
Option Explicit
Public Function DateTime() As String
Dim fmt As String
fmt = "dd/mm/yyyy hh:nn:ss"
DateTime = "DateTime: " & Format(Now(), fmt)
End Function
</pre>
</li>
<li><p>Save the Class Module.</p></li>
<li><p>Create a New Form with the name Form1, or any other name you prefer and open it in Design View.</p></li>
<li><p>Add a TextBox Control on the Form and make sure the TextBox Name is Text0.</p></li>
<li><p>Display the Property Sheet of the Form and select the Other Tab in the Property Sheet.</p></li>
<li>Set the <b>Has Module </b>Property value to <b>Yes</b> to add a Class Module to the Form.</li><li><p>Display the Code Module of Form1, Copy and Paste the following Code in the Form Module, Save and Close the Form:</p>
<pre>Private Sub Form_Load()
Dim DT As New ClsDateTime
Me.Text0 = DT.DateTime
End Sub
</pre></li><li><p>Open Form1 in Normal View. The current Date and Time will appear in the TextBox.</p></li></ol>
<p>In the Form_Load() Event Procedure creates an Instance of the ClsDateTime Class Module with the object name <b>DT.</b> The next statement while entering <b>DT.</b> the DateTime() Function name will appear and all we have to do is to add the Function. When the Form is open the Date and Time information is displayed in the TextBox.</p><p>The same procedure can be repeated between two Class Modules to call the Function in another Class Module, instead of the Form Module.</p><p>We use three levels of Class Modules for our Streamlined Structured VBA Coding examples. Let us try this Function in a similar setup i.e. the Form Module, the Intermediary Class Module, and the Class Module with our DateTime() Function.</p> <ol><li><p>Make a Copy of Form1 and name it as Form2. </p></li><li><p>Rename the TextBox Name to Text2. </p></li><li><p>Display its Class Module, then Copy and Paste the following two Lines of Code, overwriting the existing lines.</p>
<pre>Option Compare Database
Private test As New Class1
</pre>
</li><li><p>Create a Class Module with the Name <b>Class1</b>.</p></li><li><p>Copy and Pase the Following Code into the Class1 Module:</p>
<pre>Option Compare Database
Private D As New ClsDateTime
Private Sub Class_initialize()
Forms("Form2").Text2 = D.DateTime
End Sub
</pre>
</li><li><p>Select Save from the File Menu to save all the Files.</p></li><li><p>Open Form2 in Normal View. The DateTime value should appear in Text2 TextBox on the Form. </p></li></ol>
<p>Since Class Modules cannot load themselves in Memory we used Form2 Module to create an Instance of Class1 Module into memory. When Class1 Class Module is loaded into memory it creates an Instance of the ClsDateTime Class Module and executes the Class_Initialize() Subroutine. From within this Subroutine Calls the DateTime() Public Function. The result received is saved in the TextBox in Form2.</p><p>Hope you understand now how it works.</p><p>Try Calling the DateTime() Function from the Standard Module from a Test() Function.</p>
<h3>Demo Database Download Link. </h3>
<!--Download Link /downloads/2023/10/DirListing2K1.zip-->
<div class="separator" style="clear: both; text-align: center;">
<a href="https://drive.google.com/file/d/1cCSPECL-EDkSKr9veP_MnjRDaeKHgp_r/view?usp=sharing">
<img border="0" data-original-height="29" data-original-width="22" height="29" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFE4uDLH9dk3S6pEcfTkT6GW6r11XqBI4HQnLnhQFZMKFeeQt5ABV_NqvLHM68J7RacISsvbUEl0PlAq6V7i0t0mQF3iesrBuqGuvrf5CJV72OzrGZ7rdMxkxzw8FYlz-qniNySoqtqUN4/s320/zip.gif" width="22" />DirListing2K1.zip</a>
</div>
<!--Download Link /downloads/2023/10/DirListing2K1.zip-->
<br />
<h4>Streamlining Form Module Code in Standalone Class Module.</h4>
<ol>
<li><a href="https://www.msaccesstips.com/2023/04/reusing-form-module-vba-code-for-new.html" target="_Blank">Re-using Form Module VBA Code for New Projects.</a></li>
<li><a href="https://www.msaccesstips.com/2023/04/streamlining-form-module-code-part-two.html" target="_Blank">Streamlining Form Module Code Part-Two</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/05/streamlining-form-module-code-part-three.html" target="_Blank">Streamlining Form Module Code Part-Three</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/06/streamlining-form-module-code-part-four.html" target="_Blank">Streamlining Form Module Code Part-Four</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/06/streamlining-form-module-code-part-five.html" target="_Blank">Streamlining Form Module Code Part-Five</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-six.html" target="_Blank">Streamlining Form Module Code Part-Six</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-seven.html" target="_Blank">Streamlining Form Module Code Part-Seven</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-eight.html" target="_Blank">Streamlining Form Module Code Part-Eight</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part-nine.html" target="_Blank">Streamlining Form Module Code Part-Nine</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part-ten.html" target="_Blank">Streamlining Form Module Code Part-Ten</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part.html" target="_Blank">Streamlining Form Module Code Part-Eleven</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-report-module-code-in.html" target="_Blank">Streamlining Report Module Code in Class Module</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-report-module-in-class.html" target="_Blank">Streamlining Report Module Code in Class Module-2</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-form-module-code-part-14.html" target="_Blank">Streamlining Form Module Code Part-14:All Controls</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-custom-made-form-wizard.html" target="_Blank">Streamlining Custom Made Form Wizard-15</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-form-vba-custom-report.html" target="_Blank">Streamlining Custom Report Wizard-16</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-form-vba-external-files.html" target="_Blank">Streamlining Form VBA External File Browser-17</a></li>
<li><a href="https://www.msaccesstips.com/2023/11/streamlining-event-procedures.html" target="_Blank">Streamlining Event Procedures of 3D TextWizard-18</a></li>
<li><a href="https://www.msaccesstips.com/2023/11/streamlining-event-procedures-rgbcolor.html" target="_Blank">Streamlining Event Procedures RGB Color Wizard-19</a></li>
<li><a href="https://www.msaccesstips.com/2023/12/streamlining-numbers-to-words-converter.html" target="_Blank">Streamlining Event Procedures Number To Words-20</a></li>
</ol>
a.p.r. pillaihttp://www.blogger.com/profile/02627590161919120214noreply@blogger.com0tag:blogger.com,1999:blog-7457417942281874810.post-18985961098835455242023-10-18T08:16:00.007+05:302024-01-07T23:00:14.287+05:30Streamlining Form VBA Custom Report Wizard - 16<h3 style="text-align: left;"> Streamlining Custom Made Reports Wizard Form Module VBA Code.</h3><p>Hope you like the Custom Form Wizard of last week, which organized its VBA Code in standalone Class Modules. You can reach the Code and review and study them without interfering with the Form Design and Form Module.</p><p>The custom-made <a href="https://www.msaccesstips.com/2023/10/streamlining-custom-made-form-wizard.html" target="_blank">Form Wizard</a> and the Report Wizard Forms have no difference in their User Interface Design. The Report Wizard was also published earlier, way back in December 2008 under Access 2003. Now, the Report Wizard Form Module VBA Codes run from the standalone Class Module to create the Reports.</p><p>The Report Wizard is designed using a TabControl with two Pages. The first TabPage displays the Wizard Type Options in a ListBox and the Table/Query list in a ComboBox Control. </p><p>1. Report in Column Format.</p>
<!-- Google In-Article Ads start -->
<script async="" src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle" data-ad-client="ca-pub-2376623373770548" data-ad-format="fluid" data-ad-layout="in-article" data-ad-slot="6147807460" style="display: block; text-align: center;"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
<!-- Google In-Article Ads END -->
<p>2. Report in Tabular Format.</p><p>The above two Options are inserted as Value List in the RowSource Property of the ListBox. The Default Value Property is set with the expression: <b>= WizList.Column(0,0)</b> to select the first item by default.</p><p>The ComboBox Control displays the list of Tables and Select-Queries filtered from the System Table MSysObjects. The ComboBox's Default Value Property is also set with the expression: <b>=FilesList.Column(0,0)</b> to select the first file as Default.</p><h3 style="text-align: left;">The SQL of the File Selection Query.</h3>
<pre>SELECT MSysObjects.Name
FROM MSysObjects
WHERE (((MSysObjects.Type)=1 Or (MSysObjects.Type)=5) AND ((Left([Name],4))<>'WizQ') AND ((Left([Name],1))<>'~') AND ((MSysObjects.Flags)=0))
ORDER BY MSysObjects.Type, MSysObjects.Name;
</pre>
<p>
The TabControl first page image is given below:
</p>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiX9o4TKvhqttg_Q72ZSo6qm3h-1yKfDpYgwwnMYhU_NBya0yB7l83o7qYUwtymrvrKs1rEw51Ric_yi7fhl7Zzw1w7zUbsTZvPQcmeN7f2Y9ot_dyttqyZJwy2fFy48DHm81_33QfmJwuxEhI_PW9DguDpPX7_MQwnWIxR1Z8G-KSa4DyhBC2hKUMbbWit/s707/ReportWizard1.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="Report Wizard Page1" border="0" data-original-height="707" data-original-width="691" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiX9o4TKvhqttg_Q72ZSo6qm3h-1yKfDpYgwwnMYhU_NBya0yB7l83o7qYUwtymrvrKs1rEw51Ric_yi7fhl7Zzw1w7zUbsTZvPQcmeN7f2Y9ot_dyttqyZJwy2fFy48DHm81_33QfmJwuxEhI_PW9DguDpPX7_MQwnWIxR1Z8G-KSa4DyhBC2hKUMbbWit/s320/ReportWizard1.png" /></a></div>
<p>Report Wizard Page2 Image:</p>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj23v7leG6A7xsjffb06Nzt17YrgETceaLsMkISWWDgfNUz_Ks7In9EJhZPyzR6h7mNfMbvYW0tye7_h2WlUUI3l2ukNmFMmpbRkUiXo_VnOdCcykpqX1m7s_Q4eJp3lsTepYx-ar116XJHMOMhIf0z40z_Y_lVy7kPjC_KELvcyHt-hyhf8E5cV_Y8DBYP/s708/ReportWizard2.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="Report Wizard Page2" border="0" data-original-height="708" data-original-width="692" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj23v7leG6A7xsjffb06Nzt17YrgETceaLsMkISWWDgfNUz_Ks7In9EJhZPyzR6h7mNfMbvYW0tye7_h2WlUUI3l2ukNmFMmpbRkUiXo_VnOdCcykpqX1m7s_Q4eJp3lsTepYx-ar116XJHMOMhIf0z40z_Y_lVy7kPjC_KELvcyHt-hyhf8E5cV_Y8DBYP/s320/ReportWizard2.png" /></a></div>
<p>The following lines of the VBA Code are only needed in the Form's Class Module. All other Events Subroutines and Functions are placed in the Standalone Class Modules.</p>
<pre>Option Compare Database
Option Explicit
Private obj As New RWizObject_Init
Private Sub Form_Load()
Set obj.fm_fom = Me
End Sub
</pre>
<p>The <b>RWizObject_Init </b>Intermediary Class Module is Instantiated with the Object Name <b>obj</b> in the global declaration area of the Form Module. In the Form_Load() Event Procedure the Form Object reference is passed to the RWizObject_Init <a href="https://www.msaccesstips.com/2018/10/ms-access-class-module-and-vba.html" target="_blank">Class Module</a> Property Procedure through the statement <b>Set obj.fm_fom = Me.</b></p>
<h3>The RWizObject_Init Class.</h3>
<p>The <b>RWizObject_Init</b> VBA Code is listed below. All the Report creation functions are placed within this Class Module.</p>
<pre>Option Compare Database
Option Explicit
Private fom As Access.Form
Private cmdb As RWiz_CmdButton
Private lstb As RWiz_ListBox
Private comb As RWiz_Combo
Private tb As RWiz_TabCtl
Private Coll As New Collection
'Wizard Functions Running Command Button Instance'
'Functions are placed in this Module
Private WithEvents cmdFinish As Access.CommandButton
Dim DarkBlue As Long, twips As Long, xtyp As Integer, strFile As String
Public Property Get fm_fom() As Form
Set fm_fom = fom
End Property
Public Property Set fm_fom(ByRef mfom As Form)
Set fom = mfom
Call Class_Init
End Property
Private Sub Class_Init()
Dim Ctl As Control
Const EP = "[Event Procedure]"
'Filter Table/Select Query Names for ComboBox
Call Create_FilesList
For Each Ctl In fom.Controls
Select Case Ctl.ControlType
Case acTabCtl
Set tb = New RWiz_TabCtl
Set tb.Tb_Frm = fom
Set tb.Tb_Tab = Ctl
tb.Tb_Tab.OnChange = EP
Coll.Add tb
Set tb = Nothing
Case acCommandButton
Select Case Ctl.Name
Case "cmdReport"
'Not to add in the Collection object
'The Click Event Runs the Wizard Functions
'from this Class Module, not from the
'Wrapper Class - FWiz_CmdButton
Set cmdFinish = fom.cmdReport
cmdFinish.OnClick = EP
Case Else
Set cmdb = New RWiz_CmdButton
Set cmdb.w_Frm = fom
Set cmdb.w_cmd = Ctl
cmdb.w_cmd.OnClick = EP
Coll.Add cmdb
Set cmdb = Nothing
End Select
Case acComboBox
Set comb = New RWiz_Combo
Set comb.cbo_Frm = fom
Set comb.c_cbo = Ctl
comb.c_cbo.OnGotFocus = EP
comb.c_cbo.OnLostFocus = EP
Case acListBox
Set lstb = New RWiz_ListBox
Set lstb.lst_Frm = fom
Set lstb.m_lst = Ctl
lstb.m_lst.OnGotFocus = EP
lstb.m_lst.OnLostFocus = EP
Coll.Add lstb
Set lstb = Nothing
End Select
Next
End Sub
Private Sub cmdFinish_Click()
xtyp = fom!WizList
strFile = fom!FilesList
If xtyp = 1 Then
Columns strFile
Else
Tabular strFile
End If
DoCmd.Close acForm, fom.Name
End Sub</pre>
<h3>Create_FilesList() Subroutine Code.</h3>
<p>The Subroutine that creates the Files List for the ComboBox on the first page of the Wizard.</p>
<pre>'Create Tables/Queries List for
Private Sub Create_FilesList()
Dim strSQL1 As String
Dim cdb As DAO.Database
Dim Qry As DAO.QueryDef
Dim FList As ComboBox
On Error GoTo Create_FilesList_Err
DoCmd.Restore
strSQL1 = "SELECT MSysObjects.Name " _
& "FROM MSysObjects " _
& "WHERE (((MSysObjects.Type)=1 Or (MSysObjects.Type)=5) " _
& "AND ((Left([Name],4))<>'WizQ') AND ((Left([Name],1))<>'~') " _
& "AND ((MSysObjects.Flags)=0)) " _
& "ORDER BY MSysObjects.Type, MSysObjects.Name;"
DarkBlue = 8388608
twips = 1440
Set cdb = CurrentDb
Set Qry = cdb.QueryDefs("WizQuery")
If Err = 3265 Then
Set Qry = cdb.CreateQueryDef("WizQuery")
Qry.SQL = strSQL1
cdb.QueryDefs.Append Qry
cdb.QueryDefs.Refresh
Err.Clear
End If
With Forms("ReportWizard")
Set FList = .FilesList
.FilesList.RowSource = "WizQuery"
.FilesList.Requery
End With
Create_FilesList_Exit:
Exit Sub
Create_FilesList_Err:
MsgBox Err & ": " & Err.Description, , "Create_FilesList()"
Resume Create_FilesList_Exit
End Sub
</pre>
<h3>The Function that Creates the Report in Column Format.</h3>
<pre>Public Function Columns(ByVal DataSource As String)
Dim cdb As Database
Dim FldList() As String
Dim Ctrl As Control
Dim Rpt As Report
Dim PgSection As Section
Dim DetSection As Section
Dim HdSection As Section
Dim lngTxtLeft As Long
Dim lngTxtTop As Long
Dim lngTxtHeight As Long
Dim lngtxtwidth As Long
Dim lngLblLeft As Long
Dim lngLblTop As Long
Dim lngLblHeight As Long
Dim lngLblWidth As Long
Dim FldCheck As Boolean
Dim strTblQry As String
Dim intflds As Integer
Dim lstcount As Long
Dim RptFields As ListBox
Dim j As Integer
'Create Report with Selected Fields
On Error Resume Next
strFile = DataSource
Set RptFields = fom.SelList
lstcount = RptFields.listcount
If lstcount = 0 Then
MsgBox "Fields Not Selected for Report!"
Exit Function
Else
lstcount = lstcount - 1
End If
ReDim FldList(0 To lstcount) As String
Set cdb = CurrentDb
Set Rpt = CreateReport
Set HdSection = Rpt.Section(acPageHeader)
HdSection.Height = 0.6667 * twips
Set DetSection = Rpt.Section(acDetail)
DetSection.Height = 0.166 * twips
For j = 0 To lstcount
FldList(j) = RptFields.ItemData(j)
Next
With Rpt
.Caption = strFile
.RecordSource = strFile
lngtxtwidth = 1.5 * twips
lngTxtLeft = 1.1 * twips
lngTxtTop = 0.0417 * twips
lngTxtHeight = 0.2181 * twips
lngLblWidth = lngtxtwidth
lngLblLeft = 0.073 * twips
lngLblTop = 0.0417 * twips
lngLblHeight = 0.2181 * twips
End With
For j = 0 To lstcount
Set Ctrl = CreateReportControl(Rpt.Name, acTextBox, acDetail, , FldList(j), lngTxtLeft, lngTxtTop, lngtxtwidth, lngTxtHeight)
With Ctrl
.ControlSource = FldList(j)
.FontName = "Comic Sans MS"
.FontSize = 8
.FontWeight = 700
.ForeColor = DarkBlue
.BorderColor = DarkBlue
.Name = FldList(j)
.BackColor = RGB(255, 255, 255)
.BorderStyle = 1
.SpecialEffect = 0
Select Case (j / 9)
Case 1,2,3
lngTxtTop = (0.0417 * twips)
lngTxtLeft = lngTxtLeft + (2.7084 * twips)
Case Else
lngTxtTop = lngTxtTop + .Height + (0.1 * twips)
End Select
End With
Set Ctrl = CreateReportControl(Rpt.Name, acLabel, acDetail, FldList(j), FldList(j), lngLblLeft, lngLblTop, lngLblWidth, lngLblHeight)
With Ctrl
.Caption = FldList(j)
.Height = (0.2181 * twips)
.Name = FldList(j) & " Label"
.Width = twips
.ForeColor = 0
.BorderStyle = 0
.FontWeight = 400
Select Case (j/9)
Case 1,2,3
lngLblTop = (0.0417 * twips)
lngLblLeft = lngLblLeft + (2.7083 * twips)
Case Else
lngLblTop = lngLblTop + .Height + (0.1 * twips)
End Select
End With
Next
lngLblWidth = 4.5 * twips
lngLblLeft = 0.073 * twips
lngLblTop = 0.0521 * twips
lngLblHeight = 0.323 & twips
lngLblWidth = 4.5 & twips
Set Ctrl = CreateReportControl(Rpt.Name, acLabel, acPageHeader, , "Head1", lngLblLeft, lngLblTop, lngLblWidth, lngLblHeight)
With Ctrl
.Caption = strFile
.TextAlign = 2
.Width = 4.5 * twips
.Height = 0.38 * twips
.ForeColor = DarkBlue
.BorderStyle = 0
.BorderColor = DarkBlue
.FontName = "Times New Roman"
.FontSize = 20
.FontWeight = 700 ' Bold
.FontItalic = True
.FontUnderline = True
End With
Page_Footer Rpt
DoCmd.OpenReport Rpt.Name, acViewPreview
Columns_Exit:
Exit Function
Columns_Err:
MsgBox Err.Description, , "Columns"
Resume Columns_Exit
End Function
</pre>
<h3>The Tabular Type Report Creation Function.</h3>
<p>The major part of the Code lines in both these Wizards are Variable declarations for defining the TextBox and for its Child Label Controls, and for their dimension values, other values like Font, Font Size, ForeColor, and other attribute values settings also come after the creation of these controls.</p><p>The statement <b>Set Ctrl = CreateReportControl()</b>:</p><pre> Set Ctrl = CreateReportControl(Rpt.Name, acTextBox, _
acDetail, , FldList(j), lngTxtLeft, lngTxtTop, lngtxtwidth, lngTxtHeight)</pre><p>have several <a href="https://www.msaccesstips.com/2018/07/function-parameter-array-passing.html" target="_blank">Parameters</a> which need their values predefined before calling the <b>CreateReportControl()</b> Function. The first Parameter is the <a href="https://www.msaccesstips.com/2007/08/highlighting-reports.html " target="_blank">Report</a> Name, next is the type of control (here the TextBox), where to create the Control (in the Detail Section), next is the Parent Parameter if it is a SubReport (here omitted), the fifth parameter is the Field Name and the next four parameters are the control's dimension values.</p><p>The Font and Color attributes of the control are set after its creation. Similarly, the TextBox control's Child Label <a href="https://www.msaccesstips.com/2007/08/edit-data-in-zoom-in-control.html" target="_blank">Control</a> is created next in the Page-Header Section of the Report. </p>
<!-- Google In-Article Ads start -->
<script async="" src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle" data-ad-client="ca-pub-2376623373770548" data-ad-format="fluid" data-ad-layout="in-article" data-ad-slot="6147807460" style="display: block; text-align: center;"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
<!-- Google In-Article Ads END -->
<p>In the above Column-Format Report, the Label Control is created in the Detail Section and to the left side of each TextBox Control. The TextBox is created after leaving enough space for the child-label control on the left side.</p>
<pre>Public Function Tabular(ByVal DataSource As String)
Dim cdb As Database
Dim FldList() As String
Dim Ctrl As Control
Dim Rpt As Report
Dim PgSection As Section
Dim DetSection As Section
Dim lngTxtLeft As Long
Dim lngTxtTop As Long
Dim lngTxtHeight As Long
Dim lngtxtwidth As Long
Dim lngLblLeft As Long
Dim lngLblTop As Long
Dim lngLblHeight As Long
Dim lngLblWidth As Long
Dim FldCheck As Boolean
Dim strTblQry As String
Dim intflds As Integer
Dim lstcount As Long
Dim RptFields As ListBox
Dim j As Integer
'Create Report with Selected Fields
On Error Resume Next
strFile = DataSource
Set RptFields = fom.SelList
lstcount = RptFields.listcount
If lstcount = 0 Then
MsgBox "Fields Not Selected for Report!"
Exit Function
Else
lstcount = lstcount - 1
End If
ReDim FldList(0 To lstcount) As String
Set cdb = CurrentDb</pre><pre>'Create Report Object
Set Rpt = CreateReport
Set PgSection = Rpt.Section(acPageHeader)
PgSection.Height = 0.6667 * twips
Set DetSection = Rpt.Section(acDetail)
DetSection.Height = 0.1667 * twips
For j = 0 To lstcount
FldList(j) = RptFields.ItemData(j)
Next
With Rpt
.Caption = strFile
.RecordSource = strFile
lngtxtwidth = 0.5 * twips
lngTxtLeft = 0.073 * twips
lngTxtTop = 0
lngTxtHeight = 0.1668 * twips
lngLblWidth = lngtxtwidth
lngLblLeft = lngTxtLeft
lngLblTop = 0.5 * twips
lngLblHeight = lngTxtHeight
End With
For j = 0 To lstcount
Set Ctrl = CreateReportControl(Rpt.Name, acTextBox, _
acDetail, , FldList(j), lngTxtLeft, lngTxtTop, lngtxtwidth, lngTxtHeight)
With Ctrl
.ControlSource = FldList(j)
.ForeColor = DarkBlue
.BorderColor = DarkBlue
.BorderStyle = 1
.Name = FldList(j)
lngTxtLeft = lngTxtLeft + (0.5 * twips)
End With
Set Ctrl = CreateReportControl(Rpt.Name, acLabel, _
acPageHeader, , FldList(j), lngLblLeft, lngLblTop, lngLblWidth, lngLblHeight)
With Ctrl
.Caption = FldList(j)
.Name = FldList(j) & " Label"
.Width = (0.5 * twips)
.ForeColor = DarkBlue
.BorderColor = DarkBlue
.BorderColor = 0
.BorderStyle = 1
.FontWeight = 700 ' Bold
lngLblLeft = lngLblLeft + (0.5 * twips)
End With
Next
lngLblWidth = 4.5 * twips
lngLblLeft = 0.073 * twips
lngLblTop = 0.0521 * twips
lngLblHeight = 0.323 & twips
lngLblWidth = 4.5 & twips
Set Ctrl = CreateReportControl(Rpt.Name, acLabel, acPageHeader, , "Head1", lngLblLeft, lngLblTop, lngLblWidth, lngLblHeight)
With Ctrl
.Caption = strFile
.TextAlign = 2
.Width = 4.5 * twips
.Height = 0.38 * twips
.ForeColor = DarkBlue
.BorderStyle = 0
.BorderColor = DarkBlue
.FontName = "Times New Roman"
.FontSize = 16
.FontWeight = 700 ' Bold
.FontItalic = True
.FontUnderline = True
End With
On Error GoTo Tabular_Err
Page_Footer Rpt
DoCmd.OpenReport Rpt.Name, acViewPreview
Tabular_Exit:
Exit Function
Tabular_Err:
MsgBox Err.Description, , "Tabular"
Resume Tabular_Exit
End Function
</pre>
<h3>The Page_Footer() Function Code.</h3>
<p>This Function is called by both the <b>Column</b> and <b>Tabular</b> Wizards to create the Date and Page Numbers in the PageFooter Section of the Report. </p>
<pre>Public Function Page_Footer(ByRef obj)
Dim lngWidth As Long, ctrwidth As Long, ctrlCount As Long
Dim j As Long, cdb As Database
Dim lngleft As Long, lngtop As Long, LineCtrl As Control, Ctrl As Control
Dim rptSection As Section, leftmost As Long, lngheight As Long
Dim rightmost As Long, RightIndx As Integer
'
'Note : The Controls appearing in Detail Section from left to Right
' is not indexed 0 to nn in the order of placing,
' instead 1st control placed in the Section has index value 0
' irrespective of its current position.
'
On Error GoTo Page_Footer_Err
Set cdb = CurrentDb
Set rptSection = obj.Section(acDetail)
ctrlCount = rptSection.Controls.Count - 1
lngleft = rptSection.Controls(0).Left
rightmost = rptSection.Controls(0).Left
'indexed 0 control may not be the leftmost control on the Form/Report
'so find the leftmost control's left value
For j = 0 To ctrlCount
leftmost = rptSection.Controls(j).Left
If leftmost < lngleft Then
lngleft = leftmost
End If
If leftmost > rightmost Then
rightmost = leftmost
RightIndx = j
End If
Next
lngtop = 0.0208 * 1440
lngWidth = 0: ctrwidth = 0
lngWidth = rightmost + rptSection.Controls(RightIndx).Width
lngWidth = lngWidth - lngleft
Set LineCtrl = CreateReportControl(obj.Name, acLine, acPageFooter, "", "", lngleft, lngtop, lngWidth, 0)
Set Ctrl = LineCtrl
LineCtrl.BorderColor = 12632256
LineCtrl.BorderWidth = 2
LineCtrl.Name = "ULINE"
lngtop = 0.0418 * 1440
lngleft = LineCtrl.Left
lngWidth = 2 * 1440
lngheight = 0.229 * 1440
'draw Page No control at the Report footer
Set LineCtrl = CreateReportControl(obj.Name, acTextBox, acPageFooter, "", "", lngleft, lngtop, lngWidth, lngheight)
With LineCtrl
.ControlSource = "='Page : ' & [page] & ' / ' & [pages]"
.Name = "PageNo"
.FontName = "Arial"
.FontSize = 10
.FontWeight = 700
.TextAlign = 1
End With
'draw Date Control at the right edge of the Line Control
'calculate left position of Date control
lngleft = (LineCtrl.Left + Ctrl.Width) - lngWidth
Set LineCtrl = CreateReportControl(obj.Name, acTextBox, acPageFooter, "", "", lngleft, lngtop, lngWidth, lngheight)
With LineCtrl
.ControlSource = "='Date : ' & Format(Date(),'dd/mm/yyyy')"
.Name = "Dated"
.FontName = "Arial"
.FontSize = 10
.FontWeight = 700
.TextAlign = 3
End With
Page_Footer_Exit:
Exit Function
Page_Footer_Err:
MsgBox Err & ": " & Err.Description, "Page_Footer()"
Resume Page_Footer_Exit
End Function
</pre>
<p>There are several Command Buttons on both Pages of the TabControl and all their Event Subroutines are run in the <b>RWiz_CmdButton</b> Wrapper Class. There is one <a href="https://www.msaccesstips.com/2008/03/double-action-command-button.html" target="_blank">Command Button</a> on the second Page with the caption <b>Finish</b> that runs the Report Wizard's Functions. All the Wizard Functions are placed in the <b>WizObject_Init</b> Class Module. For that reason, a separate Command Button Control Instance is defined for the <b>cmdFinish </b>in the WizObject_Init Class Module. The Command Button name on the Form is <b>cmdReport</b> with the caption <b>Finish. </b> The cmdFinish Instance created in the Class Module is not added to the <a href="https://www.msaccesstips.com/2018/12/ms-access-and-collection-object-basics.html" target="_blank">Collection</a> Object after enabling the OnClick Event. </p><p>The Click Event Subroutine of this Command Button is written in the WizObject_Init Class Module so that the Report Wizard Functions can be called from this Module directly.</p><p>At the beginning of the Class_Init() Subroutine, the <b>Create_FilesList()</b> Function is called to create the ComboBox's source list of Tables and Select Queries, followed by the creation of ListBoxes, Command Buttons instances, enabling their Events and adding them to the Collection Object. </p><p>The <b>cmdReport</b> Click Event calls the Report Creation Function. The Column Type Report is not likely to be used, but it is useful for Label Printing.</p><h3 style="text-align: left;">The RWiz_CmdButton Class Module.</h3><p>This Wrapper Class Module of CommandButton Object contains the following Command Button Click Event Subroutines.</p><pre>Option Compare Database
Option Explicit
Private WithEvents cmd As CommandButton
Private frm As Form
Dim DarkBlue As Long, twips As Long, xtyp As Integer, strFile As String
Public Property Get w_Frm() As Form
Set w_Frm = frm
End Property
Public Property Set w_Frm(ByRef wFrm As Form)
Set frm = wFrm
End Property
Public Property Get w_cmd() As CommandButton
Set w_cmd = cmd
End Property
Public Property Set w_cmd(ByRef wcmd As CommandButton)
Set cmd = wcmd
End Property
Private Sub cmd_Click()
Dim lblInfo As String
Select Case cmd.Name
Case "cmdCancel2"
DoCmd.Close acForm, frm.Name
Case "cmdNext"
If frm.SelList.listcount = 0 Then
frm.cmdReport.Enabled = False
Else
frm.cmdReport.Enabled = True
End If
'Display the Wizard selection along with
'the Table/Query selected in a Label Control
'In the 2nd Page when the User Clicks
'the cmdNext Command Button to display
'the 2nd Page of the Wizard.
lblInfo = "Table/Query: " & frm!FilesList
If frm!WizList = 1 Then
lblInfo = lblInfo & " - Column Report."
Else
lblInfo = lblInfo & " - Tabular Report."
End If
frm!info.Caption = lblInfo
'Create the field List of the selected Table
'and display them in the 1st ListBox on the
'2nd Page of the Report Wizard.
Call SelectTable
Case "cmdCancel"
DoCmd.Close acForm, frm.Name
Case "cmdRight"
'Move the selected field to the Right=side ListBox.
'Multiselect option not given
RightAll 1
Case "cmdRightAll"
'Option Number Moves all the fields from
'Left side ListBox to the Right-side ListBox
RightAll 2
Case "cmdLeft"
LeftAll 1
Case "cmdLeftAll"
LeftAll 2
Case "cmdBack"
'Go back to first Page. cancels the 2nd Page selections.
frm.SelList.RowSource = "" 'Empty Selected field list
frm.FilesList.RowSource = "WizQuery"
frm.Page1.Visible = True
frm.Page1.SetFocus
frm.Page2.Visible = False
End Select
End Sub
Private Sub SelectTable()
Dim vizlist As ListBox
Dim lcount As Integer
Dim chkflag As Boolean
Dim FildList As ListBox
Dim strName As String
Dim strRSource As String
Dim cdb As DAO.Database
Dim doc As Document
Dim Tbl As DAO.TableDef
Dim Qry As DAO.QueryDef
Dim QryTyp As Integer
Dim FieldCount As Integer
Dim flag As Byte
Dim j As Integer
Set vizlist = frm.WizList
lcount = vizlist.listcount - 1
chkflag = False
For j = 0 To lcount
If vizlist.Selected(j) = True Then
xtyp = j + 1
chkflag = True
End If
Next
If IsNull(frm![FilesList]) = True Then
MsgBox "Select a File from Table/Query List.", vbOKOnly + vbExclamation, "cmdNext"
frm.WizList.Selected(0) = True
Else
strFile = frm.FilesList
frm.Page2.Visible = True
frm.Page2.SetFocus
frm.Page1.Visible = False
Set cdb = CurrentDb
flag = 0
For Each Tbl In cdb.TableDefs
If Tbl.Name = strFile Then
flag = 1
End If
Next
For Each Qry In cdb.QueryDefs
If Qry.Name = strFile Then
flag = 2
End If
Next
If flag = 1 Then
Set Tbl = cdb.TableDefs(strFile)
Set FildList = frm.FldList
strRSource = ""
FieldCount = Tbl.Fields.Count - 1
For j = 0 To FieldCount
If Len(strRSource) = 0 Then
strRSource = Tbl.Fields(j).Name
Else
strRSource = strRSource & ";" & Tbl.Fields(j).Name
End If
Next
ElseIf flag = 2 Then
Set Qry = cdb.QueryDefs(strFile)
strRSource = ""
FieldCount = Qry.Fields.Count - 1
For j = 0 To FieldCount
If Len(strRSource) = 0 Then
strRSource = Qry.Fields(j).Name
Else
strRSource = strRSource & ";" & Qry.Fields(j).Name
End If
Next
End If
frm.FldList.RowSource = strRSource
frm.FldList.Requery
End If
End Sub
Private Function RightAll(ByVal SelectionType As Integer)
Dim FldList As ListBox, SelctList As ListBox, strRSource As String
Dim listcount As Long, j As Long, strRS2 As String
'On Error GoTo RightAll_Err
If SelectionType = 0 Then
Exit Function
End If
Set FldList = Forms("ReportWizard").FldList
Set SelctList = Forms("ReportWizard").SelList
listcount = FldList.listcount - 1
strRSource = SelctList.RowSource: strRS2 = ""
Select Case SelectionType
Case 1
For j = 0 To listcount
If FldList.Selected(j) = True Then
If Len(strRSource) = 0 Then
strRSource = FldList.ItemData(j)
Else
strRSource = strRSource & ";" & FldList.ItemData(j)
End If
Else
If Len(strRS2) = 0 Then
strRS2 = FldList.ItemData(j)
Else
strRS2 = strRS2 & ";" & FldList.ItemData(j)
End If
End If
Next
SelctList.RowSource = strRSource
FldList.RowSource = strRS2
SelctList.Requery
FldList.Requery
frm.cmdReport.Enabled = True
Case 2
For j = 0 To listcount
If Len(strRSource) = 0 Then
strRSource = FldList.ItemData(j)
Else
strRSource = strRSource & ";" & FldList.ItemData(j)
End If
Next
SelctList.RowSource = strRSource
FldList.RowSource = ""
SelctList.Requery
FldList.Requery
frm.cmdCancel2.SetFocus
If SelctList.listcount = 0 Then
frm.cmdReport.Enabled = False
End If
End Select
frm.cmdReport.Enabled = True
RightAll_Exit:
Exit Function
RightAll_Err:
MsgBox Err & ": " & Err.Description, , "RightAll"
Resume RightAll_Exit
End Function
Private Function LeftAll(ByVal SelectionType As Integer)
Dim FldList As ListBox, SelctList As ListBox, strRSource As String
Dim listcount As Long, j As Long, strRS2 As String
On Error GoTo LeftAll_Err
If SelectionType = 0 Then
Exit Function
End If
Set FldList = Forms("ReportWizard").FldList
Set SelctList = Forms("ReportWizard").SelList
listcount = SelctList.listcount - 1
strRSource = FldList.RowSource: strRS2 = ""
Select Case SelectionType
Case 1
For j = 0 To listcount
If SelctList.Selected(j) = True Then
If Len(strRSource) = 0 Then
strRSource = SelctList.ItemData(j)
Else
strRSource = strRSource & ";" & SelctList.ItemData(j)
End If
Else
If Len(strRS2) = 0 Then
strRS2 = SelctList.ItemData(j)
Else
strRS2 = strRS2 & ";" & SelctList.ItemData(j)
End If
End If
Next
SelctList.RowSource = strRS2
FldList.RowSource = strRSource
SelctList.Requery
FldList.Requery
If SelctList.listcount = 0 Then
frm.cmdReport.Enabled = False
End If
Case 2
For j = 0 To listcount
If Len(strRSource) = 0 Then
strRSource = SelctList.ItemData(j)
Else
strRSource = strRSource & ";" & SelctList.ItemData(j)
End If
Next
SelctList.RowSource = ""
FldList.RowSource = strRSource
SelctList.Requery
FldList.Requery
If SelctList.listcount = 0 Then
frm.cmdReport.Enabled = False
End If
End Select
LeftAll_Exit:
Exit Function
LeftAll_Err:
MsgBox Err.Description, , "LeftAll"
Resume LeftAll_Exit
End Function
</pre>
<p>The Set of four Command Buttons, between the List Boxes on the second Page of the Wizard Form, controls the Field select/unselect operations. The first button moves the selected field from the first list to the second Listbox for the Report only one field at a time. The second Command Button with two greater-than symbols moves all the fields in the first ListBox to the 2nd ListBox.</p><p>The next Command Button removes the item selected from the second ListBox and places it back in the first ListBox. The fourth Command Button Click will remove all the List items selected earlier in the second ListBox for Report and move them all together back in the first ListBox.</p><p>The <b>Back</b> Command Button Click will empty the second ListBox and go back to the Report Wizard's first Page.</p>
<h3>The RWiz_Combo Class Module Code </h3>
<pre>Option Compare Database
Option Explicit
Private cbofrm As Access.Form
Private WithEvents cbo As Access.ComboBox 'ComboBox object
'------------------------------------------------------
'Streamlining Form Module Code
'in Stand-alone Class Modules
'------------------------------------------------------
'ComboBox Wrapper Class
'Author: a.p.r. pillai
'Date : 20/10/2023
'Rights: All Rights(c) Reserved by www.msaccesstips.com
'------------------------------------------------------
'Form's Property GET/SET Procedures
Public Property Get cbo_Frm() As Form
Set cbo_Frm = cbofrm
End Property
Public Property Set cbo_Frm(ByRef cfrm As Form)
Set cbofrm = cfrm
End Property
'TextBox Property GET/SET Procedures
Public Property Get c_cbo() As ComboBox
Set c_cbo = cbo
End Property
Public Property Set c_cbo(ByRef pcbo As ComboBox)
Set cbo = pcbo
End Property
Private Sub cbo_Click()
cbofrm!FileList = Null
cbofrm.TabCtl0.Pages(0).Visible = True
cbofrm.TabCtl0.Pages(0).SetFocus
cbofrm.TabCtl0.Pages(1).Visible = False
cbofrm.TabCtl0.Pages(1).SetFocus
End Sub
Private Sub cbo_GotFocus()
GFColor cbofrm, cbo
End Sub
Private Sub cbo_LostFocus()
LFColor cbofrm, cbo
End Sub
</pre>
<h3>The RWiz_ListBox Class Module Code.</h3>
<pre>Option Compare Database
Option Explicit
Private lstfrm As Access.Form
Private WithEvents lst As Access.ListBox
'------------------------------------------------------
'Streamlining Form Module Code
'in Stand-alone Class Modules
'------------------------------------------------------
'ListBox Wrapper Class
'Author: a.p.r. pillai
'Date : 20/10/2023
'Rights: All Rights(c) Reserved by www.msaccesstips.com
'------------------------------------------------------
'Form's Property GET/SET Procedures
Public Property Get lst_Frm() As Form
Set lst_Frm = lstfrm
End Property
Public Property Set lst_Frm(ByRef mFrm As Form)
Set lstfrm = mFrm
End Property
'TextBox Property GET/SET Procedures
Public Property Get m_lst() As ListBox
Set m_lst = lst
End Property
Public Property Set m_lst(ByRef mLst As ListBox)
Set lst = mLst
End Property
Private Sub lst_Click()
Dim i As Integer
Select Case lst.Name
Case "WizList"
'Code
Case "FldList"
'Code
Case "SelList"
'Code
End Select
End Sub
Private Sub lst_GotFocus()
GFColor lstfrm, lst
End Sub
Private Sub lst_LostFocus()
LFColor lstfrm, lst
End Sub
</pre>
<p>The ListBox and ComboBox Class Module Subroutine Code highlights the Control when these controls receive the Focus.</p>
<h3>The RWiz_TabCtl Class Module Code. </h3>
<pre>Option Compare Database
Option Explicit
Private tbFrm As Form
Private WithEvents tb As TabControl
'------------------------------------------------------
'Streamlining Form Module Code
'in Stand-alone Class Modules
'------------------------------------------------------
'Tab Control Events
'Author: a.p.r. pillai
'Date : 20/10/2023
'Rights: All Rights(c) Reserved by www.msaccesstips.com
'------------------------------------------------------
Public Property Get Tb_Frm() As Form
Set Tb_Frm = tbFrm
End Property
Public Property Set Tb_Frm(ByRef mFrm As Form)
Set tbFrm = mFrm
End Property
Public Property Get Tb_Tab() As TabControl
Set Tb_Tab = tb
End Property
Public Property Set Tb_Tab(ByRef mTab As TabControl)
Set tb = mTab
End Property
Private Sub tb_Change()
Select Case tb.Value
Case 0
'MsgBox "Change Event: TabCtl.Page(0)"
Case 1
'MsgBox "Change Event: TabCtl.Page(1)"
End Select
End Sub
</pre>
<p>This Wrapper Class Module has the TabPage_Change() Event included for completeness, but not used for any purposes.</p>
<h3 style="text-align: left;">Download the Demo Database from the Link given below.</h3>
<!--Download Link /downloads/2023/10/ReportWizard.zip-->
<div class="separator" style="clear: both; text-align: center;">
<a href="https://drive.google.com/file/d/1J4hsQXrXZp50AEzklxLSVN2z3yJrdHnz/view?usp=sharing">
<img border="0" data-original-height="29" data-original-width="22" height="29" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFE4uDLH9dk3S6pEcfTkT6GW6r11XqBI4HQnLnhQFZMKFeeQt5ABV_NqvLHM68J7RacISsvbUEl0PlAq6V7i0t0mQF3iesrBuqGuvrf5CJV72OzrGZ7rdMxkxzw8FYlz-qniNySoqtqUN4/s320/zip.gif" width="22" />ReportWizard.zip</a>
</div>
<!--Download Link /downloads/2023/10/ReportWizard.zip-->
<br />
<h3>Streamlining Form Module Code in Standalone Class Module.</h3>
<ol>
<li><a href="https://www.msaccesstips.com/2023/04/reusing-form-module-vba-code-for-new.html" target="_Blank">Re-using Form Module VBA Code for New Projects.</a></li>
<li><a href="https://www.msaccesstips.com/2023/04/streamlining-form-module-code-part-two.html" target="_Blank">Streamlining Form Module Code Part-Two</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/05/streamlining-form-module-code-part-three.html" target="_Blank">Streamlining Form Module Code Part-Three</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/06/streamlining-form-module-code-part-four.html" target="_Blank">Streamlining Form Module Code Part-Four</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/06/streamlining-form-module-code-part-five.html" target="_Blank">Streamlining Form Module Code Part-Five</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-six.html" target="_Blank">Streamlining Form Module Code Part-Six</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-seven.html" target="_Blank">Streamlining Form Module Code Part-Seven</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-eight.html" target="_Blank">Streamlining Form Module Code Part-Eight</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part-nine.html" target="_Blank">Streamlining Form Module Code Part-Nine</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part-ten.html" target="_Blank">Streamlining Form Module Code Part-Ten</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part.html" target="_Blank">Streamlining Form Module Code Part-Eleven</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-report-module-code-in.html" target="_Blank">Streamlining Report Module Code in Class Module</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-report-module-in-class.html" target="_Blank">Streamlining Report Module Code in Class Module-2</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-form-module-code-part-14.html" target="_Blank">Streamlining Form Module Code Part-14:All Controls</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-custom-made-form-wizard.html" target="_Blank">Streamlining Custom Made Form Wizard-15</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-form-vba-custom-report.html" target="_Blank">Streamlining Custom Report Wizard-16</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-form-vba-external-files.html" target="_Blank">Streamlining Form VBA External File Browser-17</a></li>
<li><a href="https://www.msaccesstips.com/2023/11/streamlining-event-procedures.html" target="_Blank">Streamlining Event Procedures of 3D TextWizard-18</a></li>
<li><a href="https://www.msaccesstips.com/2023/11/streamlining-event-procedures-rgbcolor.html" target="_Blank">Streamlining Event Procedures RGB Color Wizard-19</a></li>
<li><a href="https://www.msaccesstips.com/2023/12/streamlining-numbers-to-words-converter.html" target="_Blank">Streamlining Event Procedures Number To Words-20</a></li>
</ol>
<p><br /></p>a.p.r. pillaihttp://www.blogger.com/profile/02627590161919120214noreply@blogger.com0tag:blogger.com,1999:blog-7457417942281874810.post-71893312299202234032023-10-09T14:52:00.008+05:302024-01-07T22:59:08.449+05:30Streamlining Custom Made Form Wizard VBA - 15<h3 style="text-align: left;"> Streamlined Custom Made Form Wizard.</h3><p>This blog post was <a href="https://www.msaccesstips.com/2008/12/custom-made-form-wizard.html" target="_blank">published earlier in December 2008</a> under Access 2003 and is now updated using the streamlined VBA Coding method in the standalone Class Modules. </p><p>The Form Wizard can create two Types of Forms, one in <b>Column</b> Format and the other in <b>Tabular</b> Format. Why do we need a custom Form Wizard, when Form and Report Wizards are already built-in to the Access System?</p><p>I was curious about the technique used in the built-in Form/Report Wizards. Additionally, I find creating tabular forms with fixed-length fields remarkably convenient for customization, especially when dealing with a substantial number of columns in an Access Form or Report.</p><p>The Forms Wizard is built using a TabControl with two Pages.</p>
<!-- Google In-Article Ads start -->
<script async="" src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle" data-ad-client="ca-pub-2376623373770548" data-ad-format="fluid" data-ad-layout="in-article" data-ad-slot="6147807460" style="display: block; text-align: center;"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
<!-- Google In-Article Ads END -->
<h3 style="text-align: left;">The First Page of the Wizard.</h3>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYb7rWqv41TI2zOps5jk8Fs7f9M3bbjn1w8FOLV1rKi8eTPx2YjJXIXmu-lj7xZ7W03Eh7lHs7psrlO-11ubeIKij6YAbG-huUeea-pxYsSjNqZ83QL5pV7rReq3YdnKdUE3Rq_1lh51PwAqvYFXh2ybTU7sltiyjNftKGwFLx27oBkdXNyFCJA9JV8qk0/s693/FormWizard1.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="689" data-original-width="693" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYb7rWqv41TI2zOps5jk8Fs7f9M3bbjn1w8FOLV1rKi8eTPx2YjJXIXmu-lj7xZ7W03Eh7lHs7psrlO-11ubeIKij6YAbG-huUeea-pxYsSjNqZ83QL5pV7rReq3YdnKdUE3Rq_1lh51PwAqvYFXh2ybTU7sltiyjNftKGwFLx27oBkdXNyFCJA9JV8qk0/s320/FormWizard1.png" width="320" /></a></div>
<p>The ListBox on the top has Wizard Type Selection, <b>Column</b> Type, or <b>Tubular</b> Type Form.</p><p>The <a href="https://www.msaccesstips.com/2009/09/dynamic-listbox-combobox-contents.html" target="_blank">Combobox</a> below will have the list of Tables and Query names, picked up from the Database System Table, with the use of a query, and added as Source Items of the Combobox.</p>
<p>The SQL of the Query is given below.</p><pre>SELECT MSysObjects.Name
FROM MSysObjects
WHERE (((MSysObjects.Type)=1 Or (MSysObjects.Type)=5) AND ((Left([Name],4))<>'WizQ') AND ((Left([Name],1))<>'~') AND ((MSysObjects.Flags)=0))
ORDER BY MSysObjects.Type, MSysObjects.Name;
</pre><p>The options seen selected in both ListBox and ComboBox are selected by default. You may change the Options before proceeding to the next level by clicking on the <a href="https://www.msaccesstips.com/2011/06/creating-animated-command-button-with.html" target="_blank">Command Button</a> with the Caption <b>Next</b>.</p><h3 style="text-align: left;">The Second Wizard Page Image.</h3>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEicfqWimjs7fir8xqRm9cwiUOVPRltgA8G7oyrjGsnTHz4E1-WIUL9JUgVHB-prylBztd7aTs0jTkzGd2bhpiA2-E2o0Nk_Ao5Ed_DBKZviwANKineftvjzE9Ujk6ZNx9FBkaBlTdO4Gc5w4XKBokTy4wr4TIAcp5VvyhkhdRN9l-pX9J2QfQMVCe5qSon4/s690/FormWizard2.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="685" data-original-width="690" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEicfqWimjs7fir8xqRm9cwiUOVPRltgA8G7oyrjGsnTHz4E1-WIUL9JUgVHB-prylBztd7aTs0jTkzGd2bhpiA2-E2o0Nk_Ao5Ed_DBKZviwANKineftvjzE9Ujk6ZNx9FBkaBlTdO4Gc5w4XKBokTy4wr4TIAcp5VvyhkhdRN9l-pX9J2QfQMVCe5qSon4/s320/FormWizard2.png" width="320" /></a></div>
<p>The Selected Table or <a href="https://www.msaccesstips.com/2008/02/union-query.html" target="_blank">Query</a> Fields will appear in the left-side ListBox Control. You may Add the required field by selecting the Field and then clicking on the <b><span style="font-size: medium;">></span></b> Button individually. If all the Fields are required then click on the <b><span style="font-size: medium;">>></span></b> Button.</p><p>Similarly, remove the selected Fields, if not required from the selected list of fields, using the left-side pointing arrow <b><span style="font-size: medium;"><</span></b> or <b><span style="font-size: medium;"><<</span></b> Buttons. If no fields are selected the <a href="https://www.msaccesstips.com/2006/09/command-button-animation.html" target="_blank">Command Button</a> with the <b>Finish</b> caption will remain disabled.</p><p>When the required fields are selected Click on the <b>Finish</b> Command Button to create the Form and to open in Normal View.</p><h3 style="text-align: left;">The FormWizard Form Module VBA Code.</h3>
<pre>Option Compare Database
Option Explicit
Private obj As New FWizObject_Init
Private Sub Form_Load()
Set obj.fm_fom = Me
End Sub
</pre>
<p>The <b>FWizObject_Init</b> Class Module contains the List of Object-level Wrapper Classes. The FWizObject_Init Class Module VBA Code is given below.</p>
<pre>Option Compare Database
Option Explicit
Private fom As Access.Form
Private cmdb As FWiz_CmdButton
Private lstb As FWiz_ListBox
Private comb As FWiz_Combo
Private tb As FWiz_TabCtl
Private Coll As New Collection
'Wizard Functions Running Command Button Instance'
'Functions are placed in this Module
<b>Private WithEvents cmdFinish As Access.CommandButton</b>
Dim DarkBlue As Long, twips As Long, xtyp As Integer, strFile As String
Public Property Get fm_fom() As Form
Set fm_fom = fom
End Property
Public Property Set fm_fom(ByRef mfom As Form)
Set fom = mfom
Call Class_Init
End Property
Private Sub Class_Init()
Dim Ctl As Control
Const EP = "[Event Procedure]"
'Filter Table/Select Query Names for ComboBox
Call Create_FilesList
For Each Ctl In fom.Controls
Select Case Ctl.ControlType
Case acTabCtl
Set tb = New FWiz_TabCtl
Set tb.Tb_Frm = fom
Set tb.Tb_Tab = Ctl
tb.Tb_Tab.OnChange = EP
Coll.Add tb
Set tb = Nothing
Case acCommandButton
Select Case Ctl.Name
Case "cmdForm"
'Not to add in the Collection object
'The Click Event Runs the Wizard Functions
'from this Class Module, not from the
'Wrapper Class - FWiz_CmdButton
Set cmdFinish = fom.cmdForm
cmdFinish.OnClick = EP
Case Else
Set cmdb = New FWiz_CmdButton
Set cmdb.w_Frm = fom
Set cmdb.w_cmd = Ctl
cmdb.w_cmd.OnClick = EP
Coll.Add cmdb
Set cmdb = Nothing
End Select
Case acComboBox
Set comb = New FWiz_Combo
Set comb.cbo_Frm = fom
Set comb.c_cbo = Ctl
comb.c_cbo.OnGotFocus = EP
comb.c_cbo.OnLostFocus = EP
Case acListBox
Set lstb = New FWiz_ListBox
Set lstb.lst_Frm = fom
Set lstb.m_lst = Ctl
lstb.m_lst.OnGotFocus = EP
lstb.m_lst.OnLostFocus = EP
Coll.Add lstb
Set lstb = Nothing
End Select
Next
End Sub
Private Sub cmdFinish_Click()
xtyp = fom!WizList
strFile = fom!FilesList
If xtyp = 1 Then
Columns strFile
Else
Tabular strFile
End If
DoCmd.Close acForm, fom.Name
End Sub
'Create Tables/Queries List for
Private Sub Create_FilesList()
Dim strSQL1 As String
Dim cdb As DAO.Database
Dim Qry As DAO.QueryDef
Dim FList As ComboBox
On Error GoTo Create_FilesList_Err
DoCmd.Restore
strSQL1 = "SELECT MSysObjects.Name " _
& "FROM MSysObjects " _
& "WHERE (((MSysObjects.Type)=1 Or (MSysObjects.Type)=5) " _
& "AND ((Left([Name],4))<>'WizQ') AND ((Left([Name],1))<>'~') " _
& "AND ((MSysObjects.Flags)=0)) " _
& "ORDER BY MSysObjects.Type, MSysObjects.Name;"
DarkBlue = 8388608
twips = 1440
Set cdb = CurrentDb
Set Qry = cdb.QueryDefs("WizQuery")
If Err = 3265 Then
Set Qry = cdb.CreateQueryDef("WizQuery")
Qry.SQL = strSQL1
cdb.QueryDefs.Append Qry
cdb.QueryDefs.Refresh
Err.Clear
End If
With Forms("FormWizard")
Set FList = .FilesList
.FilesList.RowSource = "WizQuery"
.FilesList.Requery
End With
Create_FilesList_Exit:
Exit Sub
Create_FilesList_Err:
MsgBox Err & ": " & Err.Description, , "Create_FilesList()"
Resume Create_FilesList_Exit
End Sub
'Wizard Functions
Private Function Columns(ByVal DataSource As String)
'-------------------------------------------------------------------
'Author : a.p.r. pillai
'Date : Sept-2000
'URL : www.msaccesstips.com
'All Rights Reserved by www.msaccesstips.com
'-------------------------------------------------------------------
Dim cdb As Database
Dim FldList() As String
Dim Ctrl As Control
Dim frm As Form
Dim HdSection As Section
Dim DetSection As Section
Dim FrmFields As ListBox
Dim lngTxtLeft As Long
Dim lngTxtTop As Long
Dim lngTxtHeight As Long
Dim lngtxtwidth As Long
Dim lngLblLeft As Long
Dim lngLblTop As Long
Dim lngLblHeight As Long
Dim lngLblWidth As Long
Dim FldCheck As Boolean
Dim strTblQry As String
Dim intflds As Integer
Dim lstcount As Long
Dim j As Integer
'Create Form with Selected Fields
On Error GoTo Columns_Err
strFile = DataSource
Set FrmFields = Forms("FormWizard").SelList
lstcount = FrmFields.listcount
If lstcount = 0 Then
MsgBox "Fields Not Selected for Form", , "FormWizard"
Exit Function
Else
lstcount = lstcount - 1
End If
ReDim FldList(0 To lstcount) As String
Set cdb = CurrentDb
Set frm = CreateForm
Application.RunCommand acCmdFormHdrFtr
With frm
.DefaultView = 0
.ViewsAllowed = 0
.DividingLines = False
.Section(acFooter).Visible = True
.Section(acFooter).Height = 0.1667 * twips '0.1667 Inches
.Section(acHeader).DisplayWhen = 0
.Section(acHeader).Height = 0.5 * twips '0.5 Inches
End With
Set HdSection = frm.Section(acHeader)
HdSection.Height = 0.6667 * twips
Set DetSection = frm.Section(acDetail)
DetSection.Height = 0.166 * twips
For j = 0 To lstcount
FldList(j) = FrmFields.ItemData(j)
Next
With frm
.RecordSource = strFile
.Caption = strFile
lngtxtwidth = 1.25 * twips
lngTxtLeft = 1.6694 * twips
lngTxtTop = 0
lngTxtHeight = 0.21 * twips
lngLblLeft = 0.073 * twips
lngLblTop = 0 '0.5 * twips
lngLblWidth = 1.5208 * twips
lngLblHeight = lngTxtHeight
End With
For j = 0 To lstcount
'Create Field Child Label
Set Ctrl = CreateControl(frm.Name, acLabel, acDetail, _
FldList(j), FldList(j), lngLblLeft, lngLblTop, lngLblWidth, lngLblHeight)
With Ctrl
.Caption = FldList(j)
.Name = FldList(j) & " Label"
.Width = 1.5208 * twips
.ForeColor = 0
.BorderColor = 0
.BorderStyle = 0
.FontWeight = 400 ' Normal 700 ' Bold
Select Case (1 / 9)
Case 1, 2, 3
lngLblTop = 0
lngLblLeft = lngLblLeft + (2.7083 * twips)
Case Else
lngLblTop = lngLblTop + .Height + (0.1 * 1440)
End Select
End With
'Create Field TextBox
Set Ctrl = CreateControl(frm.Name, acTextBox, acDetail, , _
FldList(j), lngTxtLeft, lngTxtTop, lngtxtwidth, lngTxtHeight)
With Ctrl
.ControlSource = FldList(j)
.FontName = "Arial"
.FontSize = 10
.Name = FldList(j)
.BackColor = RGB(255, 255, 255)
.ForeColor = 0
.BorderColor = 9868950
.BorderStyle = 1
.SpecialEffect = 2
Select Case (j / 9)
Case 1, 2, 3
lngTxtTop = 0
lngTxtLeft = lngTxtLeft + (3.7084 * twips)
Case Else
lngTxtTop = lngTxtTop + .Height + (0.1 * twips)
End Select
End With
Next
'Create Heading Label
Call CreateHeading(frm)
Columns_Exit:
Exit Function
Columns_Err:
MsgBox Err.Description, , "Columns()"
Resume Columns_Exit
End Function
Private Function Tabular(ByVal DataSource As String)
'-------------------------------------------------------------------
'Author : a.p.r. pillai
'Date : Sept-2000
'URL : www.msaccesstips.com
'All Rights Reserved by www.msaccesstips.com
'-------------------------------------------------------------------
Dim cdb As Database
Dim FldList() As String
Dim Ctrl As Control
Dim frm As Form
Dim HdSection As Section
Dim DetSection As Section
Dim lngTxtLeft As Long
Dim lngTxtTop As Long
Dim lngTxtHeight As Long
Dim lngtxtwidth As Long
Dim lngLblLeft As Long
Dim lngLblTop As Long
Dim lngLblHeight As Long
Dim lngLblWidth As Long
Dim FldCheck As Boolean
Dim strTblQry As String
Dim intflds As Integer
Dim lstcount As Long
Dim FrmFields As ListBox
Dim j As Integer
'Create Form with Selected Fields
strFile = DataSource
On Error GoTo Tabular_Err
Set FrmFields = Forms("FormWizard").SelList
lstcount = FrmFields.listcount
If lstcount = 0 Then
MsgBox "Fields Not Selected for the Form"
Exit Function
Else
lstcount = lstcount - 1
End If
ReDim FldList(0 To lstcount) As String
Set cdb = CurrentDb
Set frm = CreateForm
Application.RunCommand acCmdFormHdrFtr
With frm
.DefaultView = 1
.ViewsAllowed = 0
.DividingLines = False
.Section(acFooter).Visible = True
.Section(acHeader).DisplayWhen = 0
.Section(acHeader).Height = 0.5 * 1440
.Section(acFooter).Height = 0.1667 * 1440
End With
Set HdSection = frm.Section(acHeader)
HdSection.Height = 0.6667 * twips
Set DetSection = frm.Section(acDetail)
DetSection.Height = 0.166 * twips
For j = 0 To lstcount
FldList(j) = FrmFields.ItemData(j)
Next
With frm
.Caption = strFile
.RecordSource = strFile
lngtxtwidth = 0.5 * twips 'Inches
lngTxtLeft = 0.073 * twips
lngTxtTop = 0
lngTxtHeight = 0.166 * twips
lngLblWidth = lngtxtwidth
lngLblLeft = lngTxtLeft
lngLblTop = 0.5 * twips
lngLblHeight = lngTxtHeight
End With
For j = 0 To lstcount
'Create Fields in the Detail Section
Set Ctrl = CreateControl(frm.Name, acTextBox, acDetail, , _
FldList(j), lngTxtLeft, lngTxtTop, lngtxtwidth, lngTxtHeight)
With Ctrl
.ControlSource = FldList(j)
.Name = FldList(j)
.FontName = "Verdana"
.Width = (0.5 * twips) 'Inches
.FontSize = 8
.ForeColor = 0
.BorderColor = 12632256
.BackColor = 16777215
.BorderStyle = 1
.SpecialEffect = 0
lngTxtLeft = lngTxtLeft + (0.5 * twips)
End With
'Field Heading Labels
Set Ctrl = CreateControl(frm.Name, acLabel, acHeader, , _
FldList(j), lngLblLeft, lngLblTop, lngLblWidth, lngLblHeight)
With Ctrl
.Caption = FldList(j)
.Name = FldList(j) & " Label"
.Width = (0.5 * twips)
.ForeColor = DarkBlue
.BorderColor = DarkBlue
.BorderStyle = 1
.FontWeight = 700 ' Bold
lngLblLeft = lngLblLeft + (0.5 * twips)
End With
Next
'Heading Label
Call CreateHeading(frm)
Tabular_Exit:
Exit Function
Tabular_Err:
MsgBox Err & ": " & Err.Description, , "Tabular()"
Resume Tabular_Exit
End Function
Private Function CreateHeading(ByRef hFrm As Form)
Dim Ctl As Control
Dim lngLblLeft As Long
Dim lngLblTop As Long
Dim lngLblWidth As Long
Dim lngLblHeight As Long
On Error GoTo CreateHeading_Err
lngLblLeft = 0.073 * twips
lngLblTop = 0.0521 * twips
lngLblWidth = 1.5208 * twips
lngLblHeight = 0.323 & twips
'Create Heading Label
Set Ctl = CreateControl(hFrm.Name, acLabel, acHeader, , _
"Head1", lngLblLeft, lngLblTop, lngLblWidth, lngLblHeight)
With Ctl
.Caption = strFile
.TextAlign = 2
.Width = 4.5 * twips
.Height = 0.38 * twips
.ForeColor = DarkBlue
.BorderStyle = 0
.BorderColor = DarkBlue
.FontName = "Arial"
.FontSize = 18
.FontWeight = 700 ' Bold
.FontItalic = True
.FontUnderline = True
End With
DoCmd.OpenForm hFrm.Name, acNormal
CreateHeading_Exit:
Exit Function
CreateHeading_Err:
MsgBox Err & ": " & Err.Description, , "CreateHeading()"
Resume CreateHeading_Exit
End Function
</pre>
<p>There is a separate Command Button Instance with the name <b>cmdFinish</b> created in the Intermediate Class Module <b>FWizObject_Init </b>to run all the Wizard-related Functions placed in the main Class Module. The cmdFinish_Click() Event Procedure runs the Wizard Functions.</p>
<pre>Private Sub cmdFinish_Click()
xtyp = fom!WizList
strFile = fom!FilesList
If xtyp = 1 Then
Columns strFile
Else
Tabular strFile
End If
DoCmd.Close acForm, fom.Name 'Closes the Wizard Form.
End Sub
</pre>
<p>Sample Form Images, both Column and Tabular Forms are created, using the Categories Table given below.</p>
<h4>Wizard Created Form in Column Format.</h4>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgNmRsViqVg7JRtaD_fSPHUKimtrTDInUFh0AcJ5Abp31cHWK72ivP_pylxFSIlQwpHf3ooVLKvgsozzAplfhem9JgtjWzbAWieOj5hGt8kLsIofVpDfJ0e8jQQVJI3WNZfyoRXkj3wHW80M28h-zmuzsGy4UhgX0N6I24QZOn6bLueTVe12SYOeoSgC-j8/s744/CategoriesColumns.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="416" data-original-width="744" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgNmRsViqVg7JRtaD_fSPHUKimtrTDInUFh0AcJ5Abp31cHWK72ivP_pylxFSIlQwpHf3ooVLKvgsozzAplfhem9JgtjWzbAWieOj5hGt8kLsIofVpDfJ0e8jQQVJI3WNZfyoRXkj3wHW80M28h-zmuzsGy4UhgX0N6I24QZOn6bLueTVe12SYOeoSgC-j8/s320/CategoriesColumns.png" width="320" /></a></div>
<h3 style="text-align: left;">Wizard Form in Tabular Format with Categories Table.</h3>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhxrrW67GeqmW69b3lLbWj8skMmhaMv_jw55O_bpXC6M2YdMfPAySTBiWvq4_MTiBYzH_HcJQ3oRzB2PTYRNVFHmLHLcoShbrwfKXGzSni6XN3HhIHA807u1OO3W2-lhL_XNcRqlNpUsRsX3yX_uRC5RZygnHstlA3kdTU3tJzXx4gSQIq703yT0tEzR6xL/s772/CategoriesTabular.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="442" data-original-width="772" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhxrrW67GeqmW69b3lLbWj8skMmhaMv_jw55O_bpXC6M2YdMfPAySTBiWvq4_MTiBYzH_HcJQ3oRzB2PTYRNVFHmLHLcoShbrwfKXGzSni6XN3HhIHA807u1OO3W2-lhL_XNcRqlNpUsRsX3yX_uRC5RZygnHstlA3kdTU3tJzXx4gSQIq703yT0tEzR6xL/s320/CategoriesTabular.png" width="320" /></a></div>
<p>The Tabular Form is created with fixed-width Fields and needs to be modified with the required width of each Field. </p><h3 style="text-align: left;">The Command Buttons Wrapper Class: FWiz_CmdButton VBA Code.</h3><p>The <a href="https://www.msaccesstips.com/2009/01/command-button-animation-2.html" target="_blank">Command Button</a> Wrapper Class Module <b>FWiz_CmdButton</b> VBA Code is given below for your Reference. Other Related Wrapper Classes contain a few lines of Event Procedure VBA Code you may open the Class Modules and study them from the attached Demo Database.</p>
<pre>Option Compare Database
Option Explicit
Private WithEvents cmd As CommandButton
Private frm As Form
Dim DarkBlue As Long, twips As Long, xtyp As Integer, strFile As String
Public Property Get w_Frm() As Form
Set w_Frm = frm
End Property
Public Property Set w_Frm(ByRef wFrm As Form)
Set frm = wFrm
End Property
Public Property Get w_cmd() As CommandButton
Set w_cmd = cmd
End Property
Public Property Set w_cmd(ByRef wcmd As CommandButton)
Set cmd = wcmd
End Property
Private Sub cmd_Click()
Dim lblInfo As String
Select Case cmd.Name
Case "cmdCancel2"
DoCmd.Close acForm, frm.Name
Case "cmdNext"
If frm.SelList.listcount = 0 Then
frm.cmdForm.Enabled = False
Else
frm.cmdForm.Enabled = True
End If
lblInfo = "Table/Query: " & frm!FilesList
If frm!WizList = 1 Then
lblInfo = lblInfo & " - Columnar Form."
Else
lblInfo = lblInfo & " - Tabular Form."
End If
frm!info.Caption = lblInfo
Call SelectTable
Case "cmdCancel"
DoCmd.Close acForm, frm.Name
Case "cmdRight"
RightAll 1
Case "cmdRightAll"
RightAll 2
Case "cmdLeft"
LeftAll 1
Case "cmdLeftAll"
LeftAll 2
Case "cmdBack"
frm.FilesList.RowSource = "WizQuery"
frm.Page1.Visible = True
frm.Page1.SetFocus
frm.Page2.Visible = False
End Select
End Sub
Private Sub SelectTable()
Dim vizlist As ListBox, lcount As Integer, chkflag As Boolean
Dim FildList As ListBox, strName As String, strRSource As String
Dim cdb As Database, doc As Document
Dim Tbl As TableDef, Qry As QueryDef, QryTyp As Integer
Dim flag As Byte, FieldCount As Integer, j As Integer
Set vizlist = frm.WizList
lcount = vizlist.listcount - 1
chkflag = False
For j = 0 To lcount
If vizlist.Selected(j) = True Then
xtyp = j + 1
chkflag = True
End If
Next
If IsNull(frm![FilesList]) = True Then
MsgBox "Select a File from Table/Query List.", vbOKOnly + vbExclamation, "cmdNext"
frm.WizList.Selected(0) = True
Else
strFile = frm.FilesList
frm.Page2.Visible = True
frm.Page2.SetFocus
frm.Page1.Visible = False
Set cdb = CurrentDb
flag = 0
For Each Tbl In cdb.TableDefs
If Tbl.Name = strFile Then
flag = 1
End If
Next
For Each Qry In cdb.QueryDefs
If Qry.Name = strFile Then
flag = 2
End If
Next
If flag = 1 Then
Set Tbl = cdb.TableDefs(strFile)
Set FildList = frm.FldList
strRSource = ""
FieldCount = Tbl.Fields.Count - 1
For j = 0 To FieldCount
If Len(strRSource) = 0 Then
strRSource = Tbl.Fields(j).Name
Else
strRSource = strRSource & ";" & Tbl.Fields(j).Name
End If
Next
ElseIf flag = 2 Then
Set Qry = cdb.QueryDefs(strFile)
strRSource = ""
FieldCount = Qry.Fields.Count - 1
For j = 0 To FieldCount
If Len(strRSource) = 0 Then
strRSource = Qry.Fields(j).Name
Else
strRSource = strRSource & ";" & Qry.Fields(j).Name
End If
Next
End If
frm.FldList.RowSource = strRSource
frm.FldList.Requery
End If
End Sub
Private Function RightAll(ByVal SelectionType As Integer)
Dim FldList As ListBox, SelctList As ListBox, strRSource As String
Dim listcount As Long, j As Long, strRS2 As String
On Error GoTo RightAll_Err
If SelectionType = 0 Then
Exit Function
End If
Set FldList = Forms("FormWizard").FldList
Set SelctList = Forms("FormWizard").SelList
listcount = FldList.listcount - 1
strRSource = SelctList.RowSource: strRS2 = ""
Select Case SelectionType
Case 1
For j = 0 To listcount
If FldList.Selected(j) = True Then
If Len(strRSource) = 0 Then
strRSource = FldList.ItemData(j)
Else
strRSource = strRSource & ";" & FldList.ItemData(j)
End If
Else
If Len(strRS2) = 0 Then
strRS2 = FldList.ItemData(j)
Else
strRS2 = strRS2 & ";" & FldList.ItemData(j)
End If
End If
Next
SelctList.RowSource = strRSource
FldList.RowSource = strRS2
SelctList.Requery
FldList.Requery
frm.cmdForm.Enabled = True
Case 2
For j = 0 To listcount
If Len(strRSource) = 0 Then
strRSource = FldList.ItemData(j)
Else
strRSource = strRSource & ";" & FldList.ItemData(j)
End If
Next
SelctList.RowSource = strRSource
FldList.RowSource = ""
SelctList.Requery
FldList.Requery
frm.cmdForm.Enabled = False
End Select
frm.cmdForm.Enabled = True
RightAll_Exit:
Exit Function
RightAll_Err:
MsgBox Err.Description, , "RightAll"
Resume RightAll_Exit
End Function
Private Function LeftAll(ByVal SelectionType As Integer)
Dim FldList As ListBox, SelctList As ListBox, strRSource As String
Dim listcount As Long, j As Long, strRS2 As String
On Error GoTo LeftAll_Err
If SelectionType = 0 Then
Exit Function
End If
Set FldList = Forms("FormWizard").FldList
Set SelctList = Forms("FormWizard").SelList
listcount = SelctList.listcount - 1
strRSource = FldList.RowSource: strRS2 = ""
Select Case SelectionType
Case 1
For j = 0 To listcount
If SelctList.Selected(j) = True Then
If Len(strRSource) = 0 Then
strRSource = SelctList.ItemData(j)
Else
strRSource = strRSource & ";" & SelctList.ItemData(j)
End If
Else
If Len(strRS2) = 0 Then
strRS2 = SelctList.ItemData(j)
Else
strRS2 = strRS2 & ";" & SelctList.ItemData(j)
End If
End If
Next
SelctList.RowSource = strRS2
FldList.RowSource = strRSource
SelctList.Requery
FldList.Requery
If SelctList.listcount = 0 Then
frm.cmdForm.Enabled = False
End If
Case 2
For j = 0 To listcount
If Len(strRSource) = 0 Then
strRSource = SelctList.ItemData(j)
Else
strRSource = strRSource & ";" & SelctList.ItemData(j)
End If
Next
SelctList.RowSource = ""
FldList.RowSource = strRSource
SelctList.Requery
FldList.Requery
If SelctList.listcount = 0 Then
frm.cmdForm.Enabled = False
End If
End Select
LeftAll_Exit:
Exit Function
LeftAll_Err:
MsgBox Err.Description, , "LeftAll"
Resume LeftAll_Exit
End Function
</pre>
<h3>Demo Database Download Link:</h3>
<!--Download Link /downloads/2023/10/FormWizard1.zip-->
<div class="separator" style="clear: both; text-align: center;">
<a href="https://drive.google.com/file/d/1bD0oPeGDuPP3PXdWoUfil8fPGdMvyhpA/view?usp=sharing">
<img border="0" data-original-height="29" data-original-width="22" height="29" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFE4uDLH9dk3S6pEcfTkT6GW6r11XqBI4HQnLnhQFZMKFeeQt5ABV_NqvLHM68J7RacISsvbUEl0PlAq6V7i0t0mQF3iesrBuqGuvrf5CJV72OzrGZ7rdMxkxzw8FYlz-qniNySoqtqUN4/s320/zip.gif" width="22" />FormWizard1.zip</a>
</div>
<!--Download Link /downloads/2023/10/FormWizard1.zip-->
<br />
<h3>Streamlining Form Module Code in Standalone Class Module.</h3>
<ol>
<li><a href="https://www.msaccesstips.com/2023/04/reusing-form-module-vba-code-for-new.html" target="_Blank">Re-using Form Module VBA Code for New Projects.</a></li>
<li><a href="https://www.msaccesstips.com/2023/04/streamlining-form-module-code-part-two.html" target="_Blank">Streamlining Form Module Code Part-Two</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/05/streamlining-form-module-code-part-three.html" target="_Blank">Streamlining Form Module Code Part-Three</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/06/streamlining-form-module-code-part-four.html" target="_Blank">Streamlining Form Module Code Part-Four</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/06/streamlining-form-module-code-part-five.html" target="_Blank">Streamlining Form Module Code Part-Five</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-six.html" target="_Blank">Streamlining Form Module Code Part-Six</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-seven.html" target="_Blank">Streamlining Form Module Code Part-Seven</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-eight.html" target="_Blank">Streamlining Form Module Code Part-Eight</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part-nine.html" target="_Blank">Streamlining Form Module Code Part-Nine</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part-ten.html" target="_Blank">Streamlining Form Module Code Part-Ten</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part.html" target="_Blank">Streamlining Form Module Code Part-Eleven</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-report-module-code-in.html" target="_Blank">Streamlining Report Module Code in Class Module</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-report-module-in-class.html" target="_Blank">Streamlining Report Module Code in Class Module-2</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-form-module-code-part-14.html" target="_Blank">Streamlining Form Module Code Part-14:All Controls</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-custom-made-form-wizard.html" target="_Blank">Streamlining Custom Made Form Wizard-15</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-form-vba-custom-report.html" target="_Blank">Streamlining Custom Report Wizard-16</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-form-vba-external-files.html" target="_Blank">Streamlining Form VBA External File Browser-17</a></li>
<li><a href="https://www.msaccesstips.com/2023/11/streamlining-event-procedures.html" target="_Blank">Streamlining Event Procedures of 3D TextWizard-18</a></li>
<li><a href="https://www.msaccesstips.com/2023/11/streamlining-event-procedures-rgbcolor.html" target="_Blank">Streamlining Event Procedures RGB Color Wizard-19</a></li>
<li><a href="https://www.msaccesstips.com/2023/12/streamlining-numbers-to-words-converter.html" target="_Blank">Streamlining Event Procedures Number To Words-20</a></li>
</ol>
a.p.r. pillaihttp://www.blogger.com/profile/02627590161919120214noreply@blogger.com0tag:blogger.com,1999:blog-7457417942281874810.post-31110128470677043052023-09-30T21:17:00.016+05:302024-01-27T07:26:29.864+05:30Streamlining Form Module Code Part 14<h3 style="text-align: left;">All Frequently Used Controls on a Form.</h3><p>We learned how to capture the Event fired from the Form or Report Controls and write the Event Subroutine VBA Code in the Control Wrapper Classes. The control Instances created in the Wrapper Class are assigned with the References of the Controls on the Form. Besides that, any other control on the Form is accessible from the Wrapper Class, to save or retrieve their values. Run Animations from Wrapper Class on the Form, using other controls or their content, mimic a digital clock on the Form, run a countdown before closing the Form and so on. </p><p>For this kind of activities, we always include a Form Object Instance in all the Wrapper Classes and assign the active Form reference to the Form object Instance. For running a Form_Timer() Event or any other Form Event Procedure like MouseMove on the Detail Section of the Form the Form Instance declaration must be qualified with the keyword <b>WithEvents</b> in the Wrapper Class. Any of the above mentioned activities can be run from any Control's Wrapper Class, because the Form object Instance is included. You can declare it with or without the WithEvents keyword. But if you need to capture the Form Event then declare it with the keyword WithEvents.</p><p>The Form Detail Section declaration is as given below:</p>
<!--Google In-Article Ads start-->
<script async="" src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle" data-ad-client="ca-pub-2376623373770548" data-ad-format="fluid" data-ad-layout="in-article" data-ad-slot="6147807460" style="display: block; text-align: center;"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
<!--Google In-Article Ads END-->
<pre>Private frm as Access.Form
Private WithEvents SecDetail As Access.Section
Set SecDetail = frm.Section(acDetail)
SecDetail.OnMouseMove = "[Event Procedure]"
</pre>
<p>Report Detail Section Declaration and Reference assignments are like the following:</p>
<pre>Private WithEvents Rpt as Access.Report
Private WithEvents RptSec as Access.[_SectionInReport]
Set RptSec = Rpt.Section(acDetail)
</pre>
<p>We could keep the Event Subroutine VBA Code away from the Form and Report Modules and work with the VBA Code independently in the standalone Class Module. The Collection object plays a major role in keeping several instances of the same type of object in memory after enabling them with required Events, like the <a href="https://www.msaccesstips.com/2008/10/textbox-and-label-inner-margins.html" target="_blank">TextBox</a> on the Form, without interfering with the Form Design task. </p><p>The earlier <a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-seven.html" target="_blank">Episode Number Seven</a> is a classic example that shows the power of this new approach of Form/Report Module Coding in the Standalone Class Module, which automates a major part of the manual Coding work with a single GotFocus() and LostFocus() pair for any number of TextBoxes added on the Form. Similarly, if there are ten TextBoxes on the Form that need the <b>AfterUpdate()</b> Event Subroutines with different Validation requirements for each of them, then all of them can be written in a single AfterUpdate() Event Subroutine in the stand-alone Class Module. This rule is applicable to all Types of Controls on the Form as well.</p><p>The Event Procedure Codes are better organised in Wrapper Classes, easily reachable for maintenance, debugging without interfering with the Form design. </p>
<pre>Private Sub Txt_AfterUpdate()
Select Case Txt.Name
Case "Quantity"
'Code
Case "UnitPrice"
'Code
Case "TaxRate"
'Code
Case . . .
End Select
End Sub
</pre>
<p>If any of the TextBox's AfterUpdate Events need any change to the Code or any other new TextBox needs to implement the AfterUpdate Event Subroutine then no need to open the Form in Design View, select the TextBox Control, display the <a href="https://www.msaccesstips.com/2008/09/source-connect-str-property-and-odbc.html" target="_blank">Property</a> Sheet, look for the Event Property and click on the build . . . Button to open the specific Event Subroutine to write/modify the Code. All we need to do is to open the Class Module directly and make changes. Imagine how much time is wasted in the traditional way of Coding on each Control Type in the Form, attending more than once for the same control Event Subroutine in this way.</p><p>I am sure you will know the difference once you set your mind to pursue the new way of Coding technique, even though it takes a bit of time to get oriented with the new concept if you are a beginner in VBA Coding.</p>
<p>The Form Image given below is designed with some of the Controls designed in the earlier Episodes and the Wrapper Class Modules are Imported for running in this Form's Control Event Procedures. <b>Animations</b> of Text, a <b>Digital Clock</b>, and a <b>Count-down display</b> before closing the Form are also included in the Class Module to run on the Form. </p><p>You can do anything from the stand-alone Class Module as you do normally in the Form Module. Once you set your mind to do things differently, effortlessly, and better organized with an outlook to reuse the VBA Code with or without minor changes then you will know the difference.</p><p>Anything for the first time is difficult, that's how we all started learning VBA Coding, spending many minutes or hours on trial and error basis for solving a problem. I retired from service 13 years ago and still trying to learn new things and share with others. </p><p>New technology emerges every day and even kids aged 10 to 15 years of age (lucky kids) started to do Coding practices and App developments! </p><p>I was exposed to some form of Computer-related automation only at the age of 27. The Key Punch Machine's Panel Wiring task for automatically skipping or duplicating some columns of 80 column Punch Card, the input medium of Programs and Data at that time, in the Indian Navy Defence Establishment. The Main-Frame Computer was an ICL 1901 Machine with Disk Drives, the Programming Languages were COBOL and FORTRAN, and I learned a bit of COBOL Language at that time (1975) too. </p><p>If you need some Class Module beginner lessons they are available starting with this Link: <a href="https://www.msaccesstips.com/2018/10/ms-access-class-module-and-vba.html" target="_blank">MS-Access Class Modules and VBA</a> series of few Articles.</p><h3 style="text-align: left;">The Demo Database With All Frequently Used Controls.</h3><p>The attached sample Demo Database contains the following Form, the download link is available at the end of this page, and the main Form (<b>frmControls_All</b>) Image is given below.</p>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEizIVywqhfzoizcEIh_banRi273XyTD6qiaOwaUHkyS7f6ANpjas8lNOXMwUwMtuTthf2PQEbEANVWNgg4D8wOZBAqi41PIXfr5UQqcNEsbAeKUWIP2aq-SANmHiua_mNXI1k95xGsD7zm7D3CrKXsdPhH_hc1UCYvPSzFNk0cme6cETBk4TZwodz7tX62s/s1015/StremlineAllForm.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="831" data-original-width="1015" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEizIVywqhfzoizcEIh_banRi273XyTD6qiaOwaUHkyS7f6ANpjas8lNOXMwUwMtuTthf2PQEbEANVWNgg4D8wOZBAqi41PIXfr5UQqcNEsbAeKUWIP2aq-SANmHiua_mNXI1k95xGsD7zm7D3CrKXsdPhH_hc1UCYvPSzFNk0cme6cETBk4TZwodz7tX62s/s320/StremlineAllForm.png" width="320" /></a></div>
<p> The top left side TabControl-based Menu has three layers of options on three Tab Pages. The Tab Page Style is hidden by selecting its Property setting to <b>None.</b> The Menu Pages are selected with separate Command Button Clicks. The TabPagee change Event will be fired on the Command Button Clicks and will display a message with the Page Index Number. </p><p>The three Menus are Tables, Forms, and Reports. The TabControl along with the Menu selection <a href="https://www.msaccesstips.com/2008/04/transparent-command-button.html" target="_blank">Command Buttons</a> were Copied from the earlier episode Form and Pasted on this Form and made some changes to the Menu Options to replace with some other Forms and Reports presented in the earlier episodes. The related Class Modules are Imported to run them in this Database. </p>
<h3>Imported Class Modules of TabControl and the Menu changing Command Buttons.</h3>
<p>The following Class Modules were Imported into this Database, from an earlier episode, to run the Menu options with a few changes:</p>
<pre>TabLst_Object_Init
TabLst_CmdButton
TabLst_ListBox
TabLst_TabCtl
</pre>
<p>The Tab Page with the name TABLES has three options Employees, Orders, and Customer Tables. Double-click on an Item to open it. Before opening the Table a Female Voice will announce the Table Name before it appears on the Screen.</p><h3 style="text-align: left;">Employees and Orders Form.</h3><p>On the <b>FORMS</b> TabPage, the first option is the <i><b>Employees</b></i> Form with Orders SubForm. It has a Search and Find TextBox in the Footer Section of the Employees Form. The EmployeeID number is the Key for Search operations. If the Search was successful/or failed then a Label will flash a few seconds announcing the search operations result. Try the EmployeeID number above 9 to test the Failed result.</p>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhsWzzCeERosZpMx23U3LOOcmlP6isgTxN8zCE2c1kI4seGTtyGG0s2p-slxp03ozLxfSAsLVnmTV2f8CyWh3_ApsF8btnv9chOlLkEGwq0gwsJBIgJXRnQiNAzz3UWbkMymGLFHp-GTkEV2LduTDKpSS38NK1lWP7irbmjTRxzniXy9_PpCp-73LlMlrmo/s722/EmployeeOrders1.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="629" data-original-width="722" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhsWzzCeERosZpMx23U3LOOcmlP6isgTxN8zCE2c1kI4seGTtyGG0s2p-slxp03ozLxfSAsLVnmTV2f8CyWh3_ApsF8btnv9chOlLkEGwq0gwsJBIgJXRnQiNAzz3UWbkMymGLFHp-GTkEV2LduTDKpSS38NK1lWP7irbmjTRxzniXy9_PpCp-73LlMlrmo/s320/EmployeeOrders1.png" width="320" /></a></div>
<p><b>Note: </b><i>You may try to implement the Female Voice to announce the success or failure of the search operation.</i></p>
<p>The above Form and its VBA Codes are running from the following Class Modules, with the <b>Emp</b> Prefixes:</p>
<pre style="text-align: left;">EmpObject_Init
EmpCmdButton
EmpTextBox
EmpCombo
</pre>
<h3 style="text-align: left;">The Employee-wise Orders Freight Sales Analysis.</h3><p>The second option on the <b>Forms Menu</b> is the Employee-wise Orders Freight-Sales Analysis Form (<i><b>frm_OptionGroup2) </b></i>with a Graph Chart. The Form has an Option Group Control with three Options; 1. <i>Highest Freight Sales Value</i>, 2. <i>Lowest Freight Sales Value</i>, and 3. <i>Total Freight Sales Value</i>. The result is displayed in a TextBox with an Animated Label Caption; moving Text from Right-to-Left.</p>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg1hS1Vd7ZZnSevBzG2qSBiyS-HIu-mKOlqL6pVIMNda-ael-5R96YbsX_M8qXOWrDttqufgeRTIXFwvUh7t_snsofyLbbYqmvV2gNwtyDT95LYJtD48aTmtGANRgaIT-6wUwtA4LxvWjPfcgcHgkCdZKWNXEaAJn7_ttXg2xBnSYt-HxMETtMzrTRgrNCf/s928/OptionGrpFrm.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="661" data-original-width="928" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg1hS1Vd7ZZnSevBzG2qSBiyS-HIu-mKOlqL6pVIMNda-ael-5R96YbsX_M8qXOWrDttqufgeRTIXFwvUh7t_snsofyLbbYqmvV2gNwtyDT95LYJtD48aTmtGANRgaIT-6wUwtA4LxvWjPfcgcHgkCdZKWNXEaAJn7_ttXg2xBnSYt-HxMETtMzrTRgrNCf/s320/OptionGrpFrm.png" width="320" /></a></div>
<p>The above form is driven by the two standalone Class Modules given below. Both the Form and Class Modules are imported from one of the earlier Episodes:</p>
<pre>Opt_Object_Init
Opt_Frame2
</pre>
<p></p><p>The Option Group Subroutine VBA Code runs from the <b>Opt_Frame2</b> Class Module. The Employee <a href="https://www.msaccesstips.com/2008/03/refresh-dependant-combo-box-contents.html" target="_blank">ComboBox</a>, the Freight Value display TextBox, and the Command Button Click Event Subroutines are run from the <i>Opt_Object_Init</i> Class Module only. Separate Wrapper Classes are not created for them because the Control has only one Instance on the Form.</p>
<!--Google In-Article Ads start-->
<script async="" src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle" data-ad-client="ca-pub-2376623373770548" data-ad-format="fluid" data-ad-layout="in-article" data-ad-slot="6147807460" style="display: block; text-align: center;"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
<!--Google In-Article Ads END-->
<h3 style="text-align: left;">The Graph Chart on the Form.</h3><p>The Graph Chart's Source Data is from <i>OrderSummaryQ2</i> Query. The Source Data of this Query is taken from the <i>OrderDetailQ2</i> Query. The OrderDetailQ2 data is filtered from the Orders Table using the EmployeeID selection in the ComboBox with the name <b>cboEmp. </b> There is a hidden TextBox on the Form with the name <b>EID </b>containing<b> </b>the expression: <b>=[cboEmp]</b> to copy the EmployeeID selected in the cboEmp ComboBox. This Value is used in the <i>Link Master Field</i> Property of the Graph Chart to reflect the change of Chart Value on EmployeeID selection in the cboEmp ComboBox.</p><p>The TabControl Page number three has three Report Options; 1. Employee Records, 2. <i>Students Exam Failed cases</i> highlighting, 3. <i>Students Passed/Failed Listing</i> from the same Report. </p><p>The last two Reports and their Class Modules are Imported into this <a href="https://www.msaccesstips.com/2008/05/database-daily-backup.html" target="_blank">Database</a> from earlier Episodes. The following Stand-alone Class Modules run the <a href="https://www.msaccesstips.com/2023/04/reusing-form-module-vba-code-for-new.html" target="_blank">Event</a> Subroutine VBA Code:</p>
<pre>ClsStudentHighlight0
ClsStudentsList
</pre>
<p>The <i>ClsStudentHighlight0</i> Class Module runs the <b>Detail_Print</b> Event Subroutine Code for the Report <b>StudentsHighlight_Class0. </b></p><h3 style="text-align: left;"><b>The Report Image is given below:</b></h3>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhlwlWH-lFSeJej8ENULRimEw0izU__pTV-VU_sbuY293w7H2iTi4H_brs0ogPInjyM9u5EBLak4H0QcRt7DsMWQVqUcrYVPhUmICeXRDtFmnVYAXfzSV83GjnQo6cEmeM87OSrb6uF8DnwB7kumobHR5xdVEAknxdh5R-Itd6CtEsNUG6Svwg6tvhc4_Gj/s973/StreamReport1.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="Streamline Report Image" border="0" data-original-height="765" data-original-width="973" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhlwlWH-lFSeJej8ENULRimEw0izU__pTV-VU_sbuY293w7H2iTi4H_brs0ogPInjyM9u5EBLak4H0QcRt7DsMWQVqUcrYVPhUmICeXRDtFmnVYAXfzSV83GjnQo6cEmeM87OSrb6uF8DnwB7kumobHR5xdVEAknxdh5R-Itd6CtEsNUG6Svwg6tvhc4_Gj/s320/StreamReport1.png" width="320" /></a></div>
<p>The <i>ClsStudentsList</i> Class Module-based <b>Detail_Format()</b> and <b>Report_Page()</b> Event Subroutines are run and the <b>PageBorder()</b> Function is called from the Standard Module to draw two Report Page Border lines on each page of the Report. The ellipse is drawn around the TextBox in the Detail_Print() Event of the Report.</p>
<h3>The Students Exam Passed List Image is given below.<br /></h3>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj8XVz8gFuou28QyOqdXNTxd_DTYFkTfVKOpYKCh4xvRr0ND-zroGDOYSrys24yUfTKklUuaDlH_4jCCcuxsC2MxodA2IcVfQZ71y4KXEFaBHxRhJt-ig97TAsDVpQSoOr4HreAIBl27j4YVws6XDnz51sCLxACjDhBhlby_bbfo-aeFUs430EGufBci7l9/s936/ReportFormat.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="644" data-original-width="936" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj8XVz8gFuou28QyOqdXNTxd_DTYFkTfVKOpYKCh4xvRr0ND-zroGDOYSrys24yUfTKklUuaDlH_4jCCcuxsC2MxodA2IcVfQZ71y4KXEFaBHxRhJt-ig97TAsDVpQSoOr4HreAIBl27j4YVws6XDnz51sCLxACjDhBhlby_bbfo-aeFUs430EGufBci7l9/s320/ReportFormat.png" width="320" /></a></div>
<p>The Exam Failed Cases Listing is taken from the same Report. Both these Report listings are prepared, by hiding the Report Detail Section for those records that don't meet the Criteria, in the Detail_Format() Event. </p>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjxmILD3Vwein6HFKXk0VY6krYVmCin8v8HF28vKLClJbuXCnLXx1XiFjGiDQv7G9F61qyrBsPm4fbhD3m482wc6CQ5dBnfl_GgBQ0nVdTVrqKaYNJNnD4g8wWKlbLWZXHVwLs7EMQiiqAzeMaRgChgBF8iR2F0BwuD204TgVDAVOb8tbqh3pOiQ4yxdZbH/s928/ReportFormat2.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="688" data-original-width="928" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjxmILD3Vwein6HFKXk0VY6krYVmCin8v8HF28vKLClJbuXCnLXx1XiFjGiDQv7G9F61qyrBsPm4fbhD3m482wc6CQ5dBnfl_GgBQ0nVdTVrqKaYNJNnD4g8wWKlbLWZXHVwLs7EMQiiqAzeMaRgChgBF8iR2F0BwuD204TgVDAVOb8tbqh3pOiQ4yxdZbH/s320/ReportFormat2.png" width="320" /></a></div>
<h3 style="text-align: left;">The SubForm on the Main Form.</h3><p>Next, there is a SubForm with three TextBoxes. The Quantity TextBox accepts a value range of 1 to 10 only. The Unit Price Value should be non-zero. The entered values in these TextBoxes are validated in the <b>OnExit()</b> Event Subroutine in the <b>ClsText</b> Class Module before accepting them into the TextBoxes. The OnGotFocus() Event Subroutine of the TotalPrice TextBox calculates the Total Price value (Quantity * UnitPrice) and inserts it into the TotalPrice TextBox. The result value is displayed in the Label Control above the SubForm too. </p><p>The SubForm is introduced here to demonstrate how to scan for the SubForm Controls and enable their required Events in the Class_Init() Subroutine (in the <b>ClsControls_All</b> Class Module). There are two For . . . Next Loop, one starts with the statement:</p>
<pre>For Each ctl In <b>fom</b>.<b>mySubForm.Form</b>.Controls
</pre>
<p>for the SubForm scanning and the other starts with:</p>
<pre>For Each ctl In <b>fom</b>.Controls</pre>
<p>to scan for the required controls and enable their Event Procedures. </p><p><b>Note:</b> If you place any TextBox on the TabControl Pages they will be treated as controls on the Form only and see that their names do not conflict with the other TextBox Names on the Form.</p><p>The ComboBox on the Form has a list of Country names. When a Country Name is selected in the ComboBox its list index number is used for selecting the Capital of that Country from the ListBox automatically. If you Click on an item in the ListBox it will display the selected value in a MsgBox to indicate that the Click Event is captured in the Class Module.</p><h3 style="text-align: left;">The Option Group Control.</h3><p>Next, the Option Group Control is not connected with any specific function except when one of the Radio Buttons is clicked it will display the index number of the option as an indication that the selected Event is fired and captured in the Option Group Class Module <b>ClsOption</b>. </p><p>The <a href="https://www.msaccesstips.com/2012/05/deleting-folders-with-dos-command.html" target="_blank">Command</a> Button (with the Caption Employee Orders) opens the Form with the Graph Chart (included in the TabControl Menu) directly. The next <a href="https://www.msaccesstips.com/2006/09/command-button-animation.html" target="_blank">Command Button</a> with the caption 'Heading Color' changes the Form Heading top Label Control's ForeColor.</p><p>The Close Command Button Click runs a 10 Seconds Count-Down on the Label Control above the SubForm. When it is zero the Form <b>frmControls_All </b>will close.</p><p>The Main Form-based control's Class Module names start with the prefix <b>Cls...</b></p><p>You can see the List of these Class Modules in the declaration area of the <b>ClsControlls_All</b> Class module.</p>
<pre>Private tx As ClsText
Private cmd As ClsCmdButton
Private cbo As Clscombo
Private Lst As ClsListBox
Private Opt As ClsOption
</pre>
<h4>Demo Database Download Link:</h4>
<!--Download Link /downloads/2023/09/MixAllControls.zip.zip-->
<div class="separator" style="clear: both; text-align: center;">
<a href="https://drive.google.com/file/d/1HX-CdfeRtgjMRRN2Zl8VbzGSdduMaWFg/view?usp=sharing">
<img border="0" data-original-height="29" data-original-width="22" height="29" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFE4uDLH9dk3S6pEcfTkT6GW6r11XqBI4HQnLnhQFZMKFeeQt5ABV_NqvLHM68J7RacISsvbUEl0PlAq6V7i0t0mQF3iesrBuqGuvrf5CJV72OzrGZ7rdMxkxzw8FYlz-qniNySoqtqUN4/s320/zip.gif" width="22" />MixAllControls.zip</a>
</div>
<!--Download Link /downloads/2023/09/MixAllControls.zip-->
<br />
<h3>Streamlining Form Module Code in Standalone Class Module.</h3>
<ol>
<li><a href="https://www.msaccesstips.com/2023/04/reusing-form-module-vba-code-for-new.html" target="_Blank">Re-using Form Module VBA Code for New Projects.</a></li>
<li><a href="https://www.msaccesstips.com/2023/04/streamlining-form-module-code-part-two.html" target="_Blank">Streamlining Form Module Code Part-Two</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/05/streamlining-form-module-code-part-three.html" target="_Blank">Streamlining Form Module Code Part-Three</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/06/streamlining-form-module-code-part-four.html" target="_Blank">Streamlining Form Module Code Part-Four</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/06/streamlining-form-module-code-part-five.html" target="_Blank">Streamlining Form Module Code Part-Five</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-six.html" target="_Blank">Streamlining Form Module Code Part-Six</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-seven.html" target="_Blank">Streamlining Form Module Code Part-Seven</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-eight.html" target="_Blank">Streamlining Form Module Code Part-Eight</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part-nine.html" target="_Blank">Streamlining Form Module Code Part-Nine</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part-ten.html" target="_Blank">Streamlining Form Module Code Part-Ten</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part.html" target="_Blank">Streamlining Form Module Code Part-Eleven</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-report-module-code-in.html" target="_Blank">Streamlining Report Module Code in Class Module</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-report-module-in-class.html" target="_Blank">Streamlining Report Module Code in Class Module-2</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-form-module-code-part-14.html" target="_Blank">Streamlining Form Module Code Part-14:All Controls</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-custom-made-form-wizard.html" target="_Blank">Streamlining Custom Made Form Wizard-15</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-form-vba-custom-report.html" target="_Blank">Streamlining Custom Report Wizard-16</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-form-vba-external-files.html" target="_Blank">Streamlining Form VBA External File Browser-17</a></li>
<li><a href="https://www.msaccesstips.com/2023/11/streamlining-event-procedures.html" target="_Blank">Streamlining Event Procedures of 3D TextWizard-18</a></li>
<li><a href="https://www.msaccesstips.com/2023/11/streamlining-event-procedures-rgbcolor.html" target="_Blank">Streamlining Event Procedures RGB Color Wizard-19</a></li>
<li><a href="https://www.msaccesstips.com/2023/12/streamlining-numbers-to-words-converter.html" target="_Blank">Streamlining Event Procedures Number To Words-20</a></li>
</ol>
a.p.r. pillaihttp://www.blogger.com/profile/02627590161919120214noreply@blogger.com2tag:blogger.com,1999:blog-7457417942281874810.post-19282897465513615442023-09-23T08:39:00.015+05:302024-01-07T22:57:33.331+05:30Streamlining Report Module in Class Module-2<h3 style="text-align: left;"> Introduction.</h3><h3 style="text-align: left;">Hiding Report Lines Conditionally.</h3><p>Last week we saw that the highlighting of Report lines in the Report Print Event Subroutines can be moved into the standalone Class Module and execute the Code from there when the Report is PrintPreviewed or Printed. To highlight certain Report Line records that don't meet specific Marks criteria marked by drawing a red-colored ellipse around the TextBox. The traditional way of this procedure is Coded in the <b>Detail_Print</b> Event Subroutine in the Report Class Module. </p><h3 style="text-align: left;">The Report Detail_Format, Report_Page Events.</h3><p>The Report Detail Section Format Event runs before the Print Event. The Report Detail Format Event places the Data Records line-by-line in the Report Detail Section. The Report Header, Footer, Page Header, Footer, and Report Page Events also take place in the Report Formatting phase and if these Section Events are enabled we can write the Code in those Sections too.</p><p>Here, we will try some tricks in the <b>Detail_Format</b> Event of the Report and use the earlier Students' Exam Result listing differently, rather than mark the failed cases with an ellipse around the obtained mark percentage as we did in the earlier episode. With this new method, the Report will be printed in two different categories from the same Report. </p>
<!-- Google In-Article Ads start -->
<script async="" src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle" data-ad-client="ca-pub-2376623373770548" data-ad-format="fluid" data-ad-layout="in-article" data-ad-slot="6147807460" style="display: block; text-align: center;"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
<!-- Google In-Article Ads END -->
<p></p><ol style="text-align: left;"><li><p>Exam Passed Students Listing.</p></li><li>Exam Failed Students Listing. </li></ol><p>Both listings will be taken from the same Report based on one of the above option selections.</p><p>I know we can do this very easily with other means, like Passing the <i>OpenArgs</i> Value and filtering the Report Records. Or setting the Query Criteria to the Form-based TextBox Value on the Report launching Form. Then we will not know how we can do it through the Detail Section Format Event VBA Code. Learning to do something differently is fun and you will know you can do it more than one way when the need arises.</p><p>The Report Listing image for the Exam Passed Cases Option above is given below:</p>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj8XVz8gFuou28QyOqdXNTxd_DTYFkTfVKOpYKCh4xvRr0ND-zroGDOYSrys24yUfTKklUuaDlH_4jCCcuxsC2MxodA2IcVfQZ71y4KXEFaBHxRhJt-ig97TAsDVpQSoOr4HreAIBl27j4YVws6XDnz51sCLxACjDhBhlby_bbfo-aeFUs430EGufBci7l9/s936/ReportFormat.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="644" data-original-width="936" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj8XVz8gFuou28QyOqdXNTxd_DTYFkTfVKOpYKCh4xvRr0ND-zroGDOYSrys24yUfTKklUuaDlH_4jCCcuxsC2MxodA2IcVfQZ71y4KXEFaBHxRhJt-ig97TAsDVpQSoOr4HreAIBl27j4YVws6XDnz51sCLxACjDhBhlby_bbfo-aeFUs430EGufBci7l9/s320/ReportFormat.png" width="320" /></a></div>
<p>The Exam Failed Cases Listing.</p>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjxmILD3Vwein6HFKXk0VY6krYVmCin8v8HF28vKLClJbuXCnLXx1XiFjGiDQv7G9F61qyrBsPm4fbhD3m482wc6CQ5dBnfl_GgBQ0nVdTVrqKaYNJNnD4g8wWKlbLWZXHVwLs7EMQiiqAzeMaRgChgBF8iR2F0BwuD204TgVDAVOb8tbqh3pOiQ4yxdZbH/s928/ReportFormat2.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="688" data-original-width="928" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjxmILD3Vwein6HFKXk0VY6krYVmCin8v8HF28vKLClJbuXCnLXx1XiFjGiDQv7G9F61qyrBsPm4fbhD3m482wc6CQ5dBnfl_GgBQ0nVdTVrqKaYNJNnD4g8wWKlbLWZXHVwLs7EMQiiqAzeMaRgChgBF8iR2F0BwuD204TgVDAVOb8tbqh3pOiQ4yxdZbH/s320/ReportFormat2.png" width="320" /></a></div>
<p>The Report Launching Form Image.</p>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjIw4OUtEoWA_rDpb0FSwAF-eatDtQo3bQeFnVvaU4xbKO6DYqgzXgO9AfAfaCqQQ2uenbyiOqUFHofKLKXcs7MCXwq91bjt0WPW_d_SK9JUnSQoxRLHj-PnSXqwc0CE2XmVuaoQ9RodhNKW2l1ppvjQKzc4OEcAX3QX2YwwfJ4cYCjTfI_8v9ha1aMTz1O/s720/ReportFormatMenu.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="720" data-original-width="683" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjIw4OUtEoWA_rDpb0FSwAF-eatDtQo3bQeFnVvaU4xbKO6DYqgzXgO9AfAfaCqQQ2uenbyiOqUFHofKLKXcs7MCXwq91bjt0WPW_d_SK9JUnSQoxRLHj-PnSXqwc0CE2XmVuaoQ9RodhNKW2l1ppvjQKzc4OEcAX3QX2YwwfJ4cYCjTfI_8v9ha1aMTz1O/s320/ReportFormatMenu.png" /></a></div>
<p>The left-side Command Button launches the Report with the Normal Coding on the Report Module itself. The Report Page is drawn with a double-lined Border.</p><p>Since there are three Command Buttons on the <b>Main</b> Form we created a Wrapper Class with the name <b>ClsCmdButton</b> to launch the Reports from there. The ClsCmdButton Class VBA Code is listed below:</p>
<pre>Option Compare Database
Option Explicit
Private cmdfrm As Form
Private WithEvents cmdNor As CommandButton 'For Normal Report Module Coded Report launching
Private WithEvents cmdCls As CommandButton 'Report Module Code in Class Module
Private WithEvents cmdQuit As CommandButton'Close the Main Form
Dim Opt As Variant
Dim param As String
'------------------------------------------------------
'Streamlining Form Module Code
'in Stand-alone Class Modules
'------------------------------------------------------
'Command Button Events
'Author: a.p.r. pillai
'Date : 22/09/2023
'Rights: All Rights(c) Reserved by www.msaccesstips.com
'------------------------------------------------------
'Form's Property GET/SET Procedures
Public Property Get cmd_Frm() As Form
Set cmd_Frm = cmdfrm
End Property
Public Property Set cmd_Frm(ByRef cfrm As Form)
Set cmdfrm = cfrm
Call class_Init
End Property
Private Sub class_Init()
Const EP = "[Event Procedure]"
Set cmdNor = cmdfrm.cmdNormal
cmdNor.OnClick = EP
Set cmdCls = cmdfrm.cmdClass
cmdCls.OnClick = EP
Set cmdQuit = cmdfrm.cmdClose
cmdQuit.OnClick = EP
End Sub
Private Sub cmdNor_Click()
Dim RptOpt As Integer
RptOpt = ReportOption()
param = cmdfrm!Pass 'PassPercentage
param = param & "," & RptOpt ' and report option
If Nz(cmdfrm.Pass, 0) = 0 Then
MsgBox "Enter pass-Mark Percentage?" & vbCr & "e.g.: default 60"
Else
DoCmd.OpenReport "StudentsPassFail_Normal", acViewPreview, , , , param
End If
End Sub
Private Sub cmdCls_Click()
Dim RptOpt As Integer
RptOpt = ReportOption()
param = cmdfrm!Pass 'PassPercentage
param = param & "," & RptOpt ' and report option
If Nz(cmdfrm.Pass, 0) = 0 Then
MsgBox "Enter pass-Mark Percentage?" & vbCr & "e.g.: default 60"
Else
DoCmd.OpenReport "StudentsPassFail_Normal", acViewPreview, , , , param
End If
End sub
'Event Subroutines
Private Sub cmdQuit_Click()
If MsgBox("Close " & cmdfrm.Name & " Form?", vbYesNo + vbQuestion, "cmd_Click") = vbYes Then
DoCmd.Close acForm, cmdfrm.Name
Exit Sub
End If
End Sub
</pre>
<p>In the global declaration area, the Main Form and Command Button Instances are declared, along with two simple Variable declarations. The <b>Opt</b> Variable will hold the Report Type Option selection Value returned from a Standard Module Public Function <b>ReportOption()</b>. The <b>param</b> String Variable will be assigned with the Report <b>OpenArgs</b> input Values.</p><p>The report Option selection Menu is displayed from the ReportOption() Function in the Standard Module.</p>In the Form Object Set Property Procedure, the Class_Init() Subroutine is called. In the Class_Init() Subroutine the CommandButton Click Events are enabled.<p>In the CmdNor_Click() Subroutine the ReportOption() Function is called and the returned value is stored in the <b>RptOpt</b> Variable. The PassPercentage Marks value is retrieved from the Form is saved in the <b>param</b> Variable and the Report Option selection is also added into the Param variable both separated with a comma.</p><p>The Report is open in <b>PrintPreview</b> and the value in the param is passed as the Report <b>OpenArgs</b>.</p><p>The same steps are run for the CmdCls_Click() Subroutine that opens the StudentsPassFail_Class Report also.</p><p>The CmdQuit_Click() Event Subroutine Closes the Form.</p>
<h3>ReportOption Function VBA Code.</h3>
<pre>Option Compare Database
Option Explicit
'Report Option Selection
Public Function ReportOption() As Integer
Dim msg As String
msg = "1. Passed List" & vbCr & "2. Failed List"
Opt = 0
Do While Opt < 1 Or Opt > 2
Opt = InputBox(msg, "Report Options", 1)
If Opt = "" Then Opt = 0
Loop
ReportOption = Opt
End Function
</pre>
<h3>The Report StudentsPassFail_Normal Module VBA Code.</h3><p>The VBA Code in the Report Module, showing how it is done in the traditional way of Coding, is given below for reference.</p>
<pre>Option Compare Database
Option Explicit
Dim Opt As Variant
'---------------------------------------------------------
'Streamlining Form Module Code
'in Stand-alone Class Modules
'---------------------------------------------------------
'Sreamlining Report Module Code in Standalone Class Module
'Author: a.p.r. pillai
'Date : 22/09/2023
'Rights: All Rights(c) Reserved by www.msaccesstips.com
'---------------------------------------------------------
Private Sub Report_Load()
Dim x As Variant
x = Split(OpenArgs, ",")
Me!PassPercentage = Val(x(0))
Opt = Val(x(1))
End Sub
Private Sub Detail_Format(Cancel As Integer, FormatCount As Integer)
'Report Format Pass Calls this Subroutine
Dim Curval As Double
Dim pf As Double
Dim yn As Boolean
On Error GoTo Detail_Format_Err
pf = Me!PassPercentage
Curval = Me!Percentage 'Student's Marks percentage
yn = (Curval >= pf) 'check for pass/fail case
'Detail Section Detault Setting hidden
'During the Report Formatting phase the Items which meets
'the criteria only visible on the Report Detail Section when PrintPreviewed
Detail.Visible = False
If FormatCount = 1 Then 'The Report's Formatting Count=1
If yn Then ' yn=True - student Passed
With Me
.lblRemarks.Caption = "PASSED"
.lblRemarks.ForeColor = RGB(0, 255, 0) 'Green Color
If Opt = 1 Then 'Report Option Passed Students List
Detail.Visible = True 'Make the Detail Section Visible
End If
End With
Else 'yn=False an Option=2 Cases
With Me
.lblRemarks.Caption = "FAILED"
.lblRemarks.ForeColor = RGB(255, 0, 0) 'Red Color
If Opt = 2 Then
Detail.Visible = True
End If
End With
End If
End If
Detail_Format_Exit:
Exit Sub
Detail_Format_Err:
MsgBox Err.Description, , "Detail_Format()"
Resume Detail_Format_Exit
End Sub
Private Sub Report_Page()
PageBorder Me.Name
End Sub
</pre>
<h3>The <i>StudentPassFail_Normal</i> Report Module Segmentwise Code Review.</h3>
<p>At the Global declaration area, the Variant Variable <b>Opt</b> is declared for holding the Option selected for Exam Result Print previewing of Passed or Failed Students' List.</p><h4 style="text-align: left;"><b>The Report_Load Event Subroutine.</b></h4>
<pre>
Option Compare Database
Option Explicit
Dim Opt As Variant
'---------------------------------------------------------
'Streamlining Form Module Code
'in Stand-alone Class Modules
'---------------------------------------------------------
'Sreamlining Report Module Code in Standalone Class Module
'Author: a.p.r. pillai
'Date : 22/09/2023
'Rights: All Rights(c) Reserved by www.msaccesstips.com
'---------------------------------------------------------
Private Sub Report_Load()
Dim x As Variant
x = Split(OpenArgs, ",")
Me!PassPercentage = Val(x(0))
Opt = Val(x(1))
End Sub
</pre>
<p>In the Form_Load() Event Procedure the PassPercentage and Report selection Option received as Report <i>OpenArgs</i>, both values separated with a comma are split into an Array of two elements. The <b>x(0)</b> element with the value PassPercentage marks is saved into the Report TextBox passpercentage. The Report Type option selection value <b>x(1)</b> is saved in the <b>Opt</b> variable</p>
<p>The PassPercentage value is copied into the Local Variable <b>pf</b>. The next two lines read the first student's Obtained Marks Percentage from <b>Percentage</b> TextBox into Variable <b>CurVal</b>. The Student's Mark is compared with the Passmark Percentage <b>pf</b> and the result is obtained in the <b>yn</b> Boolean Variable.</p>
<p>The next Code segment, given below, tests to see if the current student's record is qualified to be placed in the Passed category in the Detail Section.</p>
<pre>Detail.Visible = False 'Hide the Detail Section
If FormatCount = 1 Then 'The first Format Pass on this Page.
If yn Then
lblRemarks.Caption = "PASSED"
lblRemarks.ForeColor = RGB(0, 255, 0)
If Opt = 1 Then 'Passed Students Listing
Detail.Visible = True
End If
Else
lblRemarks.Caption = "FAILED"
lblRemarks.ForeColor = RGB(255, 0, 0)
If Opt = 2 Then
Detail.Visible = True
End If
End If
End If
</pre><p>The statement <b>Detail.Visible = False</b> makes the Detail Section of the Report hidden first. The next statement checks the <a href="https://learn.microsoft.com/en-us/office/vba/api/access.report.formatcount" target="_blank">OnFormat Event </a>action is passing through the Report Detail Section for the first time to lay the student record on the Report. The Report may undergo more Formatting passes on a Report Page in preparation for Printing/PrintPreviewing. If the Student is qualified to be placed in the Passed Category and the Report Option selected also matches to <b>1</b> then the Detail Section of the Report is made visible for such records on the Report.</p><p>Otherwise, if the Student's Obtained Mark Percentage is less and the option selected is <b>2</b> then those Students' Cases in the Detail Section are made visible. In either case, the TRUE/FALSE value in the Variable <b>yn</b> and the Option selection should both match (TRUE & 1 = Passed List otherwise FALSE & 2 = Failed List) to display the Passed cases or failed cases to appear on the Report.</p><p>Next, the Report_Page() Subroutine calls the PageBorder() Function in the Standard Module to draw the PageBorder on the Report.</p><pre>Private Sub Report_Page()
PageBorder Me.Name
End Sub</pre>
<h3>The PageBorder() Function Code In Standard Module.</h3>
<pre>Public Function PageBorder(ByVal strName As String)
Dim Rpt As Report, lngColor As Long
Dim sngTop As Single
Dim sngLeft As Single
Dim sngwidth As Single
Dim sngheight As Single
'------------------------------------------------------
'Streamlining Form Module Code
'in Stand-alone Class Modules
'------------------------------------------------------
'Draw Report PageBorder
'Author: a.p.r. pillai
'Date : 18/09/2023
'Rights: All Rights(c) Reserved by www.msaccesstips.com
'------------------------------------------------------
lngColor = RGB(0, 0, 255)
On Error GoTo DrawBox_Err
Set Rpt = Reports(strName)
' Set scale to pixels.
Rpt.ScaleMode = 3
'outer Border
sngTop = Rpt.ScaleTop 'Top Value After Margin
sngLeft = Rpt.ScaleLeft 'Left Value After Margin
sngwidth = Rpt.ScaleWidth - 7 ' Right Margin -7 pixels
sngheight = Rpt.ScaleHeight - 7 'Bottom Margin -7 pixels
' Draw line as a box.
Rpt.Line (sngTop, sngLeft)-(sngwidth, sngheight), lngColor, B
'Draw Box inside the outer Box
sngTop = Rpt.ScaleTop + 5
sngLeft = Rpt.ScaleLeft + 5
sngwidth = Rpt.ScaleWidth - 13
sngheight = Rpt.ScaleHeight - 13
'Draw second Box within the Borders of the First Box
Rpt.Line (sngTop, sngLeft)-(sngwidth, sngheight), lngColor, B
DrawBox_Exit:
Exit Function
DrawBox_Err:
MsgBox Err.Description, , "DrawBox"
Resume DrawBox_Exit
End Function
</pre>
<p>The PageBorder() Function accepts the open Report's Name as a Parameter. The Function draws two Border Lines, one inside the other, inside the Report 4 sides of the Margin area. It uses the LINE Command with the X, and Y coordinates of the Left Top corner, after the left and top margin areas, and the right bottom corner value before the margin area to draw a diagonal Line. The Color parameter is given as an RGB Value and is used to draw the Line diagonally. The next Parameter value <b>B</b> draws a Box using the Diagonal Value.</p>
<p>The <b>Rpt.ScaleTop</b> and <b>Rpt.ScaleLeft</b> value calculates the left top corner point, after the Left and Top Margin area, and some plus-minus Offset values ensure that when the Box is drawn it doesn't overlap, or draw a second Border inside the outer Border. </p><h3 style="text-align: left;">The Streamlining Procedure of the Report VBA Code.</h3><p>Let us create the Wrapper Standalone Class Module to move the Report Module Code into it. The Wrapper Class Module <b>ClsStudentsList</b> Code is listed below.</p>
<pre>Option Compare Database
Option Explicit
Private WithEvents Rpt As Access.Report
Private WithEvents SecDetail As Access.[_SectionInReport]
Private RequiredMarks As Access.TextBox
Private Obtained As Access.TextBox
Private lblRem As Access.Label
Private Opt As Variant
'---------------------------------------------------------
'Streamlining Form Module Code
'in Stand-alone Class Modules
'---------------------------------------------------------
'Sreamlining Report Module Code in Standalone Class Module
'Author: a.p.r. pillai
'Date : 22/09/2023
'Rights: All Rights(c) Reserved by www.msaccesstips.com
'---------------------------------------------------------
Public Property Get mRpt() As Access.Report
Set mRpt = Rpt
End Property
Public Property Set mRpt(RptNewVal As Access.Report)
Set Rpt = RptNewVal
Call class_Init
End Property
Private Sub class_Init()
On Error GoTo Class_Init_Err:
Dim msg As String
Dim x As Variant
Const strEvent = "[Event Procedure]"
x = Split(Rpt.OpenArgs,",")
With Rpt
!PassPercentage = Val(x(0)) ' Pass Mark% save on Report
Set RequiredMarks = .PassPercentage 'Assign this Control Reference to pct
Opt = Val(x(1))
Set Obtained = .Percentage 'Student's Obtained Percentage
Set lblRem = .lblRemarks 'Passed/Failed Display Label
Set SecDetail = .Section(acDetail) 'Detail Section Reference
SecDetail.OnFormat = strEvent 'Enable Detail Section Format Event
.OnPage = strEvent 'Enable Report_Page Event
End With
Class_Init_Exit:
Exit Sub
Class_Init_Err:
MsgBox Err & ": " & Err.Description, vbCritical + vbOK, "Class_Init()"
Resume Class_Init_Exit
End Sub
Private Sub secDetail_Format(Cancel As Integer, FormatCount As Integer)
'Report Format Pass Calls this Subroutine
Dim Curval As Double
Dim pf As Double 'pass/fail
Dim yn As Boolean
On Error GoTo secDetail_Format_Err
Curval = Nz(Obtained.Value, 0)
pf = Nz(RequiredMarks.Value, 0)
yn = (Curval >= pf)
'Start Laying the Detail Section items from Format Count 1 onwards.
'All the Records are placed on the Detail Section, but the
'Lines which meets the Criteria only made visible on the Report.
SecDetail.Visible = False 'Hide the Detail Section
If FormatCount = 1 Then 'The first Format Pass on this Page.
If yn Then
lblRem.Caption = "PASSED"
lblRem.ForeColor = RGB(0, 255, 0)
If Opt = 1 Then 'Passed Students Listing
SecDetail.Visible = True
End If
Else
lblRem.Caption = "FAILED"
lblRem.ForeColor = RGB(255, 0, 0)
If Opt = 2 Then
SecDetail.Visible = True
End If
End If
End If
secDetail_Format_Exit:
Exit Sub
secDetail_Format_Err:
MsgBox Err & ": " & Err.Description, , "secDetail()"
Resume secDetail_Format_Exit
End Sub
Private Sub Rpt_Page()
PageBorder Rpt.Name
End Sub
</pre>
<p>In the global Declaration area, a Report Object with the name <b>Rpt</b> is declared and qualified with the keyword <b>WithEvents</b> to capture the Report-level Events (like Report_Page()) and execute the Event Subroutines. Another Report Section object <b>SecDetail</b> is also qualified with the keyword <b>WithEvents</b> declared to capture the Report Detail Section Format Events.</p><p>Two TextBox Controls and a Label Control are declared. The first TextBox <b>RequiredMarks</b> will hold the Pass Marks Percentage entered on the Report launching Form TextBox control and passed as Report Open Argument. The <b>Obtained</b> TextBox object instance in the Class module will read the Obtained Marks Percentage of the Student from the Report. The <b>lblRem</b> Label Control holds the reference of the Label control under the Remarks Column on the Report and will display the PASSED/FAILED Text depending on the Report Type Option selected. The <b>Opt</b> Variant Variable will receive the Report type option selected in the <b>ReportOption()</b> Function.</p><p>Next, the Report Object Property <b>Get/Set</b> Procedure assigns the Report Reference to the <b>Rpt</b> object in the ClsStudentsList Wrapper Class. From the <b>Property Set m_Rpt()</b> Procedure calls the <b>Class_Int()</b> Subroutine. The Class_Init() Subroutine Code segment is given below:</p>
<pre>Private Sub class_Init()
On Error GoTo Class_Init_Err:
Dim msg As String
Dim x As Variant
Const strEvent = "[Event Procedure]"
x = Split(Rpt.OpenArgs,",")
With Rpt
!PassPercentage = Val(x(0)) ' Pass Mark% save on Report
Set RequiredMarks = .PassPercentage 'Assign this Control Reference to pct
Opt = Val(x(1))
Set Obtained = .Percentage 'Student's Obtained Percentage
Set lblRem = .lblRemarks 'Passed/Failed Display Label
Set SecDetail = .Section(acDetail) 'Detail Section Reference
SecDetail.OnFormat = strEvent 'Enable Detail Section Format Event
.OnPage = strEvent 'Enable Report_Page Event
End With
Class_Init_Exit:
Exit Sub
Class_Init_Err:
MsgBox Err & ": " & Err.Description, vbCritical + vbOK, "Class_Init()"
Resume Class_Init_Exit
End Sub
</pre> <p>At the beginning of the Class_Init() Subroutine, one local variable <b>msg</b> and a Constant Variable <b>strEvent</b> assigned with the Text <b>"[Event Procedure]"</b> are declared. </p><p>The next VBA Code Segment initializes the declared object variables with the References of the Controls from the Report.</p>
<pre>x = Split(Rpt.OpenArgs,",")
With Rpt
!PassPercentage = Val(x(0)) ' Pass Mark% save on Report
Set RequiredMarks = .PassPercentage 'Assign this Control Reference to pct
Opt = Val(x(1))
Set Obtained = .Percentage 'Student's Obtained Percentage
Set lblRem = .lblRemarks 'Passed/Failed Display Label
Set SecDetail = .Section(acDetail) 'Detail Section Reference
SecDetail.OnFormat = strEvent 'Enable Detail Section Format Event
.OnPage = strEvent 'Enable Report_Page Event
End With
</pre>
<p>The <b>x = Split(Rpt.OpenArgs,",") </b>statement Splits the values into two parts received in the <b>OpenArgs</b> String Variable. The first part <b>x(0)</b> array element is assigned to the PassPercentage Marks TextBox on the Report. The second part, Report category Option <b>x(1)</b> is assigned to the variable Opt.</p><p> The Student's Obtained Marks <b>Percentage</b> reference is assigned to the <b>Obtained</b> TextBox instance in the Class Module. The <b>lblRem</b> Label object instance is assigned with the lblRemarks Label control reference under the <b>Remarks</b> Column. The SecDetail Section Object is assigned with the Report Detail Section Reference.</p><p><b>Note:</b> <i>What does the meaning of the word <b>Reference</b> in the context of a Form, Control, or Object? When an Object/Control is loaded into memory it occupies a certain area of the computer's memory with an addressable reference or memory Pointer. Even though this memory location number is not known to us it is directly related to the object name like Text0, or Quantity. </i></p><p><i>When this reference address is assigned to an Instance of the same Object Type, like <b>Set Txt = myForm.Quantity</b> We can work with visible/physical objects, like Form, TextBox, or Report indirectly with an instance of the same Object, as we do in the Wrapper Class, like <b>Txt.Value = 20</b> will store the Value 20 in the Quantity TextBox on the Form.</i> </p><p><i>When we call a Function with the Form or TextBox as a Parameter actually we are passing the Reference of those Objects. This is the central concept of <b>Streamlining the Form/Report Module Code in the Standalone Class Module</b> is based on.</i></p><p>Next, the Report Detail Section Format and Report_Page() Events are enabled. After that, the Report type option selection Menu is displayed to select.</p><p>Once the Report Type Option is selected the Report Formatting starts and we have specific requirements as far as the Detail Section Formatting is concerned. We have already written the Code in the Detail_Section Formatting Event Procedure and explained it earlier.</p><p>Next, the Report Page level Event Subroutine calls the PageBorder() Function from the Standard Module to draw the Report Page Border.</p><p>To execute the Code in the <b>ClsStudentsList</b> Class Module we must load this Module into memory through the Report_Load() Event Subroutine. It cannot load itself into memory. The Report Module VBA Code is given below.</p>
<pre>Option Compare Database
Option Explicit
Private Marks As New ClsStudentsList
Private Sub Report_Load()
Set Marks.mRpt = Me
End Sub</pre>
<p>The above four lines of Code help to bring the Standalone Class Module into Memory and to run the entire action in the Standalone Class Module.</p>
<p>Hope you enjoy VBA Coding in the Standalone Class Module and are aware of its potential to save time by separating the task of Report Design and VBA Coding so that it is easy to transport reusable Code into other Projects and use the Code with/without change as the case may be.</p>
<h4>Demo Database Download Link. Change Database.</h4>
<!--Download Link /downloads/2023/09/StudentsMarkList.zip-->
<div class="separator" style="clear: both; text-align: center;">
<a href="https://drive.google.com/file/d/1i4lAwfGpTCIgAvulgfSgPq9YlnmV5Ux2/view?usp=sharing">
<img border="0" data-original-height="29" data-original-width="22" height="29" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFE4uDLH9dk3S6pEcfTkT6GW6r11XqBI4HQnLnhQFZMKFeeQt5ABV_NqvLHM68J7RacISsvbUEl0PlAq6V7i0t0mQF3iesrBuqGuvrf5CJV72OzrGZ7rdMxkxzw8FYlz-qniNySoqtqUN4/s320/zip.gif" width="22" />StudentsMarkList.zip</a>
</div>
<!--Download Link /downloads/2023/09/StudentsMarkList.zip-->
<h3>Streamlining Form Module Code in Standalone Class Module.</h3>
<ol>
<li><a href="https://www.msaccesstips.com/2023/04/reusing-form-module-vba-code-for-new.html" target="_Blank">Re-using Form Module VBA Code for New Projects.</a></li>
<li><a href="https://www.msaccesstips.com/2023/04/streamlining-form-module-code-part-two.html" target="_Blank">Streamlining Form Module Code Part-Two</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/05/streamlining-form-module-code-part-three.html" target="_Blank">Streamlining Form Module Code Part-Three</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/06/streamlining-form-module-code-part-four.html" target="_Blank">Streamlining Form Module Code Part-Four</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/06/streamlining-form-module-code-part-five.html" target="_Blank">Streamlining Form Module Code Part-Five</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-six.html" target="_Blank">Streamlining Form Module Code Part-Six</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-seven.html" target="_Blank">Streamlining Form Module Code Part-Seven</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-eight.html" target="_Blank">Streamlining Form Module Code Part-Eight</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part-nine.html" target="_Blank">Streamlining Form Module Code Part-Nine</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part-ten.html" target="_Blank">Streamlining Form Module Code Part-Ten</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part.html" target="_Blank">Streamlining Form Module Code Part-Eleven</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-report-module-code-in.html" target="_Blank">Streamlining Report Module Code in Class Module</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-report-module-in-class.html" target="_Blank">Streamlining Report Module Code in Class Module-2</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-form-module-code-part-14.html" target="_Blank">Streamlining Form Module Code Part-14:All Controls</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-custom-made-form-wizard.html" target="_Blank">Streamlining Custom Made Form Wizard-15</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-form-vba-custom-report.html" target="_Blank">Streamlining Custom Report Wizard-16</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-form-vba-external-files.html" target="_Blank">Streamlining Form VBA External File Browser-17</a></li>
<li><a href="https://www.msaccesstips.com/2023/11/streamlining-event-procedures.html" target="_Blank">Streamlining Event Procedures of 3D TextWizard-18</a></li>
<li><a href="https://www.msaccesstips.com/2023/11/streamlining-event-procedures-rgbcolor.html" target="_Blank">Streamlining Event Procedures RGB Color Wizard-19</a></li>
<li><a href="https://www.msaccesstips.com/2023/12/streamlining-numbers-to-words-converter.html" target="_Blank">Streamlining Event Procedures Number To Words-20</a></li>
</ol>
a.p.r. pillaihttp://www.blogger.com/profile/02627590161919120214noreply@blogger.com0tag:blogger.com,1999:blog-7457417942281874810.post-32915098104101842722023-09-12T21:30:00.017+05:302024-01-07T22:56:45.868+05:30Streamlining Report Module Code in Class Module.<h3 style="text-align: left;"> Introduction.</h3><h3 style="text-align: left;">Report Module VBA Code in Standalone Class Module.</h3><p>After going through the earlier Episodes of this Topic: <i>Streamlining Form Module Code in the Standalone Class Module</i>, we could write the Event Subroutines, for frequently used Access Controls on the Form, in the Standalone Class Module without interfering with the Form design task. If you landed on this page directly and have not yet gone through the earlier sessions of this tutorial I suggest you start with the first page of this series <a href="https://www.msaccesstips.com/2023/04/reusing-form-module-vba-code-for-new.html" target="_blank">Streamlining/Reusing Form Module Code for new Projects</a> to understand this new concept and implementation methods fully. All the links to this Series of Articles are available at the end of this Page.</p><h3 style="text-align: left;"><span>The Report Section Print Event Subroutines.</span></h3><p><span>This is a repeat of the two Blog Posts published earlier on Highlighting Report Lines conditionally, as part of the new concept of <i>Streamlining Form/Report Module Code in the Standalone Class Module. </i>This demonstrates how to transfer the existing Event Subroutine Codes from the Report Module to the Standalone Class Module. Generally speaking, this is double work and nobody would like to go into the Code again, which was written for a particular task after incorporating several refinements and final touches. </span></p>
<!-- Google In-Article Ads start -->
<script async="" src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle" data-ad-client="ca-pub-2376623373770548" data-ad-format="fluid" data-ad-layout="in-article" data-ad-slot="6147807460" style="display: block; text-align: center;"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
<!-- Google In-Article Ads END -->
<p><span>But, I think if you would like to do some trial runs and try to convert a few existing Form/Report Module Codes to the new way of Coding you will know how much extra work you put into the traditional method and it will give you more insights into the new way of coding in comparison with the traditional way.</span></p><h3 style="text-align: left;"><span>Organize the Existing Form Module Code for the Standalone Class Module.</span></h3><p></p><ol style="text-align: left;"><li>Before you start trying to go forward with this idea it is very important that you organize the existing Code of the Controls Category-wise together in the Form Module.</li><li><p></p><p>Make a Copy of the Form, you wish to try implementing the new method, with a new name. Plan to transfer this Form's all TextBox's Event Subroutine Code into a <a href="https://www.msaccesstips.com/2019/07/withevents-texbox-and-commandbutton.html" target="_blank">TextBox</a> Wrapper Class first.</p></li><li>In preparation, move the TextBox Event Subroutines next to each other on the Form Module itself first. If a single TextBox has more than one Event Subroutine then arrange them next to each other too.</li><li><p>Create a TextBox Wrapper Class.</p></li><li>Create the Intermediate Class Module and enable the Events of the TextBoxes in the Class_Init() Subroutine.</li><li><p>Copy and Paste the TextBox Event Subroutines from the <a href="https://www.msaccesstips.com/2012/06/defining-pages-on-form.html" target="_blank">Form</a> Module to the Wrapper TextBox Class and modify the Event Subroutine Names with the correct TextBox Object Instance name as Subroutine name Prefix. Use the <b>Select Case . . . End Select</b> structure inside each category of Event to position the Code belongs to a particular TextBox on the Form.</p></li><li>Delete the Copied Subroutines from the Form Module.</li><li><p>At the Form <a href="https://www.msaccesstips.com/2012/06/defining-pages-on-form.html" target="_blank">Module</a> global declaration area define the Intermediate <a href="https://www.msaccesstips.com/2018/10/ms-access-vba-class-object-arrays.html" target="_blank">Class Object</a> Instance and in the Form_Load() Event Procedure pass the <b>Me </b>Form object to the Set Frm() Property Procedure.</p></li><li>Save the Form after changes.</li><li><p>Before we try this new Form with the Wrapper Class-based Event Subroutines in place, rename the original Form with a new name with a suffix of X or Y.</p></li><li>Rename the new Form to the Original name.</li><li><p>Open the Form as you do from your Main Menu, or whatever method is built into your Project, and ensure that all the Subroutines of all the TextBoxes on the Form function as before. If any errors then find and correct them and see that everything works normally as before.</p></li><li>Repeat this process for all other controls on the Form. </li></ol><h3 style="text-align: left;">The Traditional Way of Coding.</h3><p></p><p><span><i>The traditional method of Coding is the best way for beginners to learn VBA and that is how we all started learning VBA. My first PC was the Spectravideo Model SV-328 with built-in Microsoft Extended Basic and started learning the BASIC Language from there, and Writing Programs and saving them in Audio Cassette Tapes at that time, the cheapest mode of storage.</i></span></p><p><span><i>It is interesting now to look at the Bootup Screen Image of this Computer given below (courtesy of <a href="https://en.wikipedia.org/wiki/SV-328">Wikipedia.org</a>):</i></span></p><div class="separator" style="clear: both;"><a href="https://en.wikipedia.org/wiki/SV-328" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="676" data-original-width="923" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh3saoZyXk1lrQxarVBLh1bESacoPn6Gd0980Oqu98Z6kmbMhuVr0GiaJDQaTvzIEn_ytgXtzMJe9rRcxRMXz2sczVZMXpVQRrrTgG178SX-3_CT_tQskZRft1SNB8phcq8R0EsM9lR3NBbRLdqHt98aff9kPQe3ZWXKw6vG5iLy_yAIxcbUs4qNYBXmvds/s320/SV_328.png" width="320" /></a></div>
<p><i>Then came the GW-Basic, </i><i>Quick BASIC, </i><i>Visual Basic, and VBA.</i></p><p><i>There is a very interesting Computer Buying Guide from 1986 with me, published by the Consumer Guide Magazine, for Computers with a Price Range of $299 (Commodore 128) to $4550 (Kaypro 286i, with 512K RAM, 6 MHz, 80286 microprocessor, compatible with IBM PC AT).</i></p>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjH8P4xrkWmNQfO_6vIdZqehdOdYzBfvZFfClK0k1j8YXv5kqztdy2AK3ibkfCu-M_ZFcsNg5d_v3GxtvaI6VPdbNZys7NnUn8I2YLblV2psw6lO7XIWCVYzTNh772qbePxCc-6-EC3U4yxSjAnFa4UPd21VAR3eJlSpuOG-CCJSHNdPGxHX_8DmTv-oOHK/s3640/ComputerBuy1.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="3640" data-original-width="2264" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjH8P4xrkWmNQfO_6vIdZqehdOdYzBfvZFfClK0k1j8YXv5kqztdy2AK3ibkfCu-M_ZFcsNg5d_v3GxtvaI6VPdbNZys7NnUn8I2YLblV2psw6lO7XIWCVYzTNh772qbePxCc-6-EC3U4yxSjAnFa4UPd21VAR3eJlSpuOG-CCJSHNdPGxHX_8DmTv-oOHK/s320/ComputerBuy1.png" /></a></div>
<p><i>The configuration of the machines, their speed, and the Prices of the machines in those days are very interesting to read and compare with the present-day machines.</i></p>
<p>Coming back to the topic the following two links of earlier Blog Posts on <a href="https://www.msaccesstips.com/2018/05/activity-dates-and-quarterly-reports.html" target="_blank">Report</a> Module Coding highlight some Report lines on certain conditions during the Printing cycle of the Report. </p><p>1. <a href="https://www.msaccesstips.com/2007/08/highlighting-reports.html" target="_blank">Highlighting Reports</a>. - August 16, 2007</p><p>2. <a href="https://www.msaccesstips.com/2019/06/withevents-and-report-line-highlighting.html" target="_blank">WithEvents and Report Line Highlighting</a>. - June 21, 2019</p><p><span>Let us see how we can run the Report Print Event Subroutines Code in the Standalone <a href="https://www.msaccesstips.com/2018/10/ms-access-class-module-and-vba.html" target="_blank">Class Module</a>.</span></p><h3 style="text-align: left;"><span> The Students Examination Result Report Image.</span></h3>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhlwlWH-lFSeJej8ENULRimEw0izU__pTV-VU_sbuY293w7H2iTi4H_brs0ogPInjyM9u5EBLak4H0QcRt7DsMWQVqUcrYVPhUmICeXRDtFmnVYAXfzSV83GjnQo6cEmeM87OSrb6uF8DnwB7kumobHR5xdVEAknxdh5R-Itd6CtEsNUG6Svwg6tvhc4_Gj/s973/StreamReport1.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="Streamline Report Image" border="0" data-original-height="765" data-original-width="973" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhlwlWH-lFSeJej8ENULRimEw0izU__pTV-VU_sbuY293w7H2iTi4H_brs0ogPInjyM9u5EBLak4H0QcRt7DsMWQVqUcrYVPhUmICeXRDtFmnVYAXfzSV83GjnQo6cEmeM87OSrb6uF8DnwB7kumobHR5xdVEAknxdh5R-Itd6CtEsNUG6Svwg6tvhc4_Gj/s320/StreamReport1.png" width="320" /></a></div>
<p><b>Note:</b> <i>If you attempt to run the Report (StudentHighlight_Normal) directly by double-clicking on the Report, the Report may open in the <b>ReportPreview</b> Mode and not in <b>PrintPreview </b>Mode. In that case, you will not find the display as shown above. Right-click on the Report, and select the <b>PrintPreview</b> option from the displayed Menu to view the Report correctly.</i></p><p> The Students List with their Final Examination Result published in the form of a Report shows that the minimum Pass Marks required is 60% (360 Marks) out of 600 Total marks. The unsuccessful students' cases are marked with a Red ellipse around the obtained marks percentage. The Legend Label at the Footer of the Report is also drawn with a Circle within the dimension of the Label.</p><p>To try the procedure with different Pass Mark Percentages there is a Form with a TextBox to input the Pass Mark Percentage before launching the Exam Result Report for Print Previewing. The Pass Mark Percentage is passed in the <i>Open-Argument</i> parameter to the Report opening Command. The Form Image is given below for reference.</p>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXpDLGfC5dId3hX2PFlFObfe06wdQ1ht8aeQ2vfSMjUZc8oCugeYA_jhpHy7LD9khlB1XTy5_septsuiKgaQxUaJS-js0Y7Uar-9IbmMnU1N9T9cuBsxl_RqHDjmXyxhQK7bRLKGalcIV3m8zHrkzjuafrXsgsBYDmVASp6jc99WRLyJAG-p2pSVWVzxTE/s592/StreamReportForm2.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="Streamline Report Module" border="0" data-original-height="470" data-original-width="592" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXpDLGfC5dId3hX2PFlFObfe06wdQ1ht8aeQ2vfSMjUZc8oCugeYA_jhpHy7LD9khlB1XTy5_septsuiKgaQxUaJS-js0Y7Uar-9IbmMnU1N9T9cuBsxl_RqHDjmXyxhQK7bRLKGalcIV3m8zHrkzjuafrXsgsBYDmVASp6jc99WRLyJAG-p2pSVWVzxTE/s320/StreamReportForm2.png" width="320" /></a></div>
<p>The VBA Code run earlier in the <i>Report Class Module</i> is listed below for reference. This Code will be transferred to the Standalone Class Module to run the procedure in the new Coding method.</p>
<pre style="text-align: left;">Option Compare Database
Option Explicit
Private Curval As Double
Private MinTarget As Double
Private Sub Detail_Print(Cancel As Integer, PrintCount As Integer)
Dim intPrintCircle As Boolean
On Error GoTo secRpt_Print_Err
MinTarget = Me!PassPercentage
Curval = Me!Percentage
If (MinTarget > 0) And (Curval < MinTarget) Then
intPrintCircle = True
Else
intPrintCircle = False
End If
DrawCircle intPrintCircle, Me.Percentage
secRpt_Print_Exit:
Exit Sub
secRpt_Print_Err:
MsgBox Err.Description, , "secRpt_Print()"
Resume secRpt_Print_Exit
End Sub
Private Sub ReportFooter_Print(Cancel As Integer, PrintCount As Integer)
Dim yn As Boolean, lbl As Control
On Error GoTo secFutr_Print_Err
yn = True 'set the flag false to draw oval shape
Set lbl = Me.Legend 'pass label control in Page Footer
DrawCircle yn, lbl 'draw circle in legend label
secFutr_Print_Exit:
Exit Sub
secFutr_Print_Err:
MsgBox Err.Description, , "secFutr_Print"
Resume secFutr_Print_Exit
End Sub
Private Sub DrawCircle(ByVal bool As Boolean, ovlCtl As Control)
Dim ctl As Control
Dim bolPrintCircle As Boolean
Dim sngAspect As Single
Dim intShapeHeight As Integer
Dim intShapeWidth As Integer
Dim sngXCoord As Single
Dim sngYCoord As Single
On Error GoTo DrawCircle_Err
If bool Then 'if pass no highlighting, change logic for pass cases
bolPrintCircle = True
Else 'highlight failed cases
bolPrintCircle = False
End If
Set ctl = ovlCtl
If Not IsNull(ctl) Then
If bolPrintCircle Then
' change this value to adjust the oval shape of the circle.
sngAspect = 0.25
' Determine coordinates of ctl and to draw ellipse.
' Determine height and width of ellipse.
intShapeHeight = ctl.Height
intShapeWidth = ctl.Width
'calculate circle vertical Y coordinate
sngYCoord = ctl.Top + (intShapeHeight \ 2)
'calculate horizontal X coordinate of circile
sngXCoord = ctl.Left + (intShapeWidth \ 2)
'draw an ellipse around the Total TextBox
Report.Circle (sngXCoord, sngYCoord), intShapeWidth \ 2, RGB(255, 0, 0), , , sngAspect
bolPrintCircle = False
End If
End If
DrawCircle_Exit:
Exit Sub
DrawCircle_Err:
MsgBox Err.Description, , "DrawCircle()"
Resume DrawCircle_Exit
End Sub
</pre>
<h3 style="text-align: left;">Review of the VBA Code in the Report Class Module.</h3><p>There are two Event Subroutines, the Detail_Print() and the ReportFooter_Print() Subroutines. Then the DrawCircle() Subroutine that is called from both Detail_Print() and ReportFooter_Print() Event Subroutines to draw the ellipse around the data in the TextBox on the Report where the Marks Percentage doesn't meet the Criteria, passed as Report Open Argument from the TextBox on the Form.</p>
<!-- Google In-Article Ads start -->
<script async="" src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle" data-ad-client="ca-pub-2376623373770548" data-ad-format="fluid" data-ad-layout="in-article" data-ad-slot="6147807460" style="display: block; text-align: center;"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
<!-- Google In-Article Ads END -->
<p>The Print_Event Subroutines Code is written on the Report <b>StudentHighLight_Normal </b>Module as we normally do. This demo Report can be run directly, from the Demo Database given at the end of this Page for download, by Clicking on the <a href="https://www.msaccesstips.com/2011/06/creating-animated-command-button-with.html" target="_blank">Command Button</a>, on the Form shown above, with the Caption <i>Normal Report.</i> </p><h3 style="text-align: left;">The Changed VBA Code in the Standalone Class Module.</h3><p>The same Event Subroutines and the DrawCircle() Subroutine Codes are removed from the Report Class Module and placed in the Standalone Wrapper Class Module as given below:</p>
<pre style="text-align: left;">Option Compare Database
Option Explicit
Private WithEvents Rpt As Access.Report
Private WithEvents secRpt As Access.[_SectionInReport]
Private WithEvents secFutr As Access.[_SectionInReport]
Private pct As Access.TextBox 'Percentage
Private lgnd As Access.Label
Private Pass As Integer 'OpenArgs Value
'------------------------------------------------------
'Streamlining Report Module Code
'in Stand-alone Class Modules
'------------------------------------------------------
'Streamlining Report Module Code
'Author: a.p.r. pillai
'Date : 12/09/2023
'Rights: All Rights(c) Reserved by www.msaccesstips.com
'------------------------------------------------------
Public Property Get mRpt() As Access.Report
Set mRpt = Rpt
End Property
Public Property Set mRpt(RptNewVal As Access.Report)
Set Rpt = RptNewVal
Call Class_Init
End Property
Private Sub Class_Init()
On Error GoTo mRpt_Err
Const strEvent = "[Event Procedure]"
'Pass Percentage value
Pass = Nz(Rpt.OpenArgs, 0) 'OpenArgument
Rpt.PassPercentage.Value = Pass
With Rpt
Set secRpt = .Section(acDetail)
Set secFutr = .Section(acFooter)
secRpt.OnPrint = strEvent 'Enable Event
secFutr.OnPrint = strEvent ' "
End With
Set pct = Rpt!Percentage
Set lgnd = Rpt.Legend
mRpt_Exit:
Exit Sub
mRpt_Err:
MsgBox Err.Description, , "mRpt()"
Resume mRpt_Exit
End Sub
'Draw ellipse around controls that meet specified criteria.
Private Sub secRpt_Print(Cancel As Integer, PrintCount As Integer)
Dim obtPct As Double
Dim yn As Boolean
On Error GoTo secRpt_Print_Err
obtPct = pct.Value 'read Pass Percentage TextBox value
yn = (obtPct < Pass) 'Passed or Not (TRUE/FALSE)
'call the DrawCircle Subroutine with Fail flag
'and the Control as parameters
Call DrawCircle(yn, pct)
secRpt_Print_Exit:
Exit Sub
secRpt_Print_Err:
MsgBox Err.Description, , "secRpt_Print"
Resume secRpt_Print_Exit
End Sub
'Draw oval shape around Label in Report_Footer Section
Private Sub secFutr_Print(Cancel As Integer, PrintCount As Integer)
Dim yn As Boolean, lbl As Control
On Error GoTo secFutr_Print_Err
Set lbl = lgnd 'Fail label control in Page Footer
yn = True 'set the flag True to draw oval shape
Call DrawCircle(yn, lbl) 'draw circle in legend label
secFutr_Print_Exit:
Exit Sub
secFutr_Print_Err:
MsgBox Err.Description, , "secFutr_Print"
Resume secFutr_Print_Exit
End Sub
Private Sub DrawCircle(ByVal bool As Boolean, ovlCtl As Control)
Dim ctl As Control
Dim bolPrintCircle As Boolean
Dim sngAspect As Single
Dim intShapeHeight As Integer
Dim intShapeWidth As Integer
Dim sngXCoord As Single
Dim sngYCoord As Single
On Error GoTo DrawCircle_Err
If bool Then 'Highlight Failed Cases
bolPrintCircle = True
Else
bolPrintCircle = False
End If
Set ctl = ovlCtl
If Not IsNull(ctl) Then
If bolPrintCircle Then
' change this value to adjust the oval shape of the circle.
sngAspect = 0.25
' Determine coordinates of ctl and to draw ellipse.
' Get height and width of Control.
intShapeHeight = ctl.Height
intShapeWidth = ctl.Width
'calculate circle vertical Y coordinate
sngYCoord = ctl.Top + (intShapeHeight \ 2)
'calculate horizontal X coordinate of circile
sngXCoord = ctl.Left + (intShapeWidth \ 2)
'Draw an ellipse within the Percentage TextBox Boundaries
'in Red Color
Rpt.Circle (sngXCoord, sngYCoord), intShapeWidth \ 2, RGB(255, 0, 0), , , sngAspect
bolPrintCircle = False
End If
End If
DrawCircle_Exit:
Exit Sub
DrawCircle_Err:
MsgBox Err.Description, , "DrawCircle()"
Resume DrawCircle_Exit
End Sub
</pre>
<h3 style="text-align: left;">Review of the Report Wrapper Class VBA Code.</h3><p>To execute the Standalone Class Module Code, when the Report is open, we must get the reference of the Report and assign it to the Report object Instance <b>Rpt</b> in our Standalone Class Module. Let us review the Code from the beginning to the Class_Init() Segment.</p>
<pre>Option Compare Database
Option Explicit
Private WithEvents Rpt As Access.Report
Private WithEvents secRpt As Access.[_SectionInReport]
Private WithEvents secFutr As Access.[_SectionInReport]
Private pct As Access.TextBox 'Percentage
Private lgnd As Access.Label
Private Pass As Integer 'OpenArgs Value
'------------------------------------------------------
'Streamlining Report Module Code
'in Stand-alone Class Modules
'------------------------------------------------------
'Streamlining Report Module Code
'Author: a.p.r. pillai
'Date : 12/09/2023
'Rights: All Rights(c) Reserved by www.msaccesstips.com
'------------------------------------------------------
Public Property Get mRpt() As Access.Report
Set mRpt = Rpt
End Property
Public Property Set mRpt(RptNewVal As Access.Report)
Set Rpt = RptNewVal
Call Class_Init
End Property
Private Sub Class_Init()
On Error GoTo mRpt_Err
Const strEvent = "[Event Procedure]"
'Pass Percentage value
Pass = Nz(Rpt.OpenArgs, 0) 'OpenArgument
Rpt.PassPercentage.Value = Pass
With Rpt
Set secRpt = .Section(acDetail)
Set secFutr = .Section(acFooter)
secRpt.OnPrint = strEvent 'Enable Event
secFutr.OnPrint = strEvent ' "
End With
Set pct = Rpt.Percentage
Set lgnd = Rpt.Legend
mRpt_Exit:
Exit Sub
mRpt_Err:
MsgBox Err.Description, , "mRpt()"
Resume mRpt_Exit
End Sub
</pre>
<p>The first three lines, below the Option Explicit line, declare a Report Object Instance with the object name <b>Rpt</b> and qualified with the keyword <b>WithEvents</b> to capture the Report Events. Similarly, two Report Sections <i>Access.[_SectionInReport]</i> are declared, followed by two Report Control objects, and an Integer Variable.</p><p>Next the <b>Set/Get</b> Property Procedures for the Report Object. After the Report object is assigned to the <b>Rpt</b> object instance variable, the <b>Set mRpt() </b>Property Procedure Calls the <b>Class_Init()</b> Subroutine.</p><p>The strEvent Constant Variable is assigned with the string value "[Event Procedure]". The statement <b>Pass = Nz(Rpt.OpenArgs, 0)</b> reads the Pass Mark percentage passed as Report Open Argument through the <i>DoCmd.OpenReport</i> from the Form is saved in the Integer Variable <b>Pass</b>. The next line assigns this value to the PassPercentage TextBox on the Report.</p><p>Next, the Report Detail Section and Footer Section References are assigned to the <b>secRpt</b> and <b>secFutr</b> Section Objects respectively. The next two lines of Code enable the Print Events.</p><p>The statement <b>Set pct = Rpt.Percentage</b> assigns the Obtained Marks <b>Percentage</b> TextBox reference and the <b>lgnd</b> Label Object is assigned with the Reference of the <b>Legend</b> label control at the Report Footer.</p><p>These initialisation steps run in the standalone Class Module, when the Report is Open from the <b>cmdClass</b> Click Event Subroutine.</p><pre>Private Sub cmdClass_Click()
If Nz(Me.Pass, 0) = 0 Then
MsgBox "Enter pass-Mark Percentage?" & vbCr & "e.g.: 60"
Else
'Me.Pass - Pass Mark Percentage value passed as OpenArgs
DoCmd.OpenReport "StudentsHighlight_Class", acViewPreview, , , , Me.Pass
End If
End Sub </pre>
<h3 style="text-align: left;">Report Class Module VBA Code.</h3><p>In the Report Class Module, declare an Instance of the Standalone Class Module Object <b>ClsStudentHighlight</b> with the name <b>Marks </b>in the global area<b>. </b>The New keyword in<b> </b> <i>Private Marks As <b>New</b> ClsStudentHighlight</i> statement creates an instance of the Standalone Class Module in memory.</p><p>In the Report_Load() Event Subroutine the current Report Object <b>Me</b> is passed to the <b>Set mRpt()</b> Property Procedure in the ClsStudentHighlight Class Module Object.</p><pre style="text-align: left;">Option Compare Database
Option Explicit
Private Marks As New ClsStudentHighlight
Private Sub Report_Load()
Set Marks.mRpt = Me
End Sub
</pre><h3 style="text-align: left;">The Report Detail_Print() Event.</h3><p>When the Report is open the ClsStudentHighlight standalone Class Module Instance is also open in memory by the Report Module global declaration statement. The current Report Reference is passed to the <b>Set mRpt()</b> Property Procedure and the standalone Class Module Code starts executing.</p>
<pre>'Draw ellipse around controls that meet specified criteria.
Private <b>Sub secRpt_Print(Cancel As Integer, PrintCount As Integer)</b>
Dim obtPct As Double
Dim yn As Boolean
On Error GoTo secRpt_Print_Err
obtPct = pct.Value 'read Pass Percentage TextBox value
yn = (obtPct < Pass) 'Passed or Not (TRUE/FALSE)
'call the DrawCircle Subroutine with Fail flag
'and the Control as parameters
Call DrawCircle(yn, pct)
secRpt_Print_Exit:
Exit Sub
secRpt_Print_Err:
MsgBox Err.Description, , "secRpt_Print"
Resume secRpt_Print_Exit
End Sub
</pre>
<p>There are two major Events that take place when the Report is Open, the Report Format Event and the Report Print Event. The Report Format Event lays the data line by line and calculates how many lines can go into one Page based on the type of Paper Size, the design height of the Detail Section, and based on <a href="https://www.msaccesstips.com/2009/06/network-and-report-page-setup.html" target="_blank">Page Orientation</a>. We will highlight the Exam Failed cases in the Report Print Event execution time only.</p><p>There are two Variables declared for reading the Obtained Percentage of Marks from the <b>Percentage</b> TextBox on the Report into the Variable <b>obtPct</b>. If the obtained mark is less than the <b>Pass</b> Mark percentage (received in the OpenArgs) then the Boolean Variable <b>yn</b> value is set as TRUE.</p><p>Next, the DrawCircle() Subroutine is called, with the <b>yn</b> Test Result and <b>pct</b> the Percentage TextBox object as parameters, to draw an oval shape in Red around the Percentage TextBox on the Report indicating that the student is not successful in his/her examination.</p>
<h3>The DrawCircle() Subroutine Code.</h3>
<pre style="text-align: left;">Private Sub DrawCircle(ByVal bool As Boolean, ovlCtl As Control)
Dim ctl As Control
Dim bolPrintCircle As Boolean
Dim sngAspect As Single
Dim intShapeHeight As Integer
Dim intShapeWidth As Integer
Dim sngXCoord As Single
Dim sngYCoord As Single
On Error GoTo DrawCircle_Err
If bool Then 'Highlight Failed Cases
bolPrintCircle = True
Else
bolPrintCircle = False
End If
Set ctl = ovlCtl
If Not IsNull(ctl) Then
If bolPrintCircle Then
' Increase/Decrease this value to adjust the oval shape of the circle.
sngAspect = 0.25
' Calculate the coordinates of the ctl to draw the ellipse.
' Get height and width of Control.
intShapeHeight = ctl.Height
intShapeWidth = ctl.Width
'calculate circle vertical Y coordinate
sngYCoord = ctl.Top + (intShapeHeight \ 2)
'calculate horizontal X coordinate of circle
sngXCoord = ctl.Left + (intShapeWidth \ 2)
'Draw an ellipse within the Percentage TextBox Boundaries
'in Red Color
Rpt.Circle (sngXCoord, sngYCoord), intShapeWidth \ 2, RGB(255, 0, 0), , , sngAspect
bolPrintCircle = False
End If
End If
DrawCircle_Exit:
Exit Sub
DrawCircle_Err:
MsgBox Err.Description, , "DrawCircle()"
Resume DrawCircle_Exit
End Sub
</pre>
<h3 style="text-align: left;">The DrawCircle() Subroutine VBA Code Review.</h3><p>At the beginning, the essential Variables are declared. The first parameter <b>bool</b> value is TRUE then the local Variable bolPrintCircle Variable is Set to True to draw the ellipse..</p><p>The <b>sngAspect</b> is initialized with the value 0.25 which gives an oval shape around the TextBox. This value can be increased or decreased to keep the shape within the control's dimensions without overflowing the boundaries of the Percentage TextBox. This depends on the Width and Height of the <a href="https://www.msaccesstips.com/2008/10/textbox-and-label-inner-margins.html" target="_blank">TextBox</a>, and these values are taken and stored in the <b>intShapeHeight</b> and <b>intShapeWidth</b> Variables<b>. </b></p><p>The next two statements calculate the center point (X, and Y coordinates) of the ellipse (oval shape) shape.</p><p>The <b>Rpt.Circle()</b> statement Draws the Circle in red using the value <b>intShapeWidth\2</b> as the Radius Value of the Circle.</p>
<h3>The ReportFooter_Print() Event Subroutine.</h3>
<pre>'Draw oval shape around Label in Report_Footer Section
Private <b>Sub secFutr_Print(Cancel As Integer, PrintCount As Integer)</b>
Dim yn As Boolean, lbl As Control
On Error GoTo secFutr_Print_Err
Set lbl = lgnd 'Fail label control in Page Footer
yn = True 'set the flag True to draw oval shape
Call DrawCircle(yn, lbl) 'draw circle in legend label
secFutr_Print_Exit:
Exit Sub
secFutr_Print_Err:
MsgBox Err.Description, , "secFutr_Print"
Resume secFutr_Print_Exit
End Sub </pre>
<p>The ReportFooter_Print() Subroutine Calls the DrawCircle() Subroutine to draw the ellipse within the Legend Label Control.</p>
<h4>Demo Database Download Link.</h4>
<!--Download Link /downloads/2023/09/MarksHighlight.zip-->
<div class="separator" style="clear: both; text-align: center;">
<a href="https://drive.google.com/file/d/1CYwjSvj9tXYqsj7yzrPNnto-CLRaYlwh/view?usp=sharing">
<img border="0" data-original-height="29" data-original-width="22" height="29" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFE4uDLH9dk3S6pEcfTkT6GW6r11XqBI4HQnLnhQFZMKFeeQt5ABV_NqvLHM68J7RacISsvbUEl0PlAq6V7i0t0mQF3iesrBuqGuvrf5CJV72OzrGZ7rdMxkxzw8FYlz-qniNySoqtqUN4/s320/zip.gif" width="22" />MarksHighlight.zip</a>
</div>
<!--Download Link /downloads/2023/09/MarksHighlight.zip-->
<br />
<h3>Streamlining Form Module Code in Standalone Class Module.</h3>
<ol>
<li><a href="https://www.msaccesstips.com/2023/04/reusing-form-module-vba-code-for-new.html" target="_Blank">Re-using Form Module VBA Code for New Projects.</a></li>
<li><a href="https://www.msaccesstips.com/2023/04/streamlining-form-module-code-part-two.html" target="_Blank">Streamlining Form Module Code Part-Two</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/05/streamlining-form-module-code-part-three.html" target="_Blank">Streamlining Form Module Code Part-Three</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/06/streamlining-form-module-code-part-four.html" target="_Blank">Streamlining Form Module Code Part-Four</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/06/streamlining-form-module-code-part-five.html" target="_Blank">Streamlining Form Module Code Part-Five</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-six.html" target="_Blank">Streamlining Form Module Code Part-Six</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-seven.html" target="_Blank">Streamlining Form Module Code Part-Seven</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-eight.html" target="_Blank">Streamlining Form Module Code Part-Eight</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part-nine.html" target="_Blank">Streamlining Form Module Code Part-Nine</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part-ten.html" target="_Blank">Streamlining Form Module Code Part-Ten</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part.html" target="_Blank">Streamlining Form Module Code Part-Eleven</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-report-module-code-in.html" target="_Blank">Streamlining Report Module Code in Class Module</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-report-module-in-class.html" target="_Blank">Streamlining Report Module Code in Class Module-2</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-form-module-code-part-14.html" target="_Blank">Streamlining Form Module Code Part-14:All Controls</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-custom-made-form-wizard.html" target="_Blank">Streamlining Custom Made Form Wizard-15</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-form-vba-custom-report.html" target="_Blank">Streamlining Custom Report Wizard-16</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-form-vba-external-files.html" target="_Blank">Streamlining Form VBA External File Browser-17</a></li>
<li><a href="https://www.msaccesstips.com/2023/11/streamlining-event-procedures.html" target="_Blank">Streamlining Event Procedures of 3D TextWizard-18</a></li>
<li><a href="https://www.msaccesstips.com/2023/11/streamlining-event-procedures-rgbcolor.html" target="_Blank">Streamlining Event Procedures RGB Color Wizard-19</a></li>
<li><a href="https://www.msaccesstips.com/2023/12/streamlining-numbers-to-words-converter.html" target="_Blank">Streamlining Event Procedures Number To Words-20</a></li>
</ol>
a.p.r. pillaihttp://www.blogger.com/profile/02627590161919120214noreply@blogger.com0tag:blogger.com,1999:blog-7457417942281874810.post-51187149050672573002023-08-31T17:45:00.126+05:302024-01-07T22:54:54.590+05:30Streamlining Form Module Code - Part Eleven<h3>Introduction.</h3>
<p>In this Episode of Streamlining Form Module VBA Code, we will create Wrapper Classes for ComboBox and OptionGroup Controls. Having gone through the previous Episodes, you are now acquainted with the process of creating Wrapper Class Modules for other controls, such as TextBox, ListBox, CommandButton, and TabControl. You have also learned to write Event Subroutines within these modules instead of placing them in the Form Module.</p><h3 style="text-align: left;">ComboBox and OptionGroup Control.</h3><p>This time we will learn the usage of ComboBox and OptionGroup Controls as shown in the form image given below.</p>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgtYp-Yn6oP2vkjFJsMrKe3D-K7aIfnMFm19RqIbewH2r2yuWI9678Up46CiTeTx8DLNrYD4QuV5oBusrX1-ExpFQRyr16L35bcHnqnni5pgOv_2jzUfBrWis2IWAPGBStiadjkEmZWwuJJAeGnBMagliITzxBXmJLtn5fBKhV5EWDcvrirsEaOpYlpSdSG/s1015/Streamline11.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="709" data-original-width="1015" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgtYp-Yn6oP2vkjFJsMrKe3D-K7aIfnMFm19RqIbewH2r2yuWI9678Up46CiTeTx8DLNrYD4QuV5oBusrX1-ExpFQRyr16L35bcHnqnni5pgOv_2jzUfBrWis2IWAPGBStiadjkEmZWwuJJAeGnBMagliITzxBXmJLtn5fBKhV5EWDcvrirsEaOpYlpSdSG/s320/Streamline11.png" width="320" /></a></div>
<p>The <b>Order Detail </b>data in the <a href="https://www.msaccesstips.com/2009/09/filter-function-output-in-listbox-2.html" target="_blank">ListBox</a> are filtered in <b>OrderDetailQ1</b> based on the Employee ID selected in the <b>ComboBox</b> with the name <b>cboEmp</b> above the ListBox Control. The SQL of the Query is given below:</p>
<!-- Google In-Article Ads start -->
<script async="" src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle" data-ad-client="ca-pub-2376623373770548" data-ad-format="fluid" data-ad-layout="in-article" data-ad-slot="6147807460" style="display: block; text-align: center;"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
<!-- Google In-Article Ads END -->
<h3 style="text-align: left;">OrderDetailQ1 SQL.</h3>
<pre>SELECT Orders.EmployeeID, Employees.LastName, Orders.OrderID, Val(Format([OrderDate],"yyyy")) AS [Year], Orders.Freight
FROM Employees INNER JOIN Orders ON Employees.EmployeeID = Orders.EmployeeID
WHERE (((Orders.EmployeeID)=[Forms]![Frm_OptionGroup]![cboEmp]));
</pre><p>The Freight Value in OrderDetailQ1 is summarised Year-wise in <b>OrderSummaryQ1. </b>The <b>OrderSummaryQ1</b> Query has the year-wise Freight Value Source Data for the Graph Chart on the Form. OrderSummary SQL is given below.</p><h3 style="text-align: left;">OrderSummaryQ1 SQL.</h3>
<pre style="text-align: left;">SELECT [OrderDetailQ1].EmployeeID, [OrderDetailQ1].LastName, [OrderDetailQ1].Year, Sum([OrderDetailQ1].freight) AS Freight
FROM OrderDetailQ1
GROUP BY [OrderDetailQ1].EmployeeID, [OrderDetailQ1].LastName, [OrderDetailQ1].Year;
</pre>
<h3 style="text-align: left;">Freight Summary Data for the Chart.</h3>
<pre style="text-align: left;">Employee Last Name Year Freight
Davolio, Nancy Davolio <span> </span><span> </span>1996 ₹ 1,871.09
Davolio, Nancy Davolio <span> </span><span> </span>1997 ₹ 4,584.47
Davolio, Nancy Davolio <span> </span><span> </span>1998 ₹ 2,381.13
</pre>
<h3 style="text-align: left;">How ComboBox, ListBox, and the Chart linked Together?</h3><p>When the Employee Code is selected in the <a href="https://www.msaccesstips.com/2009/09/dynamic-listbox-combobox-contents.html" target="_blank">ComboBox</a> the AfterUpdate Event is fired and the statement <b>cbofrm.List0.Requery</b> updates the ListBox contents.</p><p>There is a hidden Unbound TextBox on the Form with an expression to copy the changed Employee ID Value from the cboEmp ComboBox. This is used for the "Link Master Field" Property in the Graph Chart to update the Freight Year-wise Summary Data in the <a href="https://www.msaccesstips.com/2007/09/ms-access-and-graph-charts2.html" target="_blank">Graph Chart</a>.</p><p>So, by changing the Employee ID (Combobox displays the Employee Name only, and the first column EmployeeID width Property value is set to zero) the ComboBox refreshes the ListBox OrderDetail and the Freight Year-wise Values on the Graph Chart instantly.</p><h3 style="text-align: left;">The OptionGroup Control.</h3><p>The OptionsGroup Control can have a group of Radio Buttons or Check Boxes or Toggle Buttons and all of them are placed within a Frame. Here we are using the Radio Buttons within the OptionGroup Frame with the name <b>Frame7. </b>The<b> Frame7 </b>has three Radio Buttons with their <a href="https://www.msaccesstips.com/2010/04/label-animation-style-2.html" target="_blank">Labels</a> and is placed below the ListBox and Graph Chart objects. </p><p>There are three options to display the Employee's Freight Sales Values in three different categories.</p><p></p><ol style="text-align: left;"><li>- The highest Freight Sales Value of the Employee.</li><li>- The Lowest Freight Sales Value.</li><li>- The Total Fright Sales Value.</li></ol><p></p><p>The Unbound TextBox at the left side of the OptionGroup Control will display the Value when the option Radio Button is selected. The selected option description will appear in a label control, at the left side of the Unbound TextBox in an animated style, by moving the Text from right to left.</p><p>The Command Button Click will close the Form.</p><p>We already created wrapper classes for the ComboBox and ListBox in the earlier episodes. The OptionGroup Control is new in this Series of Tutorials and needs a Wrapper Class. When we place an OptionGroup Control on the Form the default name used by Microsoft Access is something like <i>Frame7</i>. So we will create a Wrapper Class for the OptionGroup Control with the name <b>OptFrame</b>. </p><h3 style="text-align: left;">The OptFrame Wrapper Class VBA Code.</h3>
<pre>Option Compare Database
Option Explicit
Private WithEvents Opt As Access.OptionGroup
Private frm As Access.Form
'------------------------------------------------------
'Streamlining Form Module Code
'in Stand-alone Class Modules
'------------------------------------------------------
'OptionGroup Wrapper Class
'Author: a.p.r. pillai
'Date : 31/08/2023
'Rights: All Rights(c) Reserved by www.msaccesstips.com
'------------------------------------------------------
'Form's Property GET/SET Procedures
Public Property Get opt_Frm() As Form
Set opt_Frm = frm
End Property
Public Property Set opt_Frm(ByRef ofrm As Form)
Set frm = ofrm
End Property
'TextBox Property GET/SET Procedures
Public Property Get o_opt() As OptionGroup
Set o_opt = Opt
End Property
Public Property Set o_opt(ByRef mopt As OptionGroup)
Set Opt = mopt
End Property
'Event Subroutines Code
Private Sub opt_Click()
Dim Rslt As Variant
Dim Cap(1 To 3) As String
Static strText As String
Cap(1) = "Highest Freight Value:"
Cap(2) = " Lowest Freight Value:"
Cap(3) = " Total Freight Value:"
Select Case Opt.Name
Case "Frame7"
Select Case Opt.Value
Case 1
'Repeated Clicks on the same option is ignored.
If strText = Cap(1) Then Exit Sub
Rslt = DMax("Freight", "OrderDetailQ1")
Case 2
If strText = Cap(2) Then Exit Sub
Rslt = DMin("Freight", "OrderDetailQ1")
Case 3
If strText = Cap(3) Then Exit Sub
Rslt = DSum("Freight", "OrderDetailQ1")
End Select
End Select
frm!Result = Rslt
strText = Cap(Opt)
Call Animate(strText) 'Label Animation
End Sub
Private Sub Animate(ByVal txt As String)
'Label Animation
Dim L As Double
Dim n As String
Dim T As Double
Dim j As Integer
L = Len(txt)
txt = Space(L) & txt
For j = 1 To Len(txt) - L
n = Left(txt, 1)
txt = Right(txt, Len(txt) - 1)
txt = txt & n
frm.lblResult.Caption = Left(txt, L)
Delay 0.02 'delay 20 milliseconds
Next
End Sub
</pre>
<p>The OptionGroup Control object <b>Opt</b> <a href="https://www.msaccesstips.com/2009/11/creating-using-form-custom-property.html" target="_blank">Property</a> is qualified with the Keyword <b>WithEvents</b> is declared in the global area of the Class Module. The Wrapper Class Module name is <b>OptFrame</b>. There is a Form object Property with the name <b>frm</b> also declared in the Global Area followed by the Property Procedures for the Global Properties.</p>
<h3 style="text-align: left;">The Opt_Click() Event Subroutine.</h3><pre>Private Sub opt_Click()
Dim Rslt As Variant
Dim Cap(1 To 3) As String
Static strText As String
Cap(1) = "Highest Freight Value:"
Cap(2) = " Lowest Freight Value:"
Cap(3) = " Total Freight Value:"
Select Case Opt.Name
Case "Frame7"
Select Case Opt.Value
Case 1
If strText = Cap(1) Then Exit Sub
Rslt = DMax("Freight", "OrderDetailQ1")
Case 2
If strText = Cap(2) Then Exit Sub
Rslt = DMin("Freight", "OrderDetailQ1")
Case 3
If strText = Cap(3) Then Exit Sub
Rslt = DSum("Freight", "OrderDetailQ1")
End Select
End Select
frm!Result = Rslt
strText = Cap(Opt)
Call Animate(strText) 'Label Animation
End Sub
</pre><p>There are three options in the OptionGroup Control on the Form to extract the Highest, Lowest, and Total Freight Values from the Order Sales transactions, for the selected Employee ID in the ComboBox <b>cboEmp</b>.</p><p>The OptionGroup Button Click will retrieve the values from <b>OrderDetailQ1</b> using the DMax(), DMin(), and DSum() Functions from the filtered Order Details data and display it in the <b>Rslt</b> TextBox, on the left side of the OptionGroup Control.</p><p>The displayed freight value's category description, defined in the <b>Cap()</b> Array is picked using the selected OptionGroup Control's Radio Button Index Number. This text is passed over to the <b>Animate()</b> Subroutine as the parameter. This is displayed on the Label control Caption at the left side of the TextBox <b>Rslt</b>. The Text is displayed in an <a href="https://www.msaccesstips.com/2007/07/animated-floating-calendar.html" target="_blank">animated</a> style, exposing the description character by character moving from right to left till the full text is exposed.</p><p>The statement <i>If strText = Cap(n) Then Exit Sub</i> ignores the Animation from repeating when Clicked on the Body of the OptionGroup Frame.</p>
<!-- Google In-Article Ads start -->
<script async="" src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle" data-ad-client="ca-pub-2376623373770548" data-ad-format="fluid" data-ad-layout="in-article" data-ad-slot="6147807460" style="display: block; text-align: center;"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
<!-- Google In-Article Ads END -->
<h3 style="text-align: left;">The Label Animation.</h3><p>The Animate() Subroutine Code segment is given below:</p>
<pre>Private Sub Animate(ByVal txt As String)
'Label Animation
Dim L As Double
Dim n As String
Dim T As Double
Dim j As Integer
L = Len(txt)
txt = Space(L) & txt 'Add spaces at the left side
For j = 1 To Len(txt) - L
n = Left(txt, 1)
txt = Right(txt, Len(txt) - 1)
txt = txt & n
frm.lblResult.Caption = Left(txt, L)
Delay 0.02 ' Pause 20 Milliseconds
Next
End Sub
</pre>
<p>The length of the Parameter value in the Variable <b>txt</b> is calculated and stored in variable L. The parameter variable <b>txt</b> content is modified by adding an equal number of spaces of its original length at the left side.</p><h3 style="text-align: left;">The Animation Sequence.</h3><p>The For . . . Next Loop is set to run to the original length in Variable L. In the next three lines of Code, remove one character from the left side of the String and add it to the right end of the String. Then the leftmost L length of <a href="https://www.msaccesstips.com/2009/04/filter-by-character-and-sort.html" target="_blank">characters</a> is displayed in the Caption Property of the <b>lblResult</b> Label Control. </p><p>The next batch of characters display is delayed by 20 milliseconds and the same action is repeated till the complete description is displayed on the Label Control.</p><p>The Delay() Function VBA Code in the Standard Module is given below for information:</p>
<pre style="text-align: left;">Public Sub Delay(ByVal Sleep As Double)
Dim T As Double
T = Timer
Do While Timer < T + Sleep
DoEvents
Loop
End Sub
</pre>
<h3 style="text-align: left;">The <b>OptObject_Init</b> Class Module VBA Code.</h3>
<pre>Option Compare Database
Option Explicit
Private iFrm As Access.Form
Private LstB As OptListBox
Private txt As OptTextBox
Private Fram As OptFrame
Private wcbo As optCombo
Private wcmd As OptCmdButton
Private Coll As New Collection
'------------------------------------------------------
'Streamlining Form Module Code
'in Stand-alone Class Modules
'------------------------------------------------------
'Combo and Option Group Controls
'Author: a.p.r. pillai
'Date : 31/08/2023
'Rights: All Rights(c) Reserved by www.msaccesstips.com
'------------------------------------------------------
'Form's Property GET/SET Procedures
Public Property Get m_Frm() As Form
Set m_Frm = iFrm
End Property
Public Property Set m_Frm(ByRef mfrm As Form)
Set iFrm = mfrm
iFrm.cboEmp.DefaultValue = iFrm.cboEmp.Column(0, 1)
iFrm.List0.Requery
Call Class_Init
End Property
'Events Enabling Subroutine
Private Sub Class_Init()
Dim ctl As Control
Const EP = "[Event Procedure]"
For Each ctl In iFrm.Controls 'Scan for Controls
Select Case TypeName(ctl)
Case "OptionGroup"
Select Case ctl.Name
Case "Frame7" 'Option Group Name
Set Fram = New OptFrame 'Create Instance
Set Fram.opt_Frm = iFrm 'Assign Form Object
Set Fram.o_opt = ctl 'TextBox
Fram.o_opt.OnClick = EP
Coll.Add Fram 'Save EmpTextBox Class
Set Fram = Nothing 'Erase temp Instance
End Select
Case "ComboBox"
Set wcbo = New optCombo
Set wcbo.cbo_Frm = iFrm
Set wcbo.c_cbo = ctl
wcbo.c_cbo.AfterUpdate = EP
wcbo.c_cbo.OnGotFocus = EP
wcbo.c_cbo.OnLostFocus = EP
Coll.Add wcbo
Set wcbo = Nothing
Case "TextBox"
Set txt = New OptTextBox
Set txt.tx_Frm = iFrm
Set txt.t_Txt = ctl
txt.t_Txt.OnGotFocus = EP
txt.t_Txt.OnLostFocus = EP
Coll.Add txt
Set txt = Nothing
Case "ListBox"
Set LstB = New OptListBox
Set LstB.lst_Frm = iFrm
Set LstB.m_Lst = ctl
LstB.m_Lst.OnGotFocus = EP
LstB.m_Lst.OnLostFocus = EP
Coll.Add LstB
Set LstB = Nothing
Case "CommandButton"
Set wcmd = New OptCmdButton
Set wcmd.cmd_Frm = iFrm
Set wcmd.c_cmd = ctl
wcmd.c_cmd.OnClick = EP
Coll.Add wcmd
Set wcmd = Nothing
End Select
Next
End Sub
Private Sub Class_Terminate()
'Delete Collection Object contents
Do While Coll.Count > 0
Coll.Remove 1
Loop
Set iFrm = Nothing
End Sub
</pre>
<p>As usual, all the Access Object Wrapper Classes are defined as Properties in the Global Area of the <a href="https://www.msaccesstips.com/2011/11/vba-module-object-and-methods.html" target="_blank">Module</a> followed by the Property Procedures. After the Form Object is assigned to the <b>iFrm</b> object, in the <b>Set m_frm()</b> Property Procedure two statements are executed for refreshing the ComboBox and ListBox objects to initialize with default values in both these Form Controls. From there the Class_Init() Subroutine is called.</p><p>The procedures written there are explained in detail in earlier episodes and I am sure you are well versed in the procedure.</p><p>But, I would like to draw your attention to the controls on the Form. There is only one instance each of Combobox, ListBox, TextBox, Command Button, OptionGroup Control, and Chart Object on the Form. Even though Chart Object also has several Events and can have a Wrapper Class, if we plan to capture those Events in a standalone Class Module.</p><h3 style="text-align: left;">When to create a Wrapper Class?</h3><p>There may be more than one instance of a particular object of the same type on the Form (like TextBox) but if there is only one instance of any Control, that needs one or more Event Subroutines, then it is not necessary to use the Wrapper Class for it. We can create a single Instance of such Objects in the Declaration area of the <b>OptObject_Init</b> Class Module (the intermediate Class Module), qualified with the <b>WithEvents</b> keyword. Then assign the Control instances references from the Form, enable their Events, and write the Event Subroutines in the OptObject_Init Class Module. </p><p>We need a Wrapper Class only when more than one instance of the Objects of the same Type needs to be enabled with the Event Procedures. In this frm_OptionGroup Form, there is only one Instance of the TextBox, ComboBox, ListBox, and Command Button on the Form.</p><p>Since the OptionGroup Control (Frame7) is the new entry, in the Streamlining of Form Module Code Series of Articles, we created a Wrapper Class <b>OptFrame </b>for that control alone, and for others we will create a single instance of each control qualified with the <b>WithEvents</b> keyword, to capture the Events in the OptObject_Init Class Module. They will be enabled with their required Events and write their Event Subroutines in this Intermediate Class Module.</p><p>Even though we have already created and used Wrapper Classes for these Objects earlier, using them in this case involves more VBA Code than necessary for a single instance for those Objects.</p><p>So, I created two Forms for Demo purposes:</p><p><b>frm_OptionGroup</b> - All Control's Wrapper Classes are used in the <b>OptObject_Init</b> Class</p><p><b>frm_OptionGroup2</b> - Only Opt_Frame Wrapper Class is in the <b>Opt_Object_Init2</b> Class.</p><h3 style="text-align: left;">The Opt_Object_Init2 Class Module Code.</h3><p>The frm_OptionGroup2 Form's Intermediate Class Module (Opt_Object_Init2) Code is given below:</p>
<pre style="text-align: left;">Option Compare Database
Option Explicit
Private WithEvents txt As Access.TextBox
Private WithEvents cmd As Access.CommandButton
Private WithEvents cbo As Access.ComboBox
Private WithEvents Lst As Access.ListBox
Private Fram As Opt_Frame2
Private iFrm As Access.Form
Private Coll As New Collection
'------------------------------------------------------
'Streamlining Form Module Code
'in Stand-alone Class Modules
'------------------------------------------------------
'Combo and Option Group Controls
'Author: a.p.r. pillai
'Date : 31/08/2023
'Rights: All Rights(c) Reserved by www.msaccesstips.com
'------------------------------------------------------
'Form's Property GET/SET Procedures
Public Property Get m_Frm() As Form
Set m_Frm = iFrm
End Property
Public Property Set m_Frm(ByRef mfrm As Form)
Set iFrm = mfrm
iFrm.cboEmp.DefaultValue = iFrm.cboEmp.Column(0, 1)
iFrm.List0.Requery
Set txt = iFrm.Result
Set cmd = iFrm.cmdClose
Set cbo = iFrm.cboEmp
Set Lst = iFrm.List0
Call Class_Init
End Property
'Events Enabling Subroutine
Private Sub Class_Init()
Dim ctl As Control
Const EP = "[Event Procedure]"
'Scan for Form Controls
'and Enable the required Event Procedures
For Each ctl In iFrm.Controls 'Find TextBox, ComboBox & CommandButtons
Select Case TypeName(ctl)
Case "OptionGroup"
Select Case ctl.Name
Case "Frame7" 'Option Group Name
Set Fram = New Opt_Frame2 'Create Instance
Set Fram.opt_Frm = iFrm 'Assign Form Object
Set Fram.o_opt = ctl 'TextBox
Fram.o_opt.OnClick = EP
Coll.Add Fram 'Save EmpTextBox Class
Set Fram = Nothing 'Erase temp Instance
End Select
Case "CommandButton"
cmd.OnClick = EP
Case "ComboBox"
cbo.AfterUpdate = EP
cbo.OnGotFocus = EP
cbo.OnLostFocus = EP
Case "TextBox"
Select Case ctl.Name
Case "Result"
txt.OnGotFocus = EP
txt.OnLostFocus = EP
End Select
Case "ListBox"
Lst.OnGotFocus = EP
Lst.OnLostFocus = EP
End Select
Next
End Sub
Private Sub Class_Terminate()
'Delete Collection Object contents
Do While Coll.Count > 0
Coll.Remove 1
Loop
Set iFrm = Nothing
End Sub
'Event Subroutines
Private Sub txt_GotFocus()
GFColor iFrm, txt 'Field Highlight
End Sub
Private Sub txt_LostFocus()
LFColor iFrm, txt 'Field Highlight
End Sub
Private Sub cmd_Click()
If MsgBox("Close " & iFrm.Name & " Form?", vbYesNo + vbQuestion, "cmd_Click") = vbYes Then
DoCmd.Close acForm, iFrm.Name
Exit Sub
End If
End Sub
Private Sub cbo_GotFocus()
GFColor iFrm, cbo 'ComboBox highlight
'Reset OptionGroup to default settings
iFrm.Frame7 = Null 'Reset earlier selection of OptionGroup option
iFrm!lblResult.Caption = "Result"
iFrm.Result.Value = 0
End Sub
Private Sub cbo_LostFocus()
LFColor iFrm, cbo 'ComboBox highlight
End Sub
Private Sub cbo_AfterUpdate()
iFrm.List0.Requery
End Sub
'Event Subroutines Code
Private Sub lst_GotFocus()
GFColor iFrm, Lst 'ListBox highlight
End Sub
Private Sub lst_LostFocus()
LFColor iFrm, Lst 'ListBox highlight
End Sub
</pre><h3 style="text-align: left;">Segmentwise Review of the VBA Code.</h3><p>The Global Declaration Code segment is given below for review:</p>
<pre>Option Compare Database
Option Explicit
Private WithEvents txt As Access.TextBox
Private WithEvents cmd As Access.CommandButton
Private WithEvents cbo As Access.ComboBox
Private WithEvents Lst As Access.ListBox
Private Fram As Opt_Frame2
Private iFrm As Access.Form
Private Coll As New Collection
</pre>
<p>All single object instance declarations are given at the global area of the Module, as we did at the beginning of this series of Tutorials.</p><p>Since OptionGroup Control is a new entry in this series its Wrapper Class is included here. The Form and Collection Object declarations come next. The Collection Object declaration is included only for the OptionGroup Control with the name <b>Frame7</b>.</p><h3 style="text-align: left;">The Property Procedure Segment.</h3><p>Next, the iFrm's Property Procedure Code Segment is given below:</p><pre>'Form's Property GET/SET Procedures
Public Property Get m_Frm() As Form
Set m_Frm = iFrm
End Property
Public Property Set m_Frm(ByRef mfrm As Form)
Set iFrm = mfrm
'Set the ComboBox EmployeeID first item as default value
iFrm.cboEmp.DefaultValue = iFrm.cboEmp.Column(0, 1)
iFrm.List0.Requery 'Refresh the Order Details ListBox
Set txt = iFrm.Result
Set cmd = iFrm.cmdClose
Set cbo = iFrm.cboEmp
Set Lst = iFrm.List0
Call Class_Init
End Property
</pre>
<p>In the <b>Set m_Frm()</b> Property Procedure the ComboBox and ListBox default values are assigned when the Form is open.</p><p>Next, all the single object references on the Form are assigned to the Objects declared in the global area and then Called the Class_Init() Subroutine.</p><h3 style="text-align: left;">The Class_Init() Subroutine.</h3><pre>'Events Enabling Subroutine
Private Sub Class_Init()
Dim ctl As Control
Const EP = "[Event Procedure]"
'Scan for Form Controls
'and Enable the required Event Procedures
For Each ctl In iFrm.Controls 'Find TextBox, ComboBox & CommandButtons
Select Case TypeName(ctl)
Case "OptionGroup"
Select Case ctl.Name
Case "Frame7" 'Option Group Name
Set Fram = New Opt_Frame2 'Create Instance
Set Fram.opt_Frm = iFrm 'Assign Form Object
Set Fram.o_opt = ctl 'TextBox
Fram.o_opt.OnClick = EP
Coll.Add Fram 'Save EmpTextBox Class
Set Fram = Nothing 'Erase temp Instance
End Select
Case "CommandButton"
cmd.OnClick = EP
Case "ComboBox"
cbo.AfterUpdate = EP
cbo.OnGotFocus = EP
cbo.OnLostFocus = EP
Case "TextBox"
Select Case ctl.Name
Case "Result"
txt.OnGotFocus = EP
txt.OnLostFocus = EP
End Select
Case "ListBox"
Lst.OnGotFocus = EP
Lst.OnLostFocus = EP
End Select
Next
End Sub
</pre>
<p>The Class_Init() Subroutine starts with the usual way of scanning for the Controls and checks for the OptionGroup control with the name <b>Frame7</b>, enables the Click Event, and then adds it to the Collection Object.</p><p>Next, other Form Controls' references are already assigned in the <b>Set m_frm()</b> Property Procedure and are enabled with the Events. We will be writing the Event Subroutine Code in this Class Module itself.</p><p>Next, in the TextBox's case, we have another TextBox (EID) that is kept hidden on the Form. Even though it is hidden it will appear in the scanning cycle and it will be enabled with the Events. Since there is no Event Procedure Code for that TextBox in this Module it will not have any impact. But, we would like to check for that specific TextBox (Resul) for clarity and enable its Events. This will ignore the EID TextBox. Next, the ListBox is also enabled with the required Events.</p><h3 style="text-align: left;">The Event Subroutines of Single Control Instance Cases.</h3><p>Next, the Event Subroutines Segment Code which runs in the Opt_Object_Init2 Class.</p>
<pre>'Event Runs automatically when the Form is Closed.
Private Sub Class_Terminate()
'Delete Collection Object contents
Do While Coll.Count > 0
Coll.Remove 1
Loop
Set iFrm = Nothing
End Sub
'TextBox Event Subroutines for highlighting the control
Private Sub txt_GotFocus()
GFColor iFrm, txt 'Field Highlight
End Sub
Private Sub txt_LostFocus()
LFColor iFrm, txt 'Field Highlight
End Sub
'Command Button Subroutines
Private Sub cmd_Click()
If MsgBox("Close " & iFrm.Name & " Form?", vbYesNo + vbQuestion, "cmd_Click") = vbYes Then
DoCmd.Close acForm, iFrm.Name
Exit Sub
End If
End Sub
'ComboBox Subroutines
Private Sub cbo_GotFocus()
GFColor iFrm, cbo 'ComboBox highlight
'Reset OptionGroup to default settings
iFrm.Frame7 = Null 'Reset earlier selection of OptionGroup option
iFrm!lblResult.Caption = "Result"
iFrm.Result.Value = 0
End Sub
Private Sub cbo_LostFocus()
LFColor iFrm, cbo 'ComboBox highlight
End Sub
Private Sub cbo_AfterUpdate()
iFrm.List0.Requery
End Sub
'ListBox Event Subroutines Code
Private Sub lst_GotFocus()
GFColor iFrm, Lst 'ListBox highlight
End Sub
Private Sub lst_LostFocus()
LFColor iFrm, Lst 'ListBox highlight
End Sub
</pre>
<p>All Event Subroutines are written with the Object Name declared in the Global Declaration Area in the Opt_Object_Init2 Class Module. </p><h4>Demo Database Download</h4>
<!--Download Link /downloads/2023/08/Streamline11.zip-->
<div class="separator" style="clear: both; text-align: center;">
<a href="https://drive.google.com/file/d/1NO2K2AJBHw5CXY2iAK5KqLn4O7ixaaba/view?usp=sharing">
<img border="0" data-original-height="29" data-original-width="22" height="29" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFE4uDLH9dk3S6pEcfTkT6GW6r11XqBI4HQnLnhQFZMKFeeQt5ABV_NqvLHM68J7RacISsvbUEl0PlAq6V7i0t0mQF3iesrBuqGuvrf5CJV72OzrGZ7rdMxkxzw8FYlz-qniNySoqtqUN4/s320/zip.gif" width="22" />Streamline11.zip</a>
</div>
<!--Download Link /downloads/2023/08/Streamline11.zip-->
<h3>Streamlining Form Module Code in Standalone Class Module.</h3>
<br />
<ol>
<li><a href="https://www.msaccesstips.com/2023/04/reusing-form-module-vba-code-for-new.html" target="_Blank">Re-using Form Module VBA Code for New Projects.</a></li>
<li><a href="https://www.msaccesstips.com/2023/04/streamlining-form-module-code-part-two.html" target="_Blank">Streamlining Form Module Code Part-Two</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/05/streamlining-form-module-code-part-three.html" target="_Blank">Streamlining Form Module Code Part-Three</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/06/streamlining-form-module-code-part-four.html" target="_Blank">Streamlining Form Module Code Part-Four</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/06/streamlining-form-module-code-part-five.html" target="_Blank">Streamlining Form Module Code Part-Five</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-six.html" target="_Blank">Streamlining Form Module Code Part-Six</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-seven.html" target="_Blank">Streamlining Form Module Code Part-Seven</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-eight.html" target="_Blank">Streamlining Form Module Code Part-Eight</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part-nine.html" target="_Blank">Streamlining Form Module Code Part-Nine</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part-ten.html" target="_Blank">Streamlining Form Module Code Part-Ten</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part.html" target="_Blank">Streamlining Form Module Code Part-Eleven</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-report-module-code-in.html" target="_Blank">Streamlining Report Module Code in Class Module</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-report-module-in-class.html" target="_Blank">Streamlining Report Module Code in Class Module-2</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-form-module-code-part-14.html" target="_Blank">Streamlining Form Module Code Part-14:All Controls</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-custom-made-form-wizard.html" target="_Blank">Streamlining Custom Made Form Wizard-15</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-form-vba-custom-report.html" target="_Blank">Streamlining Custom Report Wizard-16</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-form-vba-external-files.html" target="_Blank">Streamlining Form VBA External File Browser-17</a></li>
<li><a href="https://www.msaccesstips.com/2023/11/streamlining-event-procedures.html" target="_Blank">Streamlining Event Procedures of 3D TextWizard-18</a></li>
<li><a href="https://www.msaccesstips.com/2023/11/streamlining-event-procedures-rgbcolor.html" target="_Blank">Streamlining Event Procedures RGB Color Wizard-19</a></li>
<li><a href="https://www.msaccesstips.com/2023/12/streamlining-numbers-to-words-converter.html" target="_Blank">Streamlining Event Procedures Number To Words-20</a></li>
</ol>
a.p.r. pillaihttp://www.blogger.com/profile/02627590161919120214noreply@blogger.com5tag:blogger.com,1999:blog-7457417942281874810.post-30775999499621106112023-08-19T11:09:00.017+05:302024-01-07T22:53:38.944+05:30Streamlining Form Module Code - Part Ten <h3 style="text-align: left;">Creating Access Menu using Tabcontrol, ListBox, and Command Buttons.</h3>
<h4><span style="font-weight: normal;">We will take up the TabControl-based Menu design task after a few minutes.</span></h4>
<h3 style="text-align: left;">Organizing the Wrapper Classes for Different Forms.</h3><p>Let us see how we can organize the Wrapper Classes when there are several Forms and several Wrapper Classes for each Form in a database. They must be organized properly to avoid mix-ups like the wrapper class containing the Event Subroutines of Form A is added in the <b>WrapObject_Init </b>Class of Form B. </p>
<h3 style="text-align: left;">Wrapper Class Templates.</h3><p>Create one set of Wrapper Class Templates for all frequently used controls like <a href="https://www.msaccesstips.com/2019/07/withevents-textbox-commandbutton.html" target="_blank">TextBoxes</a>, CommandButtons, ComboBoxes, and others. Make Copies of Wrapper Classes and change their names, with some short name prefixes to the Wrapper Class Names like <b>Emp</b>TextBox (Emp for Employees <a href="https://www.msaccesstips.com/2021/01/treeview-control-with-subforms.html" target="_blank">Form</a>) to relate the Wrapper Classes to the Employee's Form. All other controls ComboBoxes, Command Buttons and others for the Employees Form will have the same name Prefix <b>Emp_TextBox</b> format, or <b>EmpTextBox</b> format, Emp_CmdButton, Emp_ComboBox, and so on.</p>
<!-- Google In-Article Ads start -->
<script async="" src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle" data-ad-client="ca-pub-2376623373770548" data-ad-format="fluid" data-ad-layout="in-article" data-ad-slot="6147807460" style="display: block; text-align: center;"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
<!-- Google In-Article Ads END -->
<p>After going through the earlier nine Articles on the Streamlining of Form <a href="https://www.msaccesstips.com/2019/05/withevents-in-class-module-and-data.html" target="_blank">Module</a> VBA Code topic you are now familiar with this new VBA Coding method and know how to create a new Wrapper Class if it is not available among the existing Templates when a new Control is added on the Form.</p><h3 style="text-align: left;">Forms With SubForms.</h3>
<p>Form with SubForms don't need any separate Classes, need only one Wrapper Class for the Main Form. You can see an example of the Employees Main Form with the Orders Subform in episode nine. The TextBox and ComboBox controls are scanned in the <b>EmpObject_Init</b> Class in a separate For . . . Next Loop with the SubForm Reference and how the SubForm <a href="https://www.msaccesstips.com/2011/11/control-setfocus-on-tab-page-click.html" target="_blank">Control</a> references are being mapped and added to the Collection Object. Their Event Subroutines can be written using the main Form Property like EmpTextBox wrapper Class Property name <b>Txt</b> for the TextBox on the Order SubForm like:</p>
<pre>'Data Update Reconfirm to Save the Change
Private Sub txt_BeforeUpdate(Cancel As Integer)
Dim msg As String
msg = "Field Name: " & Txt.Name & vbCr & _
"Original Value '" & UCase(Txt.OldValue) & "'" & _
vbCr & "Change to: '" & UCase(Txt.Value) & "'"
If MsgBox(msg, vbYesNo + vbQuestion, _
Txt.Name & "_BeforeUpdate()") = vbNo Then
Cancel = True
End If
End Sub
</pre>
<p>The <b>Txt</b> Object will have the correct reference of the SubForm TextBox and will execute the code for the TextBox in the Orders SubForm.</p><p>Let us see an example in the Sub Txt_GotFocus() Event Subroutine for not highlighting the '<b>OrderDate</b>'<b> </b>TextBox on the Orders SubForm in the Employees Main Form.</p>
<pre>Private Sub txt_GotFocus()
<span> </span>If Txt.Name = "OrderDate" Then
'No highlight
Else
GFColor frm, Txt
End If
End Sub</pre>
<p>We can address a control like any other control on the Main Form. The OrderDate field is not highlighted when it becomes current. </p><p>The next sample Code skips highlighting all TextBoxes on the Orders SubForm.</p>
<pre>Private Sub txt_GotFocus()
'https://learn.microsoft.com/en-us/office/vba/api/access.subform.parent
Select Case Txt.Parent.Name
Case "Orders" 'Orders SubForm has the parent Property set
'Do Nothing
Case Else
GFColor frm, Txt 'Field Highlight
End Select
End Sub
</pre>
<p>Some examples of Wrapper Class Names are given below.</p><p>Wrapper Classes for the Employees Form.</p>
<ul><li>EmpObject_Init</li>
<li>EmpTextBox</li>
<li>EmpCmdButton</li>
<li>EmpComboBox</li>
<li>EmpOptionGrp</li>
</ul>
<p>Wrapper Classes for Orders Main Form.</p>
<ul> <li>Order_Object_Init</li>
<li>Order_TextBox</li>
<li>Order_CmdButton</li>
<li>Order_ListBox</li>
<li>Order_TabCtl</li>
</ul>
<h3 style="text-align: left;">Reusing Streamlined Coding Procedures in Other Projects. </h3><p>Now the Question of how to Reuse the Classes in another Database?</p><p>Different Projects and different requirements. Most of the Subroutines written for a particular Form, based on its specific requirements, cannot be used without changes in another Project. But there are Event Subroutine Codes mentioned earlier (TextBox highlight, OnDirty, OnBeforeUpdate) or similar tasks you find can be used without change in other Projects. </p><p>In either case, the Backbone of this new streamlined Coding Procedure with less Code in the Wrapper Classes can be Exported into other Projects. Working with the Wrapper Classes for Coding independently and not mixing the Coding work with the Form design task will make the way for faster completion of Projects.</p><p>You can create a set of Wrapper Class Templates for frequently used Form Controls like:</p>
<ul>
<li>Access.TextBox</li><li>Access.CommandButton</li><li>Access.ComboBox and others</li></ul><div>with reusable Event Subroutines, like TextBox, ComboBox, ListBox highlighting OnGotFocus, OnLostFocus Event Subroutines, TextBox OnDirty, OnBeforeUpdate Event Subroutines, for safeguarding against inadvertent changes, and similar reusable code you find can be included in the Wrapper Class Templates. Any Standard Module-based Common Functions in use in the Event Subroutines, like the GFColor, and LFColor Functions we used for highlighting the <a href="https://www.msaccesstips.com/2008/10/textbox-and-label-inner-margins.html" target="_blank">TextBox</a> Controls must accompany the Wrapper Classes.</div><div><br /></div><div>All Wrapper Classes we create will have a <b>Form</b> Property along with the Control Object Property like <b>TextBox</b> and the SET/GET Property Procedures for both the above objects as their body. The Wrapper Class Templates can be saved in a separate database. The Classes saved in this database can have the prefix <b>.cls</b> like:</div>
<ul>
<li>clsObject_Init</li>
<li>clsTextBox</li>
<li>clsCmdButton and others.</li></ul><h3 style="text-align: left;">How to use the Code from the Template Database.</h3>
<ol><li><p>Attach this database to your New Project as a Library database.</p></li>
<li>Create a new Class Module in the new Database.</li>
<li><p>Change its name to match the first Wrapper Class in the attached database.</p></li>
<li>Right-click on the Wrapper Class Module of the attached <a href="https://www.msaccesstips.com/2008/05/database-daily-backup.html" target="_blank">Database</a> and select <b>View Code</b> from the displayed shortcut Menu. The Code will be displayed in the VBA Window.</li>
<li><p>Copy the Code and Paste it into the new Class Module and save it.</p></li>
<li>Repeat this procedure to transfer all the Wrapper Class Templates to the New Database.</li>
<li><p>Then remove the attached database from the Reference Library.</p></li>
</ol>
<h3 style="text-align: left;"><b>Alternative Method.</b></h3><p>Even better, you can Export the Wrapper Class Templates from the VBA <a href="https://www.msaccesstips.com/2022/04/access-and-windows-api-showwindow.html" target="_blank">Window</a> into a dedicated Folder in the Disk-Drive as separate Class Files with the file extension .<b>cls</b>, and you can Import them one by one into your new Projects.</p><h3 style="text-align: left;">Access Menu with TabControl.</h3><p>The finished Menu View Image is given below:</p>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh3WFJqk4sxxFMXIFJyWK68EV5YKXHe6G3TyWdlj3_abquFHpaoAMUx7tWCHhOyUiepBBEFrNW24oHM5ZOsFhvJSOY2y9Vs1H3meWQhPCjtGp16I4Pr4Sq-3Y5etsvpnQcCXfCZejyOMNcoDF6EVELQhU-N5ReOk5uMz1WhWFAwQs2XRe53KqcfzOnBHjUf/s772/StreamLine101.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="590" data-original-width="772" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh3WFJqk4sxxFMXIFJyWK68EV5YKXHe6G3TyWdlj3_abquFHpaoAMUx7tWCHhOyUiepBBEFrNW24oHM5ZOsFhvJSOY2y9Vs1H3meWQhPCjtGp16I4Pr4Sq-3Y5etsvpnQcCXfCZejyOMNcoDF6EVELQhU-N5ReOk5uMz1WhWFAwQs2XRe53KqcfzOnBHjUf/s320/StreamLine101.png" width="320" /></a></div>
<p>There are three layers of Menu Options (Tables, Forms, and Reports) that will appear in the same place when selected by clicking on the <a href="https://www.msaccesstips.com/2006/09/command-button-animation.html" target="_blank">Command Buttons</a> given on the left side.</p><p>The Menu Design Image is given below. </p>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_CFYhhNC0kTb3DirIpnaC66oLNVEMi5GWaV4NY0HPJraPtxJ39btOp2cns0o-rWHWwKElboyIiT_fXqT_6X7jOOLeLxE-_nIpcjPtP7tGasr0px3IBuCxdgEpqcmFer5GS4rWPNa2Y-gG0Fh14Lusemziemt0O10D3SlVKg0dq1iGA8OKEhrU8gPrSDHZ/s867/Streamline102.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="716" data-original-width="867" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_CFYhhNC0kTb3DirIpnaC66oLNVEMi5GWaV4NY0HPJraPtxJ39btOp2cns0o-rWHWwKElboyIiT_fXqT_6X7jOOLeLxE-_nIpcjPtP7tGasr0px3IBuCxdgEpqcmFer5GS4rWPNa2Y-gG0Fh14Lusemziemt0O10D3SlVKg0dq1iGA8OKEhrU8gPrSDHZ/s320/Streamline102.png" width="320" /></a></div>
<p>There are three Pages in the Tab Control, with Page Names: <b>Tables</b>, <b>Forms</b>, and <b>Reports</b>.</p>
<p>Each Tab Page has a ListBox Control with the same dimension and is positioned on the same left and top values. The idea is to display all the Menu Pages: Tables, Forms, and Reports one after the other in the same position on the TabControl.</p><p>The following Property settings will change the display style of the Tab Control to the style shown in the first Image given above:</p><p></p><ul style="text-align: left;"><li>Tab Style: None</li><li>Back Style: Transparent</li><li>Border Style: Transparent</li></ul><p></p><p>When the Tab Pages are hidden the <a href="https://www.msaccesstips.com/2008/03/double-action-command-button.html" target="_blank">Command Button</a> Click will change the Pages and the Page-Change Event will Fire. The Click Event of the Command Button will highlight the Border of the Command Button to indicate the current selection. Besides that, the current Menu selection is announced by a female Voice.</p><p>Double-clicking on the List Item will display the Table or Form or Report on the screen. This action also will announce the type of object that is currently on display.</p><p>The ListBox Menu Source Items Employees, Orders, and Customers are added in the Value List in the format:</p><pre>1;"Employees";2;"Orders";3;"Customers"</pre><p>for all three Menus Tables, Forms & Reports.</p><h3>The TabLst_Object_Init Class</h3> <p>The TabLst_Objectr_Init Class Module Code is given below:</p>
<pre>Option Compare Database
Option Explicit
Private iFrm As Access.Form
Private Coll As New Collection
Private tbc As TabLst_TabCtl
Private wcmd As TabLst_CmdButton
Private lst As TabLst_ListBox
'------------------------------------------------------
'Streamlining Form Module Code
'in Stand-alone Class Modules
'------------------------------------------------------
'Tab Control Class_Init Class
'Author: a.p.r. pillai
'Date : 16/08/2023
'Rights: All Rights(c) Reserved by www.msaccesstips.com
'------------------------------------------------------
'Form's Property GET/SET Procedures
Public Property Get m_Frm() As Form
Set m_Frm = iFrm
End Property
Public Property Set m_Frm(ByRef mfrm As Form)
Set iFrm = mfrm
Call Class_Init
End Property
'Events Enabling Subroutine
Private Sub Class_Init()
Dim ctl As Control
Const EP = "[Event Procedure]"
'Scan for Tab Control, CommandButton & ListBox Controls
'and Enable the required Event Procedures
For Each ctl In iFrm.Controls
Select Case ctl.ControlType
Case <span style="color: red;">acTabCtl</span>
Set tbc = New TabLst_TabCtl
Set tbc.tb_Frm = iFrm
Set tbc.tb_tab = ctl
<span style="color: red;">tbc.tb_tab.OnChange = EP</span>
Coll.Add tbc
Set tbc = Nothing
Case <span style="color: red;">acListBox</span>
Set lst = New TabLst_ListBox
Set lst.lst_Frm = iFrm
Set lst.m_lst = ctl
<span style="color: red;">lst.m_lst.OnDblClick = EP</span>
Coll.Add lst
Set lst = Nothing
Case acCommandButton
Select Case ctl.Name
Case <span style="color: red;">"cmdTables", "cmdForms", "cmdReports", "cmdExit"</span>
Set wcmd = New TabLst_CmdButton
Set wcmd.cmd_Frm = iFrm
Set wcmd.c_cmd = ctl
<span style="color: red;">wcmd.c_cmd.OnClick = EP</span>
Coll.Add wcmd
Set wcmd = Nothing
End Select
End Select
Next
End Sub
Private Sub Class_Terminate()
'Delete Collection Object contents
Do While Coll.Count > 0
Coll.Remove 1
Loop
Set iFrm = Nothing
End Sub
</pre>
<p>In the Global Declaration area, the Form and Collection Property declarations are made, followed by the Tab Control, Command Button, and ListBox Wrapper Class Property Declarations.</p><p>Next, the <b>Set/Get</b> <a href="https://www.msaccesstips.com/2008/09/source-connect-str-property-and-odbc.html" target="_blank">Property</a> Procedures for the Form. The Class_Init() Subroutine is Called From the Set Property Procedure of the Form object. In the For . . . Next Loop we look for our TabCtl, ListBox, and Command Button Controls.</p><p>The TabCtl control Wrapper Class TabLst_TabCtl Instance Properties tb_Frm and tb_Tab are assigned with the <b>iFrm</b>, <b>ctl</b> References. The TabCtl Control's <b>Tab Page Change</b> Event is enabled and then the TabLst_TabCtl Wrapper Class Instance is added to the Collection Object.</p>
<p>All three ListBox Properties are enabled with the <b>DblClick()</b> Event in the TabLst_ListBox Wrapper Class Instances and added to the Collection Object. Similarly, the Command Button Wrapper Class TabLst_CmdButton Instances are enabled with the <b>Click</b> Event and added to the Collection Object.</p>
<h3>The TabLst_TabCtl Class</h3>
<p>The TabLst_TabCtl Wrapper Class Module Code is given below.</p>
<pre>Option Compare Database
Option Explicit
Private tbfrm As Access.Form
Private WithEvents Tb As Access.TabControl
'------------------------------------------------------
'Streamlining Form Module Code
'in Stand-alone Class Modules
'------------------------------------------------------
'TabCtl Wrapper Class
'Author: a.p.r. pillai
'Date : 16/08/2023
'Rights: All Rights(c) Reserved by www.msaccesstips.com
'------------------------------------------------------
'Form's Property GET/SET Procedures
Public Property Get tb_Frm() As Form
Set tb_Frm = tbfrm
End Property
Public Property Set tb_Frm(ByRef tabfrm As Form)
Set tbfrm = tabfrm
End Property
'TextBox Property GET/SET Procedures
Public Property Get tb_tab() As Access.TabControl
Set tb_tab = Tb
End Property
Public Property Set tb_tab(ByRef ptab As Access.TabControl)
Set Tb = ptab
End Property
'Event Subroutines Code
Private Sub tb_Change()
Select Case Tb.Value
Case 0
MsgBox "Change Event: Page(0)"
Case 1
MsgBox "Change Event: Page(1)"
Case 2
MsgBox "Change Event: Page(2)"
End Select
End Sub
</pre>
<p>When the TabControl TabPage Change Event is fired will be captured here and display the Page Index Number in the MsgBox.</p>
<h3>The Command Button Wrapper Class.</h3>
<pre>
Option Compare Database
Option Explicit
Private WithEvents cmdfrm As Form
Private WithEvents cmd As CommandButton 'CommandButton object
Dim L As Integer
Dim ForeColor As Long
'------------------------------------------------------
'Streamlining Form Module Code
'in Stand-alone Class Modules
'------------------------------------------------------
'Command Button Events
'Author: a.p.r. pillai
'Date : 16/08/2023
'Rights: All Rights(c) Reserved by www.msaccesstips.com
'------------------------------------------------------
'Form's Property GET/SET Procedures
Public Property Get cmd_Frm() As Form
Set cmd_Frm = cmdfrm
End Property
Public Property Set cmd_Frm(ByRef cfrm As Form)
Set cmdfrm = cfrm
End Property
'TextBox Property GET/SET Procedures
Public Property Get c_cmd() As CommandButton
Set c_cmd = cmd
End Property
Public Property Set c_cmd(ByRef pcmd As CommandButton)
Set cmd = pcmd
End Property
'Event Subroutines
Private Sub cmd_Click()
Select Case cmd.Name
Case "cmdExit"
Announce "Close the Form Now?"
If MsgBox("Close the Form Now?", vbOKCancel + vbQuestion, "cmd_Click") = vbOK Then
DoCmd.Close acForm, cmdfrm.Name
Exit Sub
End If
Case "cmdTables"
'Command Button Border Highlight
cmd.BorderWidth = 2
cmdfrm.cmdForms.BorderWidth = 0
cmdfrm.cmdReports.BorderWidth = 0
cmdfrm.TabCtl0.Pages(0).SetFocus
Announce "Tables Menu Active."
Case "cmdForms"
'Command Button Border Highlight
cmd.BorderWidth = 2
cmdfrm.cmdTables.BorderWidth = 0
cmdfrm.cmdReports.BorderWidth = 0
cmdfrm.TabCtl0.Pages(1).SetFocus
Announce "Forms Menu Active."
Case "cmdReports"
'Command Button Border Highlight
cmd.BorderWidth = 2
cmdfrm.cmdForms.BorderWidth = 0
cmdfrm.cmdTables.BorderWidth = 0
cmdfrm.TabCtl0.Pages(2).SetFocus
Announce "Reports Menu Active."
End Select
End Sub
</pre>
<p>The three Command Buttons to the left side of the TabControl is the replacement of TabControl Page Buttons to hide the identity features of the TabControl. This is the secret of several layers of different Menus appearing one at a time in the same location. More layers of Menu Pages can be added to the TabControl with <a href="https://www.msaccesstips.com/2009/09/dynamic-listbox-combobox-contents.html" target="_blank">ListBoxes</a> and Command Button.</p><p>The following Properties of the TabControl are set to hide the features of the TabControl:</p>
<ol><li>Style: None </li>
<li>Back Style: Transparent </li>
<li>Border Style: Transparent </li>
</ol>
<h3>The ListBox Wrapper Class.</h3>
<pre>Option Compare Database
Option Explicit
Private lstfrm As Access.Form
Private WithEvents lst As Access.ListBox
'------------------------------------------------------
'Streamlining Form Module Code
'in Stand-alone Class Modules
'------------------------------------------------------
'ListBox Wrapper Class
'Author: a.p.r. pillai
'Date : 16/08/2023
'Rights: All Rights(c) Reserved by www.msaccesstips.com
'------------------------------------------------------
'Form's Property GET/SET Procedures
Public Property Get lst_Frm() As Form
Set lst_Frm = lstfrm
End Property
Public Property Set lst_Frm(ByRef mFrm As Form)
Set lstfrm = mFrm
End Property
'TextBox Property GET/SET Procedures
Public Property Get m_lst() As ListBox
Set m_lst = lst
End Property
Public Property Set m_lst(ByRef mLst As ListBox)
Set lst = mLst
End Property
Private Sub lst_DblClick(Cancel As Integer)
Dim i As Integer
Dim Menu(1 To 3) As String
Dim Obj(1 To 3) As String
i = Nz(lst.Value, 0)
Const Opn = "Opening "
Menu(1) = "Table "
Menu(2) = "Form "
Menu(3) = "Report "
Obj(1) = "Employees"
Obj(2) = "Orders"
Obj(3) = "Customers"
Select Case lst.Name
Case "LstTables"
Select Case i
Case 1, 2, 3
Announce Opn & Menu(1) & Obj(i) 'Speak
DoCmd.OpenTable Obj(i), acViewNormal
End Select
Case "LstForms"
Select Case i
Case 1, 2, 3
Announce Opn & Menu(2) & Obj(i) 'Speak
DoCmd.OpenForm Obj(i), acViewNormal
End Select
Case "LstReports"
Select Case i
Case 1, 2, 3
Announce Opn & Menu(3) & Obj(i) 'Speak
DoCmd.OpenReport Obj(i), acViewReport
End Select
End Select
End Sub
</pre>
<p>At the beginning of the Listbox's Double-Click Event Subroutine, a few Array Variables are Initialised with the Menu Names and menu item names to compose the Speech Text to Announce the Menu item selection. This method also reduces the <a href="https://www.msaccesstips.com/2006/10/file-browser-in-msaccess.html" target="_blank">File</a> opening statements from six to two lines each (excluding the array lines) for Tables, Forms, and Reports. </p><p>The normal Coding will look like the following:</p>
<pre>Select Case lst.Name
Case "LstTables"
Select Case i
Case 1
Announce "Opening Table Employees" 'Speak
DoCmd.OpenTable "Employees", acViewNormal
Case 2
Announce "Opening Table Orders" 'Speak
DoCmd.OpenTable "Orders", acViewNormal
Case 3
Announce "Opening Table Customers" 'speak
DoCmd.OpenTable "Customers", acViewNormal
End Select
Case "LstForms"
Select Case i
Case 1
Announce "Opening Form Employees" 'Speak
DoCmd.OpenForm "Employees", acViewNormal
Case 2
Announce "Opening Form Orders" 'Speak
DoCmd.OpenForm "Orders", acViewNormal
Case 3
Announce "Opening Form Customers" 'speak
DoCmd.OpenForm "Customers", acViewNormal
End Select
Case "LstReports"
Select Case i
Case 1
Announce "Opening Report Employees" 'Speak
DoCmd.OpenReport "Employees", acViewReport
Case 2
Announce "Opening Report Orders" 'Speak
DoCmd.OpenReport "Orders", acViewReport
Case 3
Announce "Opening Report Customers" 'speak
DoCmd.OpenReport "Customers", acViewReport
End Select
End Select
</pre>
<p>Microsoft Speech-Service VBA Code is given below. The Subroutine Code is in the Standard Module. </p>
<pre>Sub Announce(ByVal Txt As String, Optional Gender As String = "Female")
'https://learn.microsoft.com/en-us/azure/ai-services/speech-service/
Dim obj As Object
Set obj = CreateObject("SAPI.SpVoice")
Set obj.Voice = obj.GetVoices("Gender = " & Gender).Item(0)
obj.Speak Txt
End Sub
</pre>
<p>The TabLst_ListBox Wrapper Class DblClick() Event Subroutines Runs the Menu Options Tables, Forms, and Reports opening activities. </p><p> </p>
<!--Download Link /downloads/2023/08/Streamline10.zip-->
<div class="separator" style="clear: both; text-align: center;">
<a href="https://drive.google.com/file/d/1P_tanaSG3qE_Gl9mBMlvvWPDMyEs3BY9/view?usp=sharing">
<img border="0" data-original-height="29" data-original-width="22" height="29" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFE4uDLH9dk3S6pEcfTkT6GW6r11XqBI4HQnLnhQFZMKFeeQt5ABV_NqvLHM68J7RacISsvbUEl0PlAq6V7i0t0mQF3iesrBuqGuvrf5CJV72OzrGZ7rdMxkxzw8FYlz-qniNySoqtqUN4/s320/zip.gif" width="22" />Streamline10.zip</a>
</div>
<!--Download Link /downloads/2023/08/Streamline10.zip-->
<h3>Streamlining Form Module Code in Standalone Class Module.</h3>
<br />
<ol>
<li><a href="https://www.msaccesstips.com/2023/04/reusing-form-module-vba-code-for-new.html" target="_Blank">Re-using Form Module VBA Code for New Projects.</a></li>
<li><a href="https://www.msaccesstips.com/2023/04/streamlining-form-module-code-part-two.html" target="_Blank">Streamlining Form Module Code Part-Two</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/05/streamlining-form-module-code-part-three.html" target="_Blank">Streamlining Form Module Code Part-Three</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/06/streamlining-form-module-code-part-four.html" target="_Blank">Streamlining Form Module Code Part-Four</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/06/streamlining-form-module-code-part-five.html" target="_Blank">Streamlining Form Module Code Part-Five</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-six.html" target="_Blank">Streamlining Form Module Code Part-Six</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-seven.html" target="_Blank">Streamlining Form Module Code Part-Seven</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-eight.html" target="_Blank">Streamlining Form Module Code Part-Eight</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part-nine.html" target="_Blank">Streamlining Form Module Code Part-Nine</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part-ten.html" target="_Blank">Streamlining Form Module Code Part-Ten</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part.html" target="_Blank">Streamlining Form Module Code Part-Eleven</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-report-module-code-in.html" target="_Blank">Streamlining Report Module Code in Class Module</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-report-module-in-class.html" target="_Blank">Streamlining Report Module Code in Class Module-2</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-form-module-code-part-14.html" target="_Blank">Streamlining Form Module Code Part-14:All Controls</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-custom-made-form-wizard.html" target="_Blank">Streamlining Custom Made Form Wizard-15</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-form-vba-custom-report.html" target="_Blank">Streamlining Custom Report Wizard-16</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-form-vba-external-files.html" target="_Blank">Streamlining Form VBA External File Browser-17</a></li>
<li><a href="https://www.msaccesstips.com/2023/11/streamlining-event-procedures.html" target="_Blank">Streamlining Event Procedures of 3D TextWizard-18</a></li>
<li><a href="https://www.msaccesstips.com/2023/11/streamlining-event-procedures-rgbcolor.html" target="_Blank">Streamlining Event Procedures RGB Color Wizard-19</a></li>
<li><a href="https://www.msaccesstips.com/2023/12/streamlining-numbers-to-words-converter.html" target="_Blank">Streamlining Event Procedures Number To Words-20</a></li>
</ol>
<p></p>a.p.r. pillaihttp://www.blogger.com/profile/02627590161919120214noreply@blogger.com2tag:blogger.com,1999:blog-7457417942281874810.post-64380787418667536562023-08-07T21:59:00.014+05:302024-01-07T22:53:01.690+05:30Streamlining Form Module Code - Part Nine<h3 style="text-align: left;"> Introduction.</h3><p>After going through the last eight episodes of the Title Topic I assume that you are familiar with this new coding procedure and realized its advantages. By implementing this new stand-alone Class Module-based coding approach, we can achieve more functionality with significantly less VBA code.</p>
<p><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-seven.html" target="_blank">Episode number Seven</a> demonstrated how we could highlight the entire Array of TextBoxes on the Form, by running the OnGotFocus() Subroutine and resetting the highlight to the earlier state in the LostFocus() Event Subroutines, with only the following six lines of VBA Code in the WrapTextBox Class. The GFColor() and LFColor() public functions in the Standard Module are called from the GotFocus() and LostFocus() <a href="https://www.msaccesstips.com/2019/05/access-form-control-arrays-and-event.html" target="_blank">TextBox</a> Event Subroutines in the WrapTextBox Class respectively.</p>
<pre>Private Sub txt_GotFocus()
GFColor frm, Txt 'Field Highlight
End Sub
Private Sub txt_LostFocus()
LFColor frm, Txt 'Field Highlight
End Sub
</pre>
<!-- Google In-Article Ads start -->
<script async="" src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle" data-ad-client="ca-pub-2376623373770548" data-ad-format="fluid" data-ad-layout="in-article" data-ad-slot="6147807460" style="display: block; text-align: center;"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
<!-- Google In-Article Ads END -->
<p>If you add more TextBoxes on the Form or delete existing ones and not necessary to make any changes to the VBA Code.</p><p>Similarly, the following small code snippet monitors all the TextBox controls on the Employees Form to monitor any attempt to modify the value in any one of the TextBoxes on the Form. This applies to all TextBoxes on the main form Employees and the SubForm Orders as well.</p><h3 style="text-align: left;">The OnDirty Event Subroutine.</h3>
<pre>Private Sub txt_Dirty(Cancel As Integer)
If MsgBox("Editing the " & UCase(Txt.Name) _
& ": Value? " & Txt.Value, vbYesNo + vbQuestion, _
Txt.Name & " DIRTY()") = vbNo Then
Cancel = True
End If
End Sub
</pre>
<p>The code snippet above effectively monitors all TextBoxes within the Main Form "Employees" and the Sub-Form "Orders" to prevent inadvertent changes. When the user attempts to edit a field, a warning message is displayed, and the user must confirm his/her intention to change the field value before they can proceed with editing. If the user realizes that it was a mistake then they have the option to cancel the Event and revert the field to its original value. This implementation helps to prevent accidental modifications and ensures data integrity. </p>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg3EcGlOK6aam2GmooEv8KJoAoD-0wRRmKdTIIecRDps9DUvFSoZxr_s1NxQyCxkIaLW1IbXn6_tJeww-tugZOgnPwBH2nLCP9CD5pyTxV8bOhXgUFWt1q8Y05IxvO161IzhPVQ7SIsuUwQSIkgv0YzvaCPWLOy9pt2KEFrusyWLnPcl5MtUcXsH9aFjGW_/s819/SLine9.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="682" data-original-width="819" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg3EcGlOK6aam2GmooEv8KJoAoD-0wRRmKdTIIecRDps9DUvFSoZxr_s1NxQyCxkIaLW1IbXn6_tJeww-tugZOgnPwBH2nLCP9CD5pyTxV8bOhXgUFWt1q8Y05IxvO161IzhPVQ7SIsuUwQSIkgv0YzvaCPWLOy9pt2KEFrusyWLnPcl5MtUcXsH9aFjGW_/s320/SLine9.png" width="320" /></a></div>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhpAZrzMDltxS6mzSxfFCGkYyqUwrN18J3FEDKV52j5GJUDKlie0H5vpjpt14S9Sit1LsXNU8L69ONdVDYlRaXQo68SnAM6yJh60Pb2vBz20UDqml_DganfESb2ld5YmoSu8WJzPY0HUUWCnhMhFo475k-xPfqh4tIQiSJbysO2k_ni5DYvAvD_TADRgxoB/s784/SLine91.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="672" data-original-width="784" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhpAZrzMDltxS6mzSxfFCGkYyqUwrN18J3FEDKV52j5GJUDKlie0H5vpjpt14S9Sit1LsXNU8L69ONdVDYlRaXQo68SnAM6yJh60Pb2vBz20UDqml_DganfESb2ld5YmoSu8WJzPY0HUUWCnhMhFo475k-xPfqh4tIQiSJbysO2k_ni5DYvAvD_TADRgxoB/s320/SLine91.png" width="320" /></a></div>
<p>The TextBoxes on the Orders SubForm are also under the surveillance of this Code.</p><h3 style="text-align: left;">The BeforeUpdate Event Subroutine.</h3><p>When you modify data and press the Enter key, the BeforeUpdate event procedure, responsible for safeguarding the entire array of TextBoxes on the forms, will be triggered. All these events are then captured within the same <i>Sub txt_BeforeUpdate()</i> event subroutine, as shown in the code provided below.
</p>
<pre>'Data Update Reconfirm to Save the Change
Private Sub txt_BeforeUpdate(Cancel As Integer)
If MsgBox("Field Name: " & Txt.Name & vbCr & _
"Original Value '" & UCase(Txt.OldValue) & "'" & _
vbCr & "Change to: '" _
& UCase(Txt.Value) & "'", vbYesNo + vbQuestion, _
UCase(Txt.Name) & " BeforeUpdate()?") = vbNo Then
Cancel = True
End If
End Sub
</pre>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiUcY1FDiUdg8MLX78MSt9iupvqdnEAS6qfvp-BvmvS2BJquVAWZ8_DFNyUVIXSFBaA4vX_TisUoLCQMGDWU-0dQ8dYvkOGawjGNhiz25DckP9WEudz6HGVqJVy5CbHOM8LEDBdNF08PBnG9hhtrAnq089VuT93lXq1W9gA-AdSEqbndkVO-hvQB21_vdFM/s933/SLine92.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="669" data-original-width="933" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiUcY1FDiUdg8MLX78MSt9iupvqdnEAS6qfvp-BvmvS2BJquVAWZ8_DFNyUVIXSFBaA4vX_TisUoLCQMGDWU-0dQ8dYvkOGawjGNhiz25DckP9WEudz6HGVqJVy5CbHOM8LEDBdNF08PBnG9hhtrAnq089VuT93lXq1W9gA-AdSEqbndkVO-hvQB21_vdFM/s320/SLine92.png" width="320" /></a></div>
<p>Again, the User has to reconfirm to save the data in the Field.</p><h3 style="text-align: left;">Reusability of Streamlined VBA Code Writing.</h3>
<p>The key advantage of this Streamlined VBA Code writing is its reusability. Instead of writing the event handling code for each TextBox individually in the Form Module, you can write it just once in a TextBox Wrapper Class Module. This Class Module acts as a template for handling <i>BeforeUpdate()</i> events for all TextBox controls. </p>
<p>Here's how it works:</p>
<ol>
<li><p>Write the BeforeUpdate event handling code in the TextBox Wrapper Class Module.</p></li>
<li>Instantiate the Wrapper Class Module for each TextBox in the Main Form and Subform.</li>
<li><p>Assign the references of the Form and TextBox controls to their corresponding properties in the Wrapper Class Module.</p></li>
<li><p>Enable the required event procedures (e.g., BeforeUpdate) in the Wrapper Class <b>EmpObject_Init</b> Module.</p></li>
<li>Store the instantiated Wrapper Class <b>EmpTextBox</b> in memory through the <a href="https://www.msaccesstips.com/2019/02/dictionary-object-basics.html" target="_blank">Collection</a> Object. </li>
</ol>
<p>By following this approach, you can efficiently manage and handle events for multiple TextBoxes on different forms without duplicating code. It promotes code organization, reduces redundancy, and makes it easier to maintain and update the event-handling logic in the future.</p><p>So far we have not tried any example with the Main Form with a SubForm setting, how to reference the Controls on the Subform, enable their Events, and how to streamline the Code for both Forms.</p><p>In this episode, we will use the Employees Table as the record source for the Employees' Main Form. The sub-Form Orders has the Orders Table as a Record Source and is designed as a Tabular Form.</p>
<!-- Google In-Article Ads start -->
<script async="" src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle" data-ad-client="ca-pub-2376623373770548" data-ad-format="fluid" data-ad-layout="in-article" data-ad-slot="6147807460" style="display: block; text-align: center;"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
<!-- Google In-Article Ads END -->
<p>We will learn how to address the Subform Controls (TextBox and ComboBox) in the <b>EmpObject_Init</b> Wrapper Class. Enable their required Events and save the Wrapper Class instances into the Collection Object, as we did earlier. </p><p>The Control Wrapper Class (EmpTextBox) instances, enabled with their required Events stay in memory as the <a href="https://www.msaccesstips.com/2019/01/table-records-in-collection-object-and.html" target="_blank">Collection Object </a>Items and capture the Events fired from the TextBoxes on the Form and SubForm and execute the Event Subroutines.</p><p>In the last episode, we created a new Wrapper Class EmpCmdButton for the Command Button Class Object and added it as the Property of the EmpObject_Init Class.</p><p>This time we need a new Wrapper Class <b>EmpCombo</b> for the ComboBox Control to track the Events fired from the <a href="https://www.msaccesstips.com/2013/12/updating-combobox-when-not-in-list-is.html" target="_blank">ComboBoxes</a> from the Main Form and Subform.</p><p>On the Employee Form's Footer Area, there is a TextBox Control to Search and Find the Employee Record by using EmployeeID as the search Key.</p><p>After the search operation, it will flash a Label Control (Label Animation) with a message a few times to announce whether the Search was successful or not and then disappear.</p><p>The big question here is, where we will write the Code for the Data Search and Find operations and run the Label Animations. </p><p>If you recollect the last episode (Part Eight) we used a Form-Close Countdown display on the Form running in the <b>WrapCmdButton Class</b>, within the <b>Sub cmd_Click()</b> Event Subroutine. The Countdown display was in a Label control on the Form. The digital clock was running in a Label Control on the Form, but the Form TimerInterval settings Code was running from the WrapObject_Init Wrapper Class.</p><p>Similarly, there is another common task to run from the <a href="https://www.msaccesstips.com/2013/10/form-footer-section-dropping-down-magic.html" target="_blank">Form footer</a> area. There is an EmployeeID Search and Find unbound TextBox at the Footer of the Form. The EmployeeID value will be entered into the <b>FindID</b>TextBox control, to find the record on the Employees Form<b>. </b>The question is in which Wrapper Class we will write the VBA Code for the search and find operations? </p>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj9Q4AqerowYYOnFCGkBf29573OP_7chNMo8VRnSqJ3u7-zRruaR3_LTIqEaT48r8rgYuWmBWKTN-S-Idw0gCn21XLeLAycAHYhtGkK9oWH-cXxMAXCgnK3sj6E8QlLYEqMgO6yYSaW1McSLYdfSbFffclLEvbyspOQc6MA_cKbP0Vj5LonTwTvPyLClgNC/s1007/SLine93.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="666" data-original-width="1007" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj9Q4AqerowYYOnFCGkBf29573OP_7chNMo8VRnSqJ3u7-zRruaR3_LTIqEaT48r8rgYuWmBWKTN-S-Idw0gCn21XLeLAycAHYhtGkK9oWH-cXxMAXCgnK3sj6E8QlLYEqMgO6yYSaW1McSLYdfSbFffclLEvbyspOQc6MA_cKbP0Vj5LonTwTvPyLClgNC/s320/SLine93.png" width="320" /></a></div>
<p>Besides that, there is a <a href="https://www.msaccesstips.com/2010/05/label-animation-in-colors.html" target="_blank">Label</a> Control below the FindID TextBox, to display the result of the search operation.</p>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7QgX41VL93m9B6VumwH6UnslAfTafsxLuawDVpDDBn9ARaM0sxle05WlLOfKb0KCoYCnhnjo8W5QH20ILG5BgkUt0nbO9N9q6cnFuz_-T9NBVtIoe4UKCis5Z-w06fP8RW6tjpulVMy51_DUOciaZQx_mCK82KH5vUhU6Szcywc4Xo4jKYJV68rbycI8N/s950/SLine94.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="705" data-original-width="950" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7QgX41VL93m9B6VumwH6UnslAfTafsxLuawDVpDDBn9ARaM0sxle05WlLOfKb0KCoYCnhnjo8W5QH20ILG5BgkUt0nbO9N9q6cnFuz_-T9NBVtIoe4UKCis5Z-w06fP8RW6tjpulVMy51_DUOciaZQx_mCK82KH5vUhU6Szcywc4Xo4jKYJV68rbycI8N/s320/SLine94.png" width="320" /></a></div>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiv69CFW2w6H-7qJrRvSWlGTqW8llR4tKMHKDZBmRb__ojct2dRvmcAIAI4wr4n31w7joJOzSfqFPPcdqaX40Uczxns0ETZjbjVqbox74RZkqWoUY_Qu29f95GH-WGtHMobprLOroTmpldudwYLLv-itoqVl0i4hAgBjY4nUxsk29r0BpDceGqSd2Hq1NoE/s941/SLine95.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="745" data-original-width="941" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiv69CFW2w6H-7qJrRvSWlGTqW8llR4tKMHKDZBmRb__ojct2dRvmcAIAI4wr4n31w7joJOzSfqFPPcdqaX40Uczxns0ETZjbjVqbox74RZkqWoUY_Qu29f95GH-WGtHMobprLOroTmpldudwYLLv-itoqVl0i4hAgBjY4nUxsk29r0BpDceGqSd2Hq1NoE/s320/SLine95.png" width="320" /></a></div>
<p>The EmployeeID will be entered into the FindID unbound TextBox and will press the Enter Key. This action should start searching for the EmployeeID in the Employees Form. The Code can run in the TextBox AfterUpdate() or LostFocus() Event Subroutine. However, the search for the record should be done on the RecordsetClone of the Form Object. Another option is to add a <a href="https://www.msaccesstips.com/2006/09/command-button-animation.html" target="_blank">Command Button</a> next to the Unbound TextBox to Click to start the search for the Record. In that case, we can write the Code in the Command Button Click Event Subroutine in Wrapper Class EmpCmdButton. We have a Label Control on the Footer of the Form running an animated display (On/Off) with the status of search success or failure. </p><h3 style="text-align: left;">Events that involve the Form Object.</h3><p>Every Wrapper Class that we create, like EmpTextBox, EmpCmdButton, EmpCombo, and other upcoming ones will have a Form Class as Property, with or without the <b>WithEvents</b> declaration. </p><p>If it is with the WithEvents declaration (<b>Private WithEvents frm as Form</b>) then we can directly capture the Form Events in that Wrapper Class. Otherwise, it can be used for accessing any control on the Form to read/write values or to access the Control properties, when the need arises.</p><p>Whether we are using the Form Property for any purpose or not we are assigning the physical Form's reference to all the Wrapper Class Instance created in the intermediate <b>WrapObject_Init</b> or <b>EmpObject_Init</b> Class's <b>Class_Init()</b> Subroutine.</p><p>So, here we prefer to write the <b>FindID</b> AfterUpdate() Event Subroutine Code in the EmpTextBox Wrapper Class Module.</p><p>First, let us see how we can incorporate the <b>FindID</b> unbound TextBox's <b>AfterUpdate()</b> <i>Event enabling</i> Code in the <b>EmpObject_Init</b> Wrapper Class within the existing Code. The full VBA Code is given below. The Combobox wrapper Class Property with the name <b>CBO</b> and its Event enabling Code is also added in the Class_Init Subroutine. All new Code lines are highlighted with Red Color.</p><p>The WrapObject_Init Class was copied from the earlier Project, renamed as <b>EmpObject_Init,</b> and added with new Code lines marked with red. Similarly other Classes. This way you can create Wrapper Classes quickly and reuse existing Code for other Forms and if necessary with modifications.</p><pre>Option Compare Database
Option Explicit
Private WithEvents iFrm As Access.Form
<span style="color: red;">Private WithEvents oFrm As Form
</span>
Private iTxt As EmpTextBox
<span style="color: red;">Private wcbo As EmpCombo</span>
Private wcmd As EmpCmdButton
Private Coll As New Collection
'------------------------------------------------------
'Streamlining Form Module Code
'in Stand-alone Class Modules
'------------------------------------------------------
'Main Form SubForm with Data
'Author: a.p.r. pillai
'Date : 06/08/2023
'Rights: All Rights(c) Reserved by www.msaccesstips.com
'------------------------------------------------------
'Form's Property GET/SET Procedures
Public Property Get m_Frm() As Form
Set m_Frm = iFrm
End Property
Public Property Set m_Frm(ByRef mfrm As Form)
Set iFrm = mfrm
Call Class_Init
End Property
'Events Enabling Subroutine
Private Sub Class_Init()
Dim ctl As Control
Const EP = "[Event Procedure]"
<span style="color: red;">iFrm.OnTimer = EP</span> 'Enable TimerInverval for Label Annimation
'Scan for TextBox, CommandButton & Combobox Controls on Employees Form
'and Enable the required Event Procedures
For Each ctl In iFrm.Controls 'Find TextBox, ComboBox & CommandButtons
Select Case TypeName(ctl)
Case "TextBox"
<span style="color: red;"> Select Case ctl.Name
Case "FindID" '</span>Employee ID Search TextBox<span style="color: red;">
Set iTxt = New EmpTextBox 'Create Instance
Set iTxt.tx_Frm = iFrm 'Assign Form Object
Set iTxt.t_Txt = ctl 'TextBox
iTxt.t_Txt.OnGotFocus = EP
iTxt.t_Txt.OnLostFocus = EP
iTxt.t_Txt.AfterUpdate = EP </span>'For EmployeeID Search<span style="color: red;">
Coll.Add iTxt </span>'Save EmpTextBox Class<span style="color: red;">
Set iTxt = Nothing </span>'Erase temp Instance<span style="color: red;">
</span>GoTo CmdButton<span style="color: red;">
End Select</span>
Set iTxt = New EmpTextBox 'Create Instance
Set iTxt.tx_Frm = iFrm
Set iTxt.t_Txt = ctl 'Pass TextBox Control on Form
iTxt.t_Txt.OnGotFocus = EP
iTxt.t_Txt.OnLostFocus = EP
<span style="color: red;">iTxt.t_Txt.OnDirty = EP 'To warn against Data Change
iTxt.t_Txt.BeforeUpdate = EP</span> 'Reconfirm Data Change to update
Coll.Add iTxt 'Save EmpTextBox Class in Collection Object
Set iTxt = Nothing 'Erase temp Instance
<span style="color: red;">CmdButton:</span>
Case "CommandButton"
Select Case ctl.Name
Case "cmdClose"
Set wcmd = New EmpCmdButton
Set wcmd.cmd_Frm = iFrm
Set wcmd.c_cmd = ctl
wcmd.c_cmd.OnClick = EP
Coll.Add wcmd
Set wcmd = Nothing
End Select
<span style="color: red;"> Case "ComboBox"
Set wcbo = New EmpCombo
Set wcbo.cbo_Frm = iFrm
Set wcbo.c_cbo = ctl
wcbo.c_cbo.OnGotFocus = EP
wcbo.c_cbo.OnLostFocus = EP
Coll.Add wcbo
Set wcbo = Nothing
End Select</span>
Next
'Order Sub-Form<span style="color: red;">
Set oFrm = iFrm.Orders.Form</span>
<span style="color: #2b00fe;">For Each ctl In oFrm.Controls</span><span style="color: red;"> </span>'Scan for SubForm Control and enable Events<span style="color: red;">
Select Case TypeName(ctl)
Case "TextBox"
Set iTxt = New EmpTextBox </span>'Create Instance<span style="color: red;">
Set iTxt.tx_Frm = oFrm
Set iTxt.t_Txt = ctl </span>'Pass TextBox Control on Form<span style="color: red;">
iTxt.t_Txt.OnGotFocus = EP </span>'To highlght TextBox<span style="color: red;">
iTxt.t_Txt.OnLostFocus = EP </span>'Reset Highlight<span style="color: red;">
iTxt.t_Txt.OnDirty = EP
iTxt.t_Txt.BeforeUpdate = EP
Coll.Add iTxt 'Save EmpTextBox Class
Set iTxt = Nothing 'Erase temp Instance
Case "ComboBox"
Set wcbo = New EmpCombo
Set wcbo.cbo_Frm = oFrm
Set wcbo.c_cbo = ctl
wcbo.c_cbo.OnGotFocus = EP </span>'To highlght ComboBox<span style="color: red;">
wcbo.c_cbo.OnLostFocus = EP </span>'Reset Highlight<span style="color: red;">
Coll.Add wcbo
Set wcbo = Nothing
End Select</span>
<span style="color: #2b00fe;">Next</span>
End Sub
Private Sub Class_Terminate()
'Delete Collection Object contents
Do While Coll.Count > 0
Coll.Remove 1
Loop
Set iFrm = Nothing
<span style="color: red;"> Set oFrm = Nothing</span>
End Sub
</pre><p>The EmpTextBox Wrapper Class EmployeeID Search VBA Code is given below:</p>
<pre>Option Compare Database
Option Explicit
Private <span style="color: red;">WithEvents</span> frm As Form
<span style="color: red;">Private subFrm As Form</span>
Private WithEvents Txt As TextBox 'TextBox object
<span style="color: red;">Dim L As Integer
Dim ForeColor As Long</span>
'------------------------------------------------------
'Streamlining Form Module Code
'in Stand-alone Class Modules
'------------------------------------------------------
'Main Form SubForm with Data
'Author: a.p.r. pillai
'Date : 06/08/2023
'Rights: All Rights(c) Reserved by www.msaccesstips.com
'------------------------------------------------------
'Form's Property GET/SET Procedures
Public Property Get tx_Frm() As Form
Set tx_Frm = frm
End Property
Public Property Set tx_Frm(ByRef pfrm As Form)
Set frm = pfrm
End Property
'TextBox Property GET/SET Procedures
Public Property Get t_Txt() As TextBox
Set t_Txt = Txt
End Property
Public Property Set t_Txt(ByRef tTxt As TextBox)
Set Txt = tTxt
End Property
'Evbent Subroutines
'===================
Private Sub txt_GotFocus()
GFColor frm, Txt 'Field Highlight
<span style="color: red;"> If Txt.Name = "FindID" Then
Txt.Value = Null
End If</span>
End Sub
Private Sub txt_LostFocus()
LFColor frm, Txt 'Field Highlight
End Sub
<span style="color: #2b00fe;">Private Sub txt_Dirty(Cancel As Integer)</span><span style="color: red;">
If MsgBox("Editing the " & UCase(Txt.Name) _
& ": Value? " & Txt.Value, vbYesNo + vbQuestion, _
Txt.Name & " DIRTY()") = vbNo Then
Cancel = True
End If
</span><span style="color: #2b00fe;">End Sub</span>
<span style="color: red;">'</span><span>Data Update Reconfirm to Save the Change</span><span style="color: red;">
</span><span style="color: #2b00fe;">Private Sub txt_BeforeUpdate(Cancel As Integer)</span><span style="color: red;">
Dim msg As String
msg = "Field Name: " & Txt.Name & vbCr & _
"Original Value '" & UCase(Txt.OldValue) & "'" & _
vbCr & "Change to: '" & UCase(Txt.Value) & "'"
If MsgBox(msg, vbYesNo + vbQuestion, _
Txt.Name & "_BeforeUpdate()") = vbNo Then
Cancel = True
End If
</span><span style="color: #2b00fe;">End Sub</span><span style="color: red;">
</span><span style="color: #2b00fe;">Private Sub txt_AfterUpdate()</span><span style="color: red;">
Select Case Txt.Name
Case "FindID"
Dim rst As Recordset
Dim ToFind As Integer
Dim msg As String
Dim max As Integer
'max = DCount("*", "Employees")
ToFind = Nz(frm!FindID, 0)
If ToFind < 1 Then
msg = "Employee ID: < 1 Invalid!"
MsgBox msg, vbOK + vbCritical, Txt.Name & "_AfterUpdate()"
Else
Set rst = frm.RecordsetClone
rst.FindFirst "EmployeeID=" & ToFind
If Not rst.NoMatch Then
frm.Bookmark = rst.Bookmark
With frm.Result
.Caption = "</span><span style="color: #2b00fe;">*** Successful ***</span><span style="color: red;">"
ForeColor = 16711680
.ForeColor = ForeColor
End With
Else
With frm.Result
.Caption = "</span><span>**Sorry, Not found!</span><span style="color: red;">"
ForeColor = 255
End With
End If
L = 0
frm.TimerInterval = 250 '</span><span>Enable Timer</span><span style="color: red;">
End If
End Select
</span><span style="color: #2b00fe;">End Sub</span><span style="color: red;">
<br /></span></pre><pre>'Label Animation Code.<span style="color: red;">
</span><span style="color: #2b00fe;">Private Sub frm_Timer()</span><span style="color: red;">
L = L + 1
Select Case L
Case 1, 3, 5, 7, 9, 11, 13, 15, 17
frm.Result.Visible = True
Case 2, 4, 6, 8, 10, 12, 14, 16, 18
frm.Result.Visible = False
Case 19
frm.Result.ForeColor = ForeColor
frm.Result.Visible = False
frm.TimerInterval = 0
End Select
</span><span style="color: #2b00fe;">End Sub</span>
</pre>
<p>The <b>EmpCombo</b> is a new Wrapper Class of ComboBox and the GotFocus(), LostFocus() Event Subroutine VBA Code is given below:</p>
<pre>Option Compare Database
Option Explicit
Private cbofrm As Access.Form
Private WithEvents cbo As Access.ComboBox 'ComboBox object
'------------------------------------------------------
'Streamlining Form Module Code
'in Stand-alone Class Modules
'------------------------------------------------------
'ComboBox Wrapper Class
'Author: a.p.r. pillai
'Date : 06/08/2023
'Rights: All Rights(c) Reserved by www.msaccesstips.com
'------------------------------------------------------
'Form's Property GET/SET Procedures
Public Property Get cbo_Frm() As Form
Set cbo_Frm = cbofrm
End Property
Public Property Set cbo_Frm(ByRef cfrm As Form)
Set cbofrm = cfrm
End Property
'TextBox Property GET/SET Procedures
Public Property Get c_cbo() As ComboBox
Set c_cbo = cbo
End Property
Public Property Set c_cbo(ByRef pcbo As ComboBox)
Set cbo = pcbo
End Property
'Event Subroutines Code
Private Sub cbo_GotFocus()
GFColor cbofrm, cbo 'ComboBox highlight
End Sub
Private Sub cbo_LostFocus()
LFColor cbofrm, cbo 'ComboBox highlight
End Sub
</pre>
<h4>Your Assessment of the Streamlined Coding Procedure in Standalone Class Module.</h4>
<p>If you are an experienced Access VBA Programmer, then you may be able to make an assessment of the following Points:</p><p></p><ol style="text-align: left;"><li>The amount of work and time required to write the Code for the OnDirty() Event Subroutines Code for each TextBox and ComboBox Controls on the Employees & Orders Subform in their Form Modules through the existing manual VBA Code writing procedure.</li><li><p>Similarly, the amount of Work and Time required for the AfterUpdate() Event Subroutines on Both Forms.</p></li><li><p>Normally we don't write these codes in the Form Module for all the TextBoxes, only for essential cases. How do you feel about this new method and the ease of its implementation time saving compared to existing Coding practices? Not necessary to quantify, the emphasis is on the reusability of part of the written Code in the Standalone Class Module and saving of development time for future Projects.</p></li><li><p>Even though the new concept, of Streamlining the Form Module Coding in Standalone Class Modules, it is difficult for beginners to make an assessment, what do you think about it as an Expert VBA Programmer?</p></li><li>All the Episodes are written in the style of Tutoring the Title-related examples so that non-experts can also attempt to try and learn. How easy/difficult to understand the concept.</li></ol><p></p><p>Your feedback may be left in the Comment Section. If you have a Gmail ID you can log in and Comment. Thanks.</p><p>The forthcoming Demonstrations on the Title Topic: Implementation of other Access Class Objects like Tab-Control, ListBox, Option Group, and the Report Module Code streamlining examples will follow.</p><p>The Demo Database is attached for Download.</p>
<!--Download Link /downloads/2023/08/Streamline9.zip-->
<div class="separator" style="clear: both; text-align: center;">
<a href="https://drive.google.com/file/d/1t4eyB-fBIEJYTNapvdD1MRqvDzltDNKd/view?usp=sharing">
<img border="0" data-original-height="29" data-original-width="22" height="29" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFE4uDLH9dk3S6pEcfTkT6GW6r11XqBI4HQnLnhQFZMKFeeQt5ABV_NqvLHM68J7RacISsvbUEl0PlAq6V7i0t0mQF3iesrBuqGuvrf5CJV72OzrGZ7rdMxkxzw8FYlz-qniNySoqtqUN4/s320/zip.gif" width="22" />Streamline9.zip</a>
</div>
<!--Download Link /downloads/2023/08/Streamline9.zip-->
<h3>Streamlining Form Module Code in Standalone Class Module.</h3>
<ol>
<li><a href="https://www.msaccesstips.com/2023/04/reusing-form-module-vba-code-for-new.html" target="_Blank">Re-using Form Module VBA Code for New Projects.</a></li>
<li><a href="https://www.msaccesstips.com/2023/04/streamlining-form-module-code-part-two.html" target="_Blank">Streamlining Form Module Code Part-Two</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/05/streamlining-form-module-code-part-three.html" target="_Blank">Streamlining Form Module Code Part-Three</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/06/streamlining-form-module-code-part-four.html" target="_Blank">Streamlining Form Module Code Part-Four</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/06/streamlining-form-module-code-part-five.html" target="_Blank">Streamlining Form Module Code Part-Five</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-six.html" target="_Blank">Streamlining Form Module Code Part-Six</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-seven.html" target="_Blank">Streamlining Form Module Code Part-Seven</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-eight.html" target="_Blank">Streamlining Form Module Code Part-Eight</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part-nine.html" target="_Blank">Streamlining Form Module Code Part-Nine</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part-ten.html" target="_Blank">Streamlining Form Module Code Part-Ten</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part.html" target="_Blank">Streamlining Form Module Code Part-Eleven</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-report-module-code-in.html" target="_Blank">Streamlining Report Module Code in Class Module</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-report-module-in-class.html" target="_Blank">Streamlining Report Module Code in Class Module-2</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-form-module-code-part-14.html" target="_Blank">Streamlining Form Module Code Part-14:All Controls</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-custom-made-form-wizard.html" target="_Blank">Streamlining Custom Made Form Wizard-15</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-form-vba-custom-report.html" target="_Blank">Streamlining Custom Report Wizard-16</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-form-vba-external-files.html" target="_Blank">Streamlining Form VBA External File Browser-17</a></li>
<li><a href="https://www.msaccesstips.com/2023/11/streamlining-event-procedures.html" target="_Blank">Streamlining Event Procedures of 3D TextWizard-18</a></li>
<li><a href="https://www.msaccesstips.com/2023/11/streamlining-event-procedures-rgbcolor.html" target="_Blank">Streamlining Event Procedures RGB Color Wizard-19</a></li>
<li><a href="https://www.msaccesstips.com/2023/12/streamlining-numbers-to-words-converter.html" target="_Blank">Streamlining Event Procedures Number To Words-20</a></li>
</ol>
a.p.r. pillaihttp://www.blogger.com/profile/02627590161919120214noreply@blogger.com2tag:blogger.com,1999:blog-7457417942281874810.post-75952224670426432172023-07-28T14:54:00.027+05:302024-01-07T22:52:17.131+05:30Streamlining Form Module Code - Part Eight<h3 style="text-align: left;"> Introduction.</h3><h4 style="text-align: left;">Continued from "Streamlining Form Module Code - Part Six & Seven"</h4>
<p>In the past, we utilized the TextBox Wrapper Class Array to store TextBox instances with Events enabled, allowing us to capture them in memory and execute the associated Event Subroutines. Although this Array method served its purpose, it required re-dimensioning the <a href="https://www.msaccesstips.com/2019/05/access-form-control-arrays-and-event-3.html" target="_blank">Array</a> space every time and maintaining separate indices for various types of objects, such as Command Buttons, ComboBoxes, ListBoxes, and others. Fortunately, there is a more efficient option at our disposal - the Collection Object.</p><p>Earlier Article Links:</p>
<!-- Google In-Article Ads start -->
<script async="" src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle" data-ad-client="ca-pub-2376623373770548" data-ad-format="fluid" data-ad-layout="in-article" data-ad-slot="6147807460" style="display: block; text-align: center;"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
<!-- Google In-Article Ads END -->
<ol>
<li><a href="https://www.msaccesstips.com/2023/04/reusing-form-module-vba-code-for-new.html" target="_Blank">Re-using Form Module VBA Coding for New Projects.</a></li>
<li><a href="https://www.msaccesstips.com/2023/04/streamlining-form-module-code-part-two.html" target="_Blank">Streamlining Form Module Coding Part-Two</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/05/streamlining-form-module-code-part-three.html" target="_Blank">Streamlining Form Module Coding Part-Three</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/06/streamlining-form-module-code-part-four.html" target="_Blank">Streamlining Form Module Coding Part-Four</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/06/streamlining-form-module-code-part-five.html" target="_Blank">Streamlining Form Module Coding Part-Five</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-six.html" target="_Blank">Streamlining Form Module Coding Part-Six</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-seven.html" target="_Blank">Streamlining Form Module Coding Part-Seven</a>.</li>
</ol>
<p>In this session of our Tutorial on the Title topic, we will learn the following tricks:</p><ol style="text-align: left;"><li>Usage of Data Table.</li><li>Collection Object replaces the Object Array.</li><li>Command Button Wrapper Class.</li><li>Form Wrapper Class. Sections: Header, Detail, and Footer Event Procedure. </li><li>Using Timer-Interval Property for Digital Clock on the Form.</li><li>Form Closing Count Down without using TimerInterval Property.</li></ol><p>Trial Run Form-Image, taken from the Countdown and Form closing phase.</p>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjVTPi2GLQ_yFDe5jSBbEPDQpYO1gS1_u7M11GNICi1LjaWdPRz_OlfYpMVmGVbbKTG0sy99n-_kgS4bpYaPVdgstPhw3KCSMGLxtrOfLQgzM8Ej99JcbKsxBKyhAPPu3GuUgRAlfP_EoaD0HthHg5Kc8cd6C1MNu3nDpU3hDbP8YqOnBJKdPdUmY2GILN-/s725/Streamline81.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="663" data-original-width="725" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjVTPi2GLQ_yFDe5jSBbEPDQpYO1gS1_u7M11GNICi1LjaWdPRz_OlfYpMVmGVbbKTG0sy99n-_kgS4bpYaPVdgstPhw3KCSMGLxtrOfLQgzM8Ej99JcbKsxBKyhAPPu3GuUgRAlfP_EoaD0HthHg5Kc8cd6C1MNu3nDpU3hDbP8YqOnBJKdPdUmY2GILN-/s320/Streamline81.png" width="320" /></a></div>
<h3 style="text-align: left;">Using Data Table.</h3><p>We run all kinds of validation checks on the data entered into the TextBox before accepting it. The entered data also can be part of any calculations and the result may be saved in another TextBox on the Form. These actions we could do on the Form from the Class Module, whether the TextBox's Control Source is assigned to a <a href="https://www.msaccesstips.com/2019/05/withevents-in-class-module-and-data.html" target="_blank">Table field</a> or not. The only difference is that the data is not stored anywhere, but we could run the required data validation checks away from the Form Module and in the stand-alone Class Module.</p><h4 style="text-align: left;">The Collection Object replaces the Object Array.</h4><p>With the new Coding procedure, we could reduce the number of Event Subroutines of a particular type into a single one.</p><p>For example, check the following sample <b>Exit()</b> Event Procedure of three TextBoxes written under a single Event Subroutine:</p>
<pre>Private Sub <b>Txt_Exit</b>(Cancel As Integer)
Dim i As Integer, Msg As String
Dim info As Integer
Select Case Txt.Name
<span style="color: red;">Case "Description"</span> 'TextBox - Description
If Len(Nz(Txt.Value, "")) = 0 Then
MsgBox "Item Description Empty"
Cancel = True
End If
<span style="color: red;">Case "Quantity"</span> 'TextBox - Quantity
i = Nz(Txt.Value, 0)
If i < 1 Or i > 10 Then
Msg = "Valid Value Range 1 - 10 Only."
info = vbCritical
MsgBox Msg, vbOK + info, "Quatity_Exit()"
GFColour frm ', Txt
Cancel = True
Else
Msg = "Quantity: " & i & " Valid."
info = vbInformation
MsgBox Msg, vbOK + info, "Quatity_Exit()"
End If
<span style="color: red;">Case "UnitPrice"</span> 'TextBox - UnitPrice
i = Nz(Txt.Value, 0)
Msg = ""
If i <= 0 Then
Msg = "Enter a Value greater than Zero!"
info = vbCritical
GFColor frm
Cancel = True
MsgBox Msg, vbOK + info, "UnitPrice_Exit()"
Else
info = vbInformation
MsgBox "Unit Price: " & i, vbOK + vbInformation, "UPrice_Exit()"
End If
End Select
End Sub
</pre><p>If there are more TextBoxes with the Exit() Event Subroutines, they can be written within this structured Subroutine. If we write them in the Form Module they will be within three different Subroutines in three different locations among other object's Subroutines in the Form Module.</p><p>The TextBox names (highlighted with Red Color along with other TextBoxes) may be enabled (RaiseEvent) with other required inbuilt Events, like GotFocus, LostFocus, and others and they also will be grouped as shown above, all GotFocus cases within a single GotFocus() Event Subroutine, and all LostFocus cases within one LostFocus() Event Subroutine. </p><p>In short, you need to write only one BeforeUpdate() Event Subroutine in the Standalone Class Module for 25 TextBoxes on the Form, if all of them are enabled with this Event. All TextBox's BeforeUpdate() Event Procedure can be inserted into this single Subroutine. This rule applies to all other Event Subroutines of TextBoxes. Everything will be in one Standalone TextBox Wrapper Class Module for Code maintenance and debugging. </p>
<!-- Google In-Article Ads start -->
<script async="" src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle" data-ad-client="ca-pub-2376623373770548" data-ad-format="fluid" data-ad-layout="in-article" data-ad-slot="6147807460" style="display: block; text-align: center;"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
<!-- Google In-Article Ads END -->
<p>All Object Types will have their own Standalone Wrapper Classes and their Event Subroutines are organized this way.</p><p>All these Event Procedures are executed one at a time as and when it is called for. They can be in the <a href="https://www.msaccesstips.com/2019/04/withevents-and-defining-your-own-events.html" target="_blank">Form Module</a> or they can be in the Wrapper Class Module in memory. </p><p>The <a href="https://www.msaccesstips.com/2023/04/reusing-form-module-vba-code-for-new.html" target="_blank">TextBoxes</a> (and other objects) on the Form are defined with the Keyword <b>WithEvents </b>individually when they are inserted in the Form<b>. </b>They are all spread out on the Form and we need to write Event Subroutines separately for each TextBox on the Form Module.</p><p>But, with the use of our <b>Wrapper TextBox Class</b>, we create only one Instance of the TextBox Class predefined with the keyword <b><a href="https://www.msaccesstips.com/2019/04/withevents-button-combo-list-textbox-tab.html" target="_blank">WithEvents</a></b>. The TextBox Class declaration in the Wrapper Class is with an object name <b>Txt </b>like <b>Private WithEvents Txt As TextBox. </b> So all the TextBox-related Event Subroutine Name will be prefixed with the TextBox Object Name <b>Txt</b> like <b>Private Sub Txt_GotFocus().</b></p><p>We will be creating separate instances of this Wrapper Class Module for each TextBox on the Form, and assigned with the References of the TextBoxes on the Form, through an automated procedure. This saves a lot of time we normally spend on writing and maintaining the Code for individual TextBoxes on the Form Module, during the Database development phase.</p><p><b></b>All GotFocus Events will be captured in this Subroutine and we need to check for their name in <i><b>Select Case Txt.Name</b> </i>followed by <b>Case "Quantity"</b> to determine which TextBox on the Form fired the Event, accordingly we write the required Code under the TextBox Name. This kind of individual name checking is unnecessary if all the TextBoxes require the same VBA Code for a particular Event, like the TextBox highlight example we tried in the earlier Episode. Or selectively write Code for required TextBoxes in this way and Code that applies globally for all TextBoxes can be written outside this name-checking structure.</p><p>The Wrapper Class will have an additional declaration of a Form object, with the name like <b>frm, </b>mostly without the WithEvents qualification. It is necessary to read or write other control values on the Form, from the Wrapper Class in memory.</p><p>To keep the Wrapper Class Instances in memory we first chose the Wrapper Class Array option in the <b>Class_Init()</b> Subroutine in the <b>WrapObject_Init2</b> intermediate Class Module. We need to create Wrapper Classes for other Access Controls like <a href="https://www.msaccesstips.com/2006/09/command-button-animation.html " target="_blank">Command Buttons</a>, Combo Boxes, and others. In that case, we need separate Indices based on the number of such controls on the Form. An alternative option is to use the Collection Object. The Collection object can hold any type of object or other type of Values, with or without the Item Key. Those who are not familiar with the Collection Object go through the following links:</p><p><b>COLLECTION OBJECT</b></p>
<ol>
<li><a href="https://www.msaccesstips.com/2018/12/ms-access-and-collection-object-basics.html" target="_Blank">MS-Access and Collection Object Basics</a>
</li><li><a href="https://www.msaccesstips.com/2019/01/ms-access-class-module-and-collection.html" target="_Blank">MS-Access Class Module and Collection Objects</a>
</li><li><a href="https://www.msaccesstips.com/2019/01/table-records-in-collection-object-and.html" target="_Blank">Table Records in Collection Object</a>
</li></ol>
<p>The Class_Init() Subroutine Code in the <b>WrapTextBox_Init</b> Class is presented in the Title Topic Part-Six and the new version <b>WrapTextBox_Init2</b> is implemented with the Collection Object. </p><h4 style="text-align: left;">WrapTextBox_Init Version VBA Code.</h4>
<pre>'Scan for TextBox Controls on Form
<span style="color: red;">j = 0</span>
'and Enable the required Event Procedures
For Each ctl In iFrm.Controls
Select Case TypeName(ctl)
Case "TextBox"
Select Case ctl.Name
Case "Quantity"
Set iTxt = New WrapTextBox 'Create Instance
Set iTxt.tx_Frm = iFrm 'Pass Form object
Set iTxt.t_Txt = ctl 'Pass TextBox Control on Form
iTxt.t_Txt.OnExit = EP 'Enable Event
iTxt.t_Txt.OnGotFocus = EP ' "
iTxt.t_Txt.OnLostFocus = EP ' "
<span style="color: red;"> j = j + 1 'increment counter
ReDim Preserve TxtArray(1 To j) 'Redim Array
Set TxtArray(j) = iTxt 'Save WrapTextBox Class</span>
Set iTxt = Nothing 'Erase temp Instance
Case "UnitPrice"
Set iTxt = New WrapTextBox 'Create Instance
Set iTxt.tx_Frm = iFrm
Set iTxt.t_Txt = ctl
iTxt.t_Txt.OnExit = EP
iTxt.t_Txt.OnGotFocus = EP
iTxt.t_Txt.OnLostFocus = EP
<span style="color: red;"> j = j + 1
ReDim Preserve TxtArray(1 To j)
Set TxtArray(j) = iTxt 'Save it in Object Array</span>
Set iTxt = Nothing 'Erase temp Instance
Case "TotalPrice"
Set iTxt = New WrapTextBox 'Create Instance
Set iTxt.tx_Frm = iFrm
Set iTxt.t_Txt = ctl
iTxt.t_Txt.OnGotFocus = EP
iTxt.t_Txt.OnLostFocus = EP
<span style="color: red;"> j = j + 1
ReDim Preserve TxtArray(1 To j)
Set TxtArray(j) = iTxt 'Save it in Object Array
</span> Set iTxt = Nothing 'Erase temp Instance
End Select
End Select
Next
</pre>
<h4 style="text-align: left;">WrapTextBox_Init2 Version VBA Code.</h4>
<pre>'Scan for TextBox Controls on Form
'and Enable the required Event Procedures
For Each ctl In iFrm.Controls
Select Case TypeName(ctl)
Case "TextBox"
Select Case ctl.Name
Case "Description"
Set iTxt = New WrapTextBox2 'Create Instance
Set iTxt.tx_Frm = iFrm
Set iTxt.t_Txt = ctl 'Pass TextBox Control on Form
iTxt.t_Txt.OnGotFocus = EP ' "
iTxt.t_Txt.OnLostFocus = EP ' "
<span style="color: red;"> Coll.Add iTxt 'Save WrapTextBox2 Class
</span>
Set iTxt = Nothing 'Erase temp Instance
Case "Quantity", "UnitPrice"
Set iTxt = New WrapTextBox2 'Create Instance
Set iTxt.tx_Frm = iFrm
Set iTxt.t_Txt = ctl 'Pass TextBox Control on Form
iTxt.t_Txt.OnExit = EP 'Enable Event
iTxt.t_Txt.OnGotFocus = EP ' "
iTxt.t_Txt.OnLostFocus = EP ' "
<span style="color: red;"> Coll.Add iTxt 'Save WrapTextBox2 Class
</span>
Set iTxt = Nothing 'Erase temp Instance
Case "Discount"
Set iTxt = New WrapTextBox2 'Create Instance
Set iTxt.tx_Frm = iFrm
Set iTxt.t_Txt = ctl 'Pass TextBox Control on Form
iTxt.t_Txt.OnGotFocus = EP ' "
iTxt.t_Txt.OnLostFocus = EP ' "
<span style="color: red;"> Coll.Add iTxt 'Save WrapTextBox2 Class
</span>
Set iTxt = Nothing 'Erase temp Instance
End Select
End Select
Next
</pre>
<p>Compare both versions of the same VBA Code, those lines marked with red, in both versions, are used for the TextBox Object Instances.</p><p>The <b>Z-Concept</b> - Streamlining of Form Module Coding Logic of Class Objects at a Glance in the updated Diagram.</p>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgZXjlnRQlU_pCrtWZBYdLXEQN5Z9ut9LocqE55So3UwmEN1ppDP-eggwk_3W4Unp5I81SjKM06r6MJCTRz50bDwGJjap0ATlUu9z1EHoS7aFsKOw9v5YooldN0GysYLoQmbdvpeIh2s3xK1GrrZKB_WpYHHyIhf-jSj2U_WDxw2IhY3YvK2gj-vtj-O369/s849/ZConceptDiagram3.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="849" data-original-width="849" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgZXjlnRQlU_pCrtWZBYdLXEQN5Z9ut9LocqE55So3UwmEN1ppDP-eggwk_3W4Unp5I81SjKM06r6MJCTRz50bDwGJjap0ATlUu9z1EHoS7aFsKOw9v5YooldN0GysYLoQmbdvpeIh2s3xK1GrrZKB_WpYHHyIhf-jSj2U_WDxw2IhY3YvK2gj-vtj-O369/s320/ZConceptDiagram3.png" width="320" /></a></div>
<h3 style="text-align: left;">Form Sections Header, Detail, Footer Event handling.</h3><p>There are several Form object Events. We will check how the Click and <a href="https://www.msaccesstips.com/2019/10/call-function-from-mousemove-event.html" target="_blank">MouseMove</a> Events are invoked from the Form Sections: Detail, Form Header, and Form Footer areas. I don't ask you to design a Form like the one shown at the top of this page. You can download the Demo database from the link given at the end of this page and try it out. Look for the changes we made in the WrapObject_Init2, WrapTextBox, in the Demo database. Compare the VBA Code with the Code you already have in the Demo Database downloaded from Episode Six.</p><p>Episode Number Eight introduces two new Wrapper Classes, WrapForm, and WrapCmdButton Classes. Form Event Subroutines and Events specific to Form Sections are run from the WrapForm Class Module. </p><p>WrapForm Class Module VBA Code is given below:</p>
<pre>Option Compare Database
Option Explicit
Private WithEvents Sfrm As Access.Form
Private WithEvents ScD As Access.Section
'------------------------------------------------------
'Streamlining Form Module Code
'in Stand-alone Class Modules
'------------------------------------------------------
'Access.Form Wrapper Class Module
'Author: a.p.r. pillai
'Date : 27/07/2023
'Rights: All Rights(c) Reserved by www.msaccesstips.com
'------------------------------------------------------
Public Property Get s_frm() As Access.Form
Set s_frm = Sfrm
End Property
Public Property Set s_frm(ByRef FFrm As Access.Form)
Set Sfrm = FFrm
End Property
Public Property Get s_SecD() As Section
Set s_SecD = ScD
End Property
Public Property Set s_SecD(ByVal iSec As Section)
Set ScD = iSec
End Property
Private Sub scD_Click()
Select Case ScD.Name
Case "FormHeader"
MsgBox "FormHeader Click", , "FormHeader Section"
End Select
End Sub
Private Sub scD_DblClick(Cancel As Integer)
Select Case ScD.Name
Case "FormFooter"
MsgBox "FormFooter DblClick", , "FormFooter Section"
End Select
End Sub
Private Sub scD_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
Select Case ScD.Name
Case "Detail"
Sfrm.Label39.Caption = X & " , " & Y
End Select
End Sub
</pre>
<p>In the Global declaration area of the Class Module two Class objects, Form and Form Section objects are declared. Followed by their Object assignment Public Property procedures. </p><p>The Form Sections Click, MouseMove, and Double Click Events are enabled for the WrapForm Class Module Demo. The Mouse Move is enabled in the Detail Section and displays the X, and Y coordinate values in a label in the Detail Section, the Form Header is enabled with the Click Event, and the Footer Section is enabled with the Double-Click Event to run. Both Header and Footer Events display a message. </p><p><b>Note</b>: The indicator label 'Click here' in the Form Footer area may read as 'Double Click'.</p><p>The Digital Clock is run from the <b>WrapObject_Init2 </b>Class Module. The<b> iFrm_Timer()</b> Subroutine is also placed in this Module to update the label control Caption Property with the Time in "hh:nn:ss" format. </p><p>The Clock runs in one-second intervals it is not advisable to put this Code in the WrapForm Class Module. The Form Section-wise Events create three instances of the WrapForm Class in memory. If the Clock running Code is put in this Class Module the Code will run from all three instances of WrapForm Class in memory.</p><p>The <b>WrapCmdButton</b> Class Module VBA Code.</p><pre>Option Compare Database
Option Explicit
Private WithEvents cfrm As Form
Private WithEvents cmd As CommandButton 'CommandButton object
'------------------------------------------------------
'Streamlining Form Module Code
'in Stand-alone Class Modules
'------------------------------------------------------
'Access.CommandButton Wrapper Class Module
'Author: a.p.r. pillai
'Date : 27/07/2023
'Rights: All Rights(c) Reserved by www.msaccesstips.com
'------------------------------------------------------
'Form's Property GET/SET Procedures
Public Property Get c_Frm() As Form
Set c_Frm = cfrm
End Property
Public Property Set c_Frm(ByRef cmdfrm As Form)
Set cfrm = cmdfrm
End Property
'TextBox Property GET/SET Procedures
Public Property Get c_cmd() As CommandButton
Set c_cmd = cmd
End Property
Public Property Set c_cmd(ByRef pcmd As CommandButton)
Set cmd = pcmd
End Property
Private Sub cmd_Click()
Select Case cmd.Name
Case "cmdClose"
If MsgBox("Close this Form?", vbOKCancel + vbQuestion, "cmd_Click") = vbOK Then
DoCmd.Close acForm, cfrm.Name
End If
End Select
End Sub
'Form Closing CountDown...
'Do Not write Form_Unload() on Form Module
Private Sub cfrm_Unload(Cancel As Integer)
Dim T As Double, t2 As Double
Dim strMsg As String
'cFrm.TimerInterval = 0 - Disable Clock, if necessary
strMsg = "Form will Close in "
cfrm.Label29.Visible = True
T = Timer
Do While Timer < T + 10
t2 = Timer
Do While Timer < t2 + 0.25
DoEvents
Loop
cfrm.Label29.Caption = strMsg & " " & Int((T + 10) - Timer) & " Seconds."
DoEvents
Loop
End Sub
</pre>
<p>The Command Button Wrapper Class is a simple Class that mostly handles the Click Event only. The <a href="https://www.msaccesstips.com/2011/06/creating-animated-command-button-with.html" target="_blank">Command Button</a> and Form object declarations are there in the Global declaration area. The Get and Set Property Procedure pairs of both objects appear next. </p><p>Next, the <i>Private Sub cmd_Click() </i>Event Subroutine for the <b>cmdClose</b> Button Clicks Event is inserted. If any other Command Buttons are inserted on the Form their Click Event also can be put in this Event Subroutine by introducing the <b>Select Case . . . End Select</b> structure to identify the Command Button name to handle their Events separately. </p><p>Before closing the Form the <b>Sub cfm_Unload()</b> Event fires. The rest of the Code is a Form Closing countdown simulation that runs for 10 seconds before actually closing the Form. There is a Label Control kept hidden above the TextBoxes on the Form made visible now. The Countdown simulation text is displayed in the Label Control and when the count is equal to zero, closes the Form.</p><p><b>Note: </b>If the Form_Unload() Event Subroutine is present in the Form Module then the WrapCmdButton Class-based <b>cfm_Unload() </b>Event Subroutine will not be able to execute. Because the Form Module-based Form_Unload() Event gets priority and closes the Form.</p><h4>Demo Database Download Link:</h4>
<!--Download Link /downloads/2023/07/Streamline8.zip-->
<div class="separator" style="clear: both; text-align: center;">
<a href="https://drive.google.com/file/d/13gNg0CHY7n3KSIz5jU6vn1BXtkK0BZIL/view?usp=sharing">
<img border="0" data-original-height="29" data-original-width="22" height="29" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFE4uDLH9dk3S6pEcfTkT6GW6r11XqBI4HQnLnhQFZMKFeeQt5ABV_NqvLHM68J7RacISsvbUEl0PlAq6V7i0t0mQF3iesrBuqGuvrf5CJV72OzrGZ7rdMxkxzw8FYlz-qniNySoqtqUN4/s320/zip.gif" width="22" />Streamline8.zip</a>
</div>
<!--Download Link /downloads/2023/07/Streamline8.zip-->
<h3>Streamlining Form Module Code in Standalone Class Module.</h3>
<ol>
<li><a href="https://www.msaccesstips.com/2023/04/reusing-form-module-vba-code-for-new.html" target="_Blank">Re-using Form Module VBA Code for New Projects.</a></li>
<li><a href="https://www.msaccesstips.com/2023/04/streamlining-form-module-code-part-two.html" target="_Blank">Streamlining Form Module Code Part-Two</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/05/streamlining-form-module-code-part-three.html" target="_Blank">Streamlining Form Module Code Part-Three</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/06/streamlining-form-module-code-part-four.html" target="_Blank">Streamlining Form Module Code Part-Four</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/06/streamlining-form-module-code-part-five.html" target="_Blank">Streamlining Form Module Code Part-Five</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-six.html" target="_Blank">Streamlining Form Module Code Part-Six</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-seven.html" target="_Blank">Streamlining Form Module Code Part-Seven</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-eight.html" target="_Blank">Streamlining Form Module Code Part-Eight</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part-nine.html" target="_Blank">Streamlining Form Module Code Part-Nine</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part-ten.html" target="_Blank">Streamlining Form Module Code Part-Ten</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part.html" target="_Blank">Streamlining Form Module Code Part-Eleven</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-report-module-code-in.html" target="_Blank">Streamlining Report Module Code in Class Module</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-report-module-in-class.html" target="_Blank">Streamlining Report Module Code in Class Module-2</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-form-module-code-part-14.html" target="_Blank">Streamlining Form Module Code Part-14:All Controls</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-custom-made-form-wizard.html" target="_Blank">Streamlining Custom Made Form Wizard-15</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-form-vba-custom-report.html" target="_Blank">Streamlining Custom Report Wizard-16</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-form-vba-external-files.html" target="_Blank">Streamlining Form VBA External File Browser-17</a></li>
<li><a href="https://www.msaccesstips.com/2023/11/streamlining-event-procedures.html" target="_Blank">Streamlining Event Procedures of 3D TextWizard-18</a></li>
<li><a href="https://www.msaccesstips.com/2023/11/streamlining-event-procedures-rgbcolor.html" target="_Blank">Streamlining Event Procedures RGB Color Wizard-19</a></li>
<li><a href="https://www.msaccesstips.com/2023/12/streamlining-numbers-to-words-converter.html" target="_Blank">Streamlining Event Procedures Number To Words-20</a></li>
</ol>
a.p.r. pillaihttp://www.blogger.com/profile/02627590161919120214noreply@blogger.com0tag:blogger.com,1999:blog-7457417942281874810.post-51635785202508073872023-07-17T19:08:00.059+05:302024-01-07T22:51:06.261+05:30Streamlining Form Module Code - Part Seven<h3>Introduction</h3>
<h4 style="text-align: left;">Streamlining Form Module Code in Standalone Class Module.</h4>
<p>Maybe next week we will continue from the earlier episode <a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-six.html" target="_blank">Part Six</a> refine those new coding methods introduced there and bring in some further improvements in its implementation. </p><p>After going through the earlier Sessions of these exercises at least some of the readers may be in doubt about the power of these new Coding methods. This page is dedicated to them to experience the normal way of Coding on the Form Module and compare it with streamlined Coding that uses the standalone Class Module. We will split this new Coding Demo into three Parts, from the normal one to the advanced automation level.</p><p></p><ol style="text-align: left;"><li><p>The Normal Coding method.</p></li><li>The Stand-alone Class Module supported method.</li><li><p>Two Stand-alone Class Modules supported Option.</p></li></ol><p></p>
<!-- Google In-Article Ads start -->
<script async="" src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle" data-ad-client="ca-pub-2376623373770548" data-ad-format="fluid" data-ad-layout="in-article" data-ad-slot="6147807460" style="display: block; text-align: center;"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
<!-- Google In-Article Ads END -->
<h3 style="text-align: left;">Part-I. </h3><h4>The review of existing Coding practices to assess the differences.</h4><p>When we design a Form for Data Entry/View we would like to see the selected field highlighted with some eye-catching color or border, (or both) to spot the active field quickly or to mark and resume work from there onwards. We need to run two Event Subroutines for a single textbox, the GotFocus Event Subroutine to highlight the field and the LostFocus one to reset the color to its earlier setting. If we have 10 <a href="https://www.msaccesstips.com/2008/10/textbox-and-label-inner-margins.html" target="_blank">TextBoxes</a> on the Form we will write 10 GotFocus, LostFocus pairs of (total of 20) Event Subroutines on the Form Module. In this case, the quickest way is to copy-paste one pair of Event Subroutines and modify the TextBox name prefixes in the Subroutine Name.</p><p>The Event Subroutines can call predefined Functions or write Code for Back Color, Border Color, and Border Width Property settings within all the Subroutines. </p><p>In either case, the time spent implementing this method for all text boxes, <a href="https://www.msaccesstips.com/2009/09/dynamic-listbox-combobox-contents.html" target="_blank">Combo boxes</a>, and List Boxes on the Form manually doesn't have any flexibility to save time for another Form design or for any other Project, except the Public Function Code in the Standard Module. </p><p>We will try a simple and very effective technique, with the use of a standalone Class Module-based solution that runs for any number of TextBox, ListBox, and ComboBoxes, which you may introduce on the Form any time, automatically without any additional Code writing for them. Besides that, you can transport the Class Module to other Projects. </p><p>Even though the common Event Subroutines GotFocus() and LostFocus() for TextBox Color Attribute settings can be written in the stand-alone Class Module. However, it is better to write them in the Standard Module as two separate Functions and it is easy to call them from the Form Module or from the stand-alone Class Module. </p><p>The first part of this demo is shown as we normally do with the Form Module to get a rough idea of how much time it takes to do that. The second part will be implemented with the addition of a standalone class Module. Part of the VBA Coding is done on the Form <a href="https://www.msaccesstips.com/2011/11/vba-module-object-and-methods.html" target="_blank">Module</a> with flexibility for the addition of TextBox controls on the Form. You will know the difference between the traditional and the new Access VBA Coding approaches.</p>
<h3 style="text-align: left;">The Traditional Method.</h3>
<p>Create a new Form and add 8 TextBoxes to the Form, one below the other. Change the TextBox Names to SID, Description, Quantity, UnitPrice, TaxPcnt, TotalPrice, Discount, and NetPay.</p>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijsyhigoJKnCwjMQGjwEvGpzdWxBmAb2eAVxb0sDQ5Qi_rrs8-5AroSWeofNBvqjZuKrV2Xt_PGTk5WvnxbUw6MgrvKkO3MxiAxkWBn1ACsXvLvllDss0WDinbEOWdvgjHitRzDSCEzJIIRXaFKhdHufypGXnJVRy1ey_PefKbKhy_ycDGgPT9jUWrfVE5/s583/HighLightTextBox.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="489" data-original-width="583" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijsyhigoJKnCwjMQGjwEvGpzdWxBmAb2eAVxb0sDQ5Qi_rrs8-5AroSWeofNBvqjZuKrV2Xt_PGTk5WvnxbUw6MgrvKkO3MxiAxkWBn1ACsXvLvllDss0WDinbEOWdvgjHitRzDSCEzJIIRXaFKhdHufypGXnJVRy1ey_PefKbKhy_ycDGgPT9jUWrfVE5/s320/HighLightTextBox.png" width="320" /></a></div>
<p>First, we will try as we do normally by selecting the <b>OnGotFocus</b> Event Property and setting the Event enabling text "[Event Procedure]" value in the property, then opening the Form Module by clicking on the Code Build button. The Form Module opens up with the empty GotFocus Event Subroutine Stub where we write the VBA Code for highlighting the active TextBox. Here, we will write a single line of Code within the GotFocus Event Subroutine to call the <b>GFColor()</b> Function with the current Form object as the <a href="https://www.msaccesstips.com/2008/11/sum-min-max-avg-paramarray.html" target="_blank">Parameter</a> value. Similarly, write the LostFocus Event Subroutine to call the <b>LFColor()</b> Function from the Standard Module, as shown below. </p><p>If we follow the same route to write Event Subroutines for 16 Event Procedures you can imagine how much time it will take to complete all of them.</p>
<pre>Private Sub SID_GotFocus()
GFColor Me
End Sub
Private Sub SID_LostFocus()
LFColor Me
End Sub
</pre><p>Copy and Paste the above Subroutine Pairs and modify them for other TextBox controls on the Form. When completed the Form Module content will look like the image given below.</p>
<pre>Option Compare Database
Option Explicit
'------------------------------------------------------
'Streamlining Form Module Code
'in Stand-alone Class Modules
'------------------------------------------------------
'Highlighting Textboxes on Form
'Author: a.p.r. pillai
'Date : 13/07/2023
'Rights: All Rights(c) Reserved by www.msaccesstips.com
'------------------------------------------------------
Private Sub SID_GotFocus()
GFColor Me
End Sub
Private Sub SID_LostFocus()
LFColor Me
End Sub
Private Sub Description_GotFocus()
GFColor Me
End Sub
Private Sub Description_LostFocus()
LFColor Me
End Sub
Private Sub Quantity_GotFocus()
GFColor Me
End Sub
Private Sub Quantity_LostFocus()
LFColor Me
End Sub
Private Sub UnitPrice_GotFocus()
GFColor Me
End Sub
Private Sub UnitPrice_LostFocus()
LFColor Me
End Sub
Private Sub TaxPcnt_GotFocus()
GFColor Me
End Sub
Private Sub TaxPcnt_LostFocus()
LFColor Me
End Sub
Private Sub TotalPrice_GotFocus()
GFColor Me
End Sub
Private Sub TotalPrice_LostFocus()
LFColor Me
End Sub
Private Sub Discount_GotFocus()
GFColor Me
End Sub
Private Sub Discount_LostFocus()
LFColor Me
End Sub
Private Sub NetPay_GotFocus()
GFColor Me
End Sub
Private Sub NetPay_LostFocus()
LFColor Me
End Sub
</pre>
<p>These subroutine pairs must be created for any addition of TextBoxes on the Form, to implement the highlight feature for them too. Save the Form with the name <b>Form1_Normal.</b></p><p>We will write two small functions with the appropriate Color Attributes of the TextBox, also suitable for ComboBox and ListBox, the first one for highlighting the TextBox, to run on the OnGotFocus Event, with the Function name <b>GFColor()</b>. The second function <b>LFColor()</b> is for resetting the color of the TextBox on the LostFocus Event. These functions are useful in our standalone Class Module-based Demo runs too. If you prefer a different Color combination then it is easy to change in the Function.</p>
<h4 style="text-align: left;">The Color Attributes change the Function Code in the Standard Module.</h4>
<pre>Option Compare Database
Option Explicit
Dim save_BackColor As Variant
Dim save_BorderWidth As Variant
Dim save_BorderColor As Variant
'------------------------------------------------------
'Streamlining Form Module Code
'in Stand-alone Class Modules
'------------------------------------------------------
'Highlighting Textboxes on Form
'Author: a.p.r. pillai
'Date : 13/07/2023
'Rights: All Rights(c) Reserved by www.msaccesstips.com
'------------------------------------------------------
Public Sub GFColor(ScrForm As Form)
'Active Field Highlight
With ScrForm.ActiveControl
save_BackColor = .BackColor
save_BorderWidth = .BorderWidth
save_BorderColor = .BorderColor
.BackColor = &HD1FDFF
.BorderWidth = 2
.BorderColor = &H1914BA
End With
End Sub
Public Sub LFColor(ScrForm As Form)
'Reset Active Field Highlight
With ScrForm.ActiveControl
.BackColor = save_BackColor
.BorderWidth = save_BorderWidth
.BorderColor = save_BorderColor
End With
End Sub
</pre><p>Copy and Paste the above VBA Function Code into a Standard Module and save it.</p>
<p>Now, open <b>Form1_Normal</b> in normal view and tab-through the TextBoxes to see how it works. If you add two more textboxes on the Form you need to add Event Subroutines in the Form Module for those two textboxes.</p>
<h3 style="text-align: left;">Part-II.</h3><h4 style="text-align: left;">The Stand-alone Class Module supported method.</h4><p>First, let us create a stand-alone Class Module for the same Functions we wrote in the Standard Module. Let us see what difference it makes.</p><ol style="text-align: left;"><li><p>Open the VBA Editing <a href="https://www.msaccesstips.com/2022/04/access-and-windows-api-showwindow.html" target="_blank">Window</a> (ALT+F11).</p></li><li>Select the <b>Class Module</b> Option from the <b>Insert</b> Menu to add a new Class Module. </li><li><p>Click on the new Class Module to select it, Click on the Properties button above to open the <a href=" https://www.msaccesstips.com/2009/11/creating-using-form-custom-property.html" target="_blank">Properties</a> Window in the left panel.</p></li><li>Change the name of the Class Module to <b>myClass1.</b></li><li><p>Copy and Paste the following VBA Code into <b>myClass1</b> Class Module. </p>
<pre>Option Compare Database
Option Explicit
Public WithEvents Tx As TextBox
Public Fm As Form
Dim save_BackColor As Variant
Dim save_BorderWidth As Variant
Dim save_BorderColor As Variant
'------------------------------------------------------
'Streamlining Form Module Code
'in Stand-alone Class Modules
'------------------------------------------------------
'Highlighting Textbox on Form
'Author: a.p.r. pillai
'Date : 13/07/2023
'Rights: All Rights(c) Reserved by www.msaccesstips.com
'------------------------------------------------------
Private Sub Tx_GotFocus() 'TextBox
'GFColor Fm 'Call the standard Module Function
With Tx
save_BackColor = .BackColor
save_BorderWidth = .BorderWidth
save_BorderColor = .BorderColor
.BackColor = &HD1FDFF
.BorderWidth = 2
.BorderColor = &H1914BA
End With
End Sub
Private Sub Tx_LostFocus()
'LFColor Fm 'Call the standard Module Function
With Tx
.BackColor = save_BackColor
.BorderWidth = save_BorderWidth
.BorderColor = save_BorderColor
End With
End Sub
</pre></li></ol><p>Let us review the above VBA Code to understand what they do for highlighting the TextBox on the Form.</p><p>The first declaration line <i>Public WithEvents <b>Tx</b> as TextBox </i>declares an Instance of Access TextBox Class. The <i>Public</i> scope declaration is used here for demo purposes only (normally it will be Private), to avoid writing the Property Procedures and to keep the myClass1 Module Code simple. When the Form is open the active TextBox instance is assigned to the <b>Tx </b>Textbox object in myClass1 Module. The <i><a href="https://www.msaccesstips.com/2019/06/withevents-and-report-line-highlighting.html" target="_blank">WithEvents</a></i> qualification allows the <b>Tx</b> TextBox instance to capture the active TextBox generated Events (in this case the <i>GotFocus</i> and <i>LostFocus</i> Events) in the corresponding Event Procedures coded in the <b>myClass1</b> Class Module.</p>
<!-- Google In-Article Ads start -->
<script async="" src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle" data-ad-client="ca-pub-2376623373770548" data-ad-format="fluid" data-ad-layout="in-article" data-ad-slot="6147807460" style="display: block; text-align: center;"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
<!-- Google In-Article Ads END -->
<p>The next line declares a Form object with the Instance name <b>Fm</b>. The <b>Form1_myClass</b> Form Object will be assigned to the <b>Fm</b> Form Property. We will create this Form after we are through with the VBA Code in <b>myClass1</b> Class Module. Three Variant type Variables are declared to save the existing Color and Property values of the TextBox before changing them in the GotFocus Event Subroutine. These are restored in the LostFocus Event Subroutine.</p><p>Next, in the GotFocus Event Subroutine Private Sub <b>Tx_GotFocus() </b>name the <b>Tx </b>represents the active TextBox on the <a href="https://www.msaccesstips.com/2009/03/positioning-pop-up-forms.html" target="_blank">Form</a>, like <b>Private Sub Description_GotFocus(), </b>if the active TextBox name is <i>Description</i> in the Form. </p><p>The next disabled line calls the GFColor() Function, written earlier in the Standard Module. We kept this line disabled and chose to write the Color Property setting Procedure here in this Module. In the first normal procedure demo, we called this Function with the Form object <b>Me </b>as the parameter. The predefined GotFocus and LostFocus Event Procedures don't have the parameter options to pass these values to the Subroutines. Instead, we will directly assign these values in the <b>Fm</b> and <b>Tx</b> object instances and we can refer to these objects on the Form in the Code.</p><p>Next, a few lines of Code set the appropriate Color attribute values to highlight the active TextBox Control. </p><p>Similarly, the <b>Tx_LostFocus()</b> Event Procedure resets the Color attributes when the TextBox activity is lost.</p><p>We are writing this Code in the Design View of the <b>myClass1</b> Class Module. When this module is loaded into the Computer's memory, then only these actions can take place. The Class Module cannot open on its own, like the Form Modules. For that, we have two options available. </p><p></p><ol style="text-align: left;"><li>Open myClass1 Class Module in memory through the Form_Load() Event Procedure. </li><li>Seek the help of another stand-alone Class Module to do that. We will use the first option in this case. We will demonstrate the second option in Part III.</li></ol><p></p><h3 style="text-align: left;">The Second Part Demo Form.</h3><ol style="text-align: left;"><li><p>Create a Copy of the first Form <b>Form1_Normal</b> and rename it as <b>Form1_myClass</b>.</p></li><li><p>Let the TextBoxes remain as they are on the Form. </p></li><li>Open the Form in Design View and display its Form Module.</li><li><p>Copy the following VBA Code and Paste it over the existing VBA Code in the Form Module. </p>
<pre>Option Compare Database
Option Explicit
Private TBox As myClass1
Private Col As New Collection
'------------------------------------------------------
'Streamlining Form Module Code
'in Stand-alone Class Modules
'------------------------------------------------------
'Highlighting Textbox on Form
'Author: a.p.r. pillai
'Date : 13/07/2023
'Rights: All Rights(c) Reserved by www.msaccesstips.com
'------------------------------------------------------
Private Sub Form_Load()
<blockquote style="border: medium; margin: 0px 0px 0px 40px; padding: 0px;">Set TBox = New myClass1</blockquote><blockquote style="border: medium; margin: 0px 0px 0px 40px; padding: 0px;">Call Class_Init</blockquote>End Sub
Private Sub Class_Init()
Dim ctl As Control
For Each ctl In Me.Controls
Select Case TypeName(ctl)
Case "TextBox"
Set TBox = New myClass1
Set TBox.Fm = Me
Set TBox.Tx = ctl
TBox.Tx.OnGotFocus = "[Event Procedure]"
TBox.Tx.OnLostFocus = "[Event Procedure]"
Col.Add TBox
End Select
Next
End Sub
Private Sub form_Unload(cancel As Integer)
Set Col = Nothing
End Sub
</pre>
</li><li><p>Select <b>Compile</b> from the <b>Debug</b> Ribbon to ensure that no issues with the VBA Code.</p></li>
<li><p>Save the Form with the VBA Code.</p></li><li><p>If you can't hold back your curiosity, then open the Form in Normal View. We will review the Code to know what they do, after your experiments.</p></li><li><p>Press the Tab Key to move from one TextBox to the other.</p><p>We will do another experiment here. Close the Form and open it again in Design View. Make the detail section of the Form wide enough to Copy and Paste the controls together (once or twice). Select the entire set of TextBoxes with their Child Labels, Copy the controls, and Paste them into the adjacent area. Don't bother about the TextBox Names.</p></li>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhpO9uOIbJ31yIemLMOfpMSYFslvMW2nSO-VvIQ0ahJSF3SJvqH8yLkfbRfOEr19F0K9ST3-LVBEaELaNPOnqVt6Q0RFNMF31jfxS8pCxXLCN9GrNwWnrBmRiEzEbakOYSfiU79mmrUr4rTdNf4oF1PdpFbeBlEOCJuNOq1qRdpXkYvIiAIhadsOP3c_4Ww/s686/myClass1.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="609" data-original-width="686" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhpO9uOIbJ31yIemLMOfpMSYFslvMW2nSO-VvIQ0ahJSF3SJvqH8yLkfbRfOEr19F0K9ST3-LVBEaELaNPOnqVt6Q0RFNMF31jfxS8pCxXLCN9GrNwWnrBmRiEzEbakOYSfiU79mmrUr4rTdNf4oF1PdpFbeBlEOCJuNOq1qRdpXkYvIiAIhadsOP3c_4Ww/s320/myClass1.png" width="320" /></a></div>
<li><p>Save the Form and then open it in Normal View, try moving from one TextBox to the other using the Tab Key. The Tab Index order may not be in the expected sequence.</p></li><li><p>Check whether the highlight moves from one TextBox to the other and reaches the new TextBox group's end.</p></li><li><p>You may add a few TextBoxes manually and try them out too.</p></li></ol><p>If you are ready to proceed further, then let us review the Form Module VBA Code segment-wise.</p>
<pre>Option Compare Database
Option Explicit
Private TBox As myClass1
Private Col As New Collection
</pre><p>In the Global Declaration Area, two object declarations are made. The first declaration is for our stand-alone Class Module: <b>myClass1</b> with the Object name <b>TBox</b>. The name TBox is an indicator that it has something to do with the TextBox object. You can give any suitable name preferable to you.</p><p>The purpose of this declaration here is that it is the starting point to bring myClass1 stand-alone Class Module into the Memory. But this declaration alone will not load the Class Object in memory. We must Instantiate this Class Object (or create an Instance) to bring it into memory. In fact, we need several instances of this Object, (as many times as the number of TextBoxes on the Form) and they will be created in the Class_Init Subroutine.</p><p>The second declaration is a Collection Object with the Object name <b>Col</b>, and the usage of the <b>New</b> keyword in the declaration creates an Instance of the Collection object in memory. If you are not familiar with Collection Object please go through the following Links to learn the Basics of this Object:</p>
<ol>
<li><a href="https://www.msaccesstips.com/2018/12/ms-access-and-collection-object-basics.html" target="_Blank">MS-Access and Collection Object Basics</a>
</li><li><a href="https://www.msaccesstips.com/2019/01/ms-access-class-module-and-collection.html" target="_Blank">MS-Access Class Module and Collection Objects</a>
</li><li><a href="https://www.msaccesstips.com/2019/01/table-records-in-collection-object-and.html" target="_Blank">Table Records in Collection Object</a>
</li></ol>
<h4 style="text-align: left;">The Form_Load() Event Subroutine.</h4><pre>Private Sub Form_Load()
Call Class_Init
End Sub</pre>
<p>The <b>Class_Init </b>statement calls the Subroutine to take over the rest of the serious work. The Class_Init part of the Code can be written within the Form_Load() Event Procedure too. But, we will be moving the Class_Init() Subroutine Code into a different Class Module later.</p>
<pre>Private Sub Class_Init()
Dim ctl As Control
For Each ctl In Me.Controls
Select Case TypeName(ctl)
Case "TextBox"
Set TBox = New myClass1
Set TBox.Fm = Me
Set TBox.Tx = ctl
TBox.Tx.OnGotFocus = "[Event Procedure]"
TBox.Tx.OnLostFocus = "[Event Procedure]"
Col.Add TBox
Set TBox = Nothing
End Select
Next
End Sub
Private Sub form_Unload(cancel As Integer)
Set Col = Nothing
End Sub
</pre>
<p>First, we declared an <b>Access.Control</b> object Variable with the name <b>Ctl</b>. In the <b>For. . . Next</b> Loop, it is used to iterate through the controls on the Form and to pick only the TextBox Control in <b>Ctl.</b></p><p>To test and select only the required control, we are using the <b>Select Case...Case...End Select</b> statement with multiple conditions. </p>
<p>Then the <b>TypeName()</b> method is used to check the Type of Control we have in the <b>Ctl</b> object Variable. If it is the TextBox (Case "TextBox") Control then we Instantiate <b>myClass1</b> Class in the <b>TBox</b> object in the statement <b>Set TBox = New myClass1</b>.</p><p><b>Note:</b> <i>We found the first TextBox object, probably with the name: <b>SID,</b><b> </b>found in Ctl Control. Since all the TextBoxes need the same Property settings, their names (like Quantity or UnitPriceare) are not important here. We only need to make sure that it is a TextBox.</i></p><p>In the next step, the Form object <b>Me</b> is passed to the <b>TBox.fm</b> Property of myClass1 Class. The current TextBox object in <b>Ctl</b> is passed to the <b>TBox.Tx</b> Property of the <b>myClass1</b> Class's first Instance.</p>
<p>The next two lines of Code enable the GotFocus and LostFocus Events of the TextBox instance, in myClass1 Class Instance in memory, by setting the Event enabling Property Value with the Text "<i>[Event Procedure]".</i></p><p>So, we have the first Instance TBox of myClass1 Class Module loaded with the reference of the first Textbox from the Form ready to save in the computer's memory and release the TextBox Reference from the TBox instance of myClass1 Class to take the next TextBox on the Form. </p><p>The best option is to save the current instance of myClass1 Class in the Collection Object that we already Instantiated in memory, the next statement <b>Col.Add TBox</b> does that. Using a <b>Key</b> value is not mandatory for Collection Object members. </p><p>We are not planning to retrieve any of the Class Object instances saved in the Collection Object but to keep them in memory and want them to listen to the Events fired from the TextBox in the Form, capture it, and execute the Event Subroutines. When the Form is closed the Collection object with all the Class Object Instances in it will be removed from memory. </p><p>Remember, we already instantiated the Collection object in the global declaration area itself with the <b>New</b> Keyword.</p><p>Now, it is time to execute the statement <b>Set TBox = Nothing</b> to release it from holding the reference of the First TextBox of the Form. </p><p>The <b>For . . . Next</b> loop takes other Textboxes, one after the other, and repeats the same process till all the TextBoxes on the Form are enabled with the Event Procedures and their references are added into the Collection Object in memory. All these actions will happen dynamically when the Form is open. </p><p>In the Form_Unload() Event Subroutine the Collection Object is erased from Memory when the Form is closed.</p><p><b>Note:</b> <i>Normally</i> o<i>n the Form we create physical instances of the TextBox Class as Property of the Form and write the Event Subroutine Code on the Form Class Module. In our new Coding approach, we create the Wrapper-Class Module enclosing a Form Class Object (all Access objects are designed using the stand-alone Class Module) and a TextBox Class that is redefined with the Keyword <a href="https://www.msaccesstips.com/2019/05/withevents-in-class-module-for-sub-form.html" target="_blank">WithEvents</a>, to capture the enabled Events in the Class Module to write their Event Subroutines in the Class Module. </i></p><p><i>We create one instance of the Wrapper Class for each TextBox on the Form. Because the TextBox Class in the Wrapper Class is redefined with the keyword <b>WithEvents</b> and capable of capturing the TextBox's inbuilt Events when fired. When the </i><i>reference of the TextBox on the Form is assigned to the W</i><i>rapper Class TextBox it is as good as the copy of the TextBox on the Form and responds to Events of the TextBox on the Form and executes the Event Procedure written in the Wrapper Class. </i></p><p><i>If you write two different actions for a textbox, one action on the active Form Module and another one in the Wrapper Class-based Textbox both will be executed one after the other. Try writing a 'Hello World' in a MsgBox in the Form Module GotFocus Event Subroutine of any one TextBox and the Form-based action executes first, followed by the Wrapper Class-based instance action next.</i></p><p>Save and Close the Form Form1_myClass.</p><p>Open the Form in Normal View try moving the focus from one TextBox to the other and check whether the TextBox highlighting works as before. You may Remove/Add TextBoxes and try again.</p><h3 style="text-align: left;">Part-III</h3><p>First, we will prepare the <b>myClass2</b> (Version 2 of myClass1) Class Module, then an intermediate Class Module to move the <b>Class_Init</b> Subroutine in it then create a new Demo Form. Creating separate Class Modules and Forms will be helpful to go back to check the earlier methods and to understand where the change took place.</p><p>Create a New Class Module with the name myClass2.</p><p>Copy and Paste the following VBA Code into it and save the Class Module.</p>
<pre>Option Compare Database
Option Explicit
Public WithEvents Tx As TextBox
Public WithEvents Lst As ListBox
Public WithEvents Cbo As ComboBox
Public Fm As Form
'------------------------------------------------------
'Streamlining Form Module Code
'in Stand-alone Class Modules
'------------------------------------------------------
'Highlighting Textbox,ListBox & ComboBox on Form
'Author: a.p.r. pillai
'Date : 13/07/2023
'Rights: All Rights(c) Reserved by www.msaccesstips.com
'------------------------------------------------------
Private Sub Tx_GotFocus() 'TextBox
GFColor fm 'Call Function from Standard Module
End Sub
Private Sub Tx_LostFocus()
LFColor fm
End Sub
Private Sub <span style="color: red;">Lst_</span>GotFocus() 'ListBox
GFColor fm
End Sub
Private Sub <span style="color: red;">Lst_</span>LostFocus()
LFColor fm
End Sub
Private Sub <span style="color: red;">cbo_</span>GotFocus() 'Combo0Box
GFColor fm
End Sub
Private Sub <span style="color: red;">cbo_</span>LostFocus()
LFColor fm
End Sub </pre>
<p>Added two more Properties, the ListBox with the object name <b>Lst</b> and ComboBox (<b>Cbo</b>) Properties in the above Class Module, in addition to the earlier TextBox (<b>Tx</b>) and Form (<b>fm</b>) Properties, in the Global declaration Area. All of them are declared as <b>Public</b> Properties, to avoid writing Property Procedure pairs for each of the declared Properties.</p><p>The Event Subroutines of TextBox are duplicated for ListBox and Comboboxes and check their Subroutine names. The GotFocus and LostFocus Subroutine name prefix, the ListBox is <b>Lst_</b> in <b>Private Sub Lst_GotFocus() </b>and <b>Lst_</b>LostFocus() in Event Subroutine names<b>.</b></p><p>Similarly <b>cbo_</b> for ComboBox Subroutine names: <b>Private Sub cbo_GotFocus()</b> and <b>cbo_</b>LostFocus Event Procedure names.</p><p>Here, we prefer to call the <b>GFColor()</b> and <b>LFColor()</b> Functions to highlight or reset the selected control, rather than writing statements for individual Color attributes settings in all the TextBox, ListBox, and Combobox Event Subroutines.</p><h3 style="text-align: left;">Relocating the Form Module Class_Init() Subroutine.</h3><p>We will transfer the Class_Init() Subroutine into a separate stand-alone Class Module and free the space in the Form Module. We need only a few essential lines of Code on the Form Module. The Class_Init Subroutine will be run from the intermediate Class Module-based Subroutine <b>Class_Init()</b>. </p><p>In this demo with some changes in the Code, we will include the ComboBox and ListBox Controls for highlighting them too, if they are present in the Form.</p><h4 style="text-align: left;">Creating the Intermediate Class Module.</h4><p></p><ol style="text-align: left;"><li>Create a new Class Module in the VBA Editing Window.</li><li>Rename it as <b>myClass_Init</b>.</li><li>Copy and Paste the following VBA Code into the Module and save it:</li></ol><p></p>
<pre>Option Compare Database
Option Explicit
Private TBox As myClass2
Private Fom As Form
Private Col As New Collection
'------------------------------------------------------
'Streamlining Form Module Code
'in Stand-alone Class Modules
'------------------------------------------------------
'Highlighting Textbox, ListBox and ComboBox on Form
'Author: a.p.r. pillai
'Date : 10/07/2023
'Rights: All Rights(c) Reserved by www.msaccesstips.com
'------------------------------------------------------
Public Property Get o_Fm() As Form
Set o_Fm = Fom
End Property
Public Property Set o_Fm(ByRef vFrm As Form)
Set Fom = vFrm
Call Class_Init
End Property
'Class Init Subroutine transferred from Form Module
'
Private Sub Class_Init()
Dim ctl As Control
For Each ctl In Fom.Controls
Select Case TypeName(ctl)
<span style="color: red;">Case "TextBox"</span>
Set TBox = New myClass2 'Instantiate myClass2 Class
Set TBox.Fm = Fom
Set TBox.Tx = ctl
TBox.Tx.OnGotFocus = "[Event Procedure]"
TBox.Tx.OnLostFocus = "[Event Procedure]"
Col.Add TBox
Set TBox = Nothing 'Erase MyClass2 Class
<span style="color: red;">Case "ListBox"</span>
Set TBox = New myClass2 'Instantiate myClass2 Class
Set TBox.Fm = Fom
Set TBox.Lst = ctl
TBox.Lst.OnGotFocus = "[Event Procedure]"
TBox.Lst.OnLostFocus = "[Event Procedure]"
Col.Add TBox
Set TBox = Nothing 'Erase MyClass2 Class<br />
<span style="color: red;">Case "ComboBox"</span>
Set TBox = New myClass2
Set TBox.Fm = Fom
Set TBox.Cbo = ctl
TBox.Cbo.OnGotFocus = "[Event Procedure]"
TBox.Cbo.OnLostFocus = "[Event Procedure]"
Col.Add TBox
Set TBox = Nothing
End Select
Next
End Sub
</pre><p>By looking at the above Code you will know what change we made in there. We declared the <b>myClass2</b> Class in the global declaration area with the object name <b>TBox. </b>In the earlier version of this Class Module <b>myClass1</b> had only the TextBox Class, but in the new Version <b>myClass2</b> added the ListBox and ComboBox Classes also as its Properties. </p><p>The <b>myClass2_Init</b> Class scans the Form with the <b>For . . . Next</b> Loop to look for the presence of the ListBox and Combobox controls besides TextBoxes. If any of them is found then it instantiates the myClass2 Class object into the <b>TBox</b> Object. Take a look at the following four lines of Code:</p><pre> Case "TextBox"
Set TBox = New myClass2 'Instantiate myClass2 Class
Set TBox.Fm = Fom
Set TBox.Tx = ctl
</pre><p>The Case "TextBox" finds a TextBox in <b>Ctl</b>. In the next step, myClass2 Class instantiates into the TBox object. The Form object is assigned in the <b>TBox.fm</b> Property. The <b>Ctl</b> Control now has a TextBox control and this is assigned to the <b>TBox.Tx</b> Property. In the next two steps, the GotFocus and LostFocus Events are enabled, and the TBox myClass2 object instance is added to the Collection object. After that, the TextBox instance Reference in the TBox object is erased.</p><p>If you look at the fourth statement for the ListBox (Set TBox.Lst = Ctl) and ComboBox (Set TBox.cbo = Ctl) they are assigned to the ListBox Property with the name <b>Lst</b> and ComboBox Property <b>cbo</b> in the myClass2 Class Object. When these controls fire the GotFocus and LostFocus Events their corresponding Event Subroutine will be executed in the myClass2 Instance saved in Collection Object in memory.</p><p>The intermediary Class Module <b>myClass2_Init</b> is ready. Now we will create our Demo Part-III Form.</p>
<h3 style="text-align: left;">Prepare Form1_myClass2 Form.</h3><p></p><ol style="text-align: left;"><li><p>Make a Copy of Form <b>Form1_myClass </b>and rename it as <b>Form1_myClass2.</b></p></li><li><p>Open the new Form in Design View.</p>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhkBQxdS6SAdcfjyhFWpqnPjMUlPYq0GadbkvAT8VfZYmJaBEIgxShAkha-bXG4_B8dtWxZ7Tl_i-awm4PCAfSKHnWtbnks6OIjFRBJwJipc5B5H6Wvb4aN2abkAwmsSsXuycPoY7GqtpO0bKGLGxscarT38MLi8iqH_E5aYrlZYD_0v-kOH3UTjOJNsL03/s628/myClass2.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="575" data-original-width="628" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhkBQxdS6SAdcfjyhFWpqnPjMUlPYq0GadbkvAT8VfZYmJaBEIgxShAkha-bXG4_B8dtWxZ7Tl_i-awm4PCAfSKHnWtbnks6OIjFRBJwJipc5B5H6Wvb4aN2abkAwmsSsXuycPoY7GqtpO0bKGLGxscarT38MLi8iqH_E5aYrlZYD_0v-kOH3UTjOJNsL03/s320/myClass2.png" width="320" /></a></div>
</li><li><p>Delete some Textboxes which we copy-pasted to the right side of the Form,</p></li><li>Add two or three List Boxes and a few Combo Boxes on the right side of the Text Boxes.</li><li><p>Save the Form and display the Form VBA Module.</p></li><li>Copy and Paste the following VBA Code in the Module overwriting the existing Code in there.</li></ol><p></p>
<pre>Option Compare Database
Option Explicit
Private Clr As New myClass2_Init
Private Sub Form_Load()
Set Clr.o_Fm = Me
End Sub
Private Sub Form_UnLoad()
Set Clr = Nothing
End Sub
</pre>
<h3>Review of the Form1_myClass2 Module Code.</h3>
<p>In the Form Module, we need the above essential VBA Code to load the Class Module <b>myClass2_Init</b> into memory. The declaration of myClass2_Init Class Module with the object name <b>Clr</b> (short-form for Color) when declared with the keyword <b>New</b> allocates memory to myClass2_Init Class object. </p><p>In the <b>Form_Load()</b> Event Subroutine the Form object <b>Me</b> is assigned to the <b>Clr.o_Frm</b> Property Procedure of myClass2_Init Class Object. After getting the Form Module reference, we can call the Class_Init() Subroutine to scan for TextBox, ListBox, and ComboBoxes on the Form and enable their required Events and facilitate capturing the events in the respective TextBox, ComboBox & ListBox instances saved in the Collection Object in memory. </p><p>When the Form is closed the <b>Form_Unload()</b> Subroutine runs and myClass2_Init Class Object is removed from memory, by the statement <b>Set Clr = Nothing</b>.</p><h3 style="text-align: left;">Loading <i>myClass2</i>, <i>myClass2_Init</i> Class Modules in Memory.</h3><p> When the Form is open, the other two stand-alone Class Modules must be loaded into the memory to remain in sync with each other and to work together.</p><p>In the <b>Form Module</b> the declaration <b>Private Clr As New myClass2_Init</b> Instantiates the myClass2_Init Class and loads it into the memory.</p><p>The <b>myClass2_Init</b> Class Module has the reference to the <b>myClass2</b> Class and creates Instances of it to enable Events after assigning Form and TextBox Instance References, ListBox, and ComboBox instances to save them in the Collection.</p><p>With these declarations, all three Class Modules; <b>Form Module</b>,<b> myClass_Init</b>, and <b>myClass2</b> along with the <b>Collection Object</b> are loaded into memory. When the Form is closed all of them will be removed from Memory. An image posted in an earlier Episode on this topic is reproduced here as how this is made possible and see it in graphical view.</p><div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgm8WYanZYg0h4RBEexxuRn-iPmx-s2dP54CgsGx5ElPLwj5ZuB7CIaQz4kQSd3Yqosfce8FRGR_qeghfd435eXZFzCuKVuPUXtE17k9tp9qxo0HuPL__zhh5FU1yMV8AwcVKjkZfnozuhPaYUjSooONwan1Fru5g2lTKrm9QZ0iFjN_dmJ4rDMFsQ6qPsZ/s823/ClassProtocol3.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="358" data-original-width="823" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgm8WYanZYg0h4RBEexxuRn-iPmx-s2dP54CgsGx5ElPLwj5ZuB7CIaQz4kQSd3Yqosfce8FRGR_qeghfd435eXZFzCuKVuPUXtE17k9tp9qxo0HuPL__zhh5FU1yMV8AwcVKjkZfnozuhPaYUjSooONwan1Fru5g2lTKrm9QZ0iFjN_dmJ4rDMFsQ6qPsZ/s320/ClassProtocol3.png" width="320" /></a></div>
<p>So all three Class Modules will be in memory and work together to achieve the streamlining of the Form Module Coding in the Stand-alone Class Module. </p><p>The <b>Class_Terminate()</b> Subroutine, in <b>myClass2_Init</b> Class Module runs automatically like the Form_Unload() Event Procedure in the Form Module.</p><p>In the Form_Unload() Event Subroutine the statement <b>Set Clr = Nothing</b> gives the signal to close the myClass2_Init Class Object.</p><p>This signals to close of the myClass2 Class Module. Before closing, the Class_Terminate() Subroutine runs and removes the Collection object with all its contents; the instances of Text Boxes, List Boxes, and Combo Boxes.</p><p>Next week, we will continue from our last episode and I hope you enjoyed what you saw here.</p><p>Download Demo Database</p>
<!--Download Link /downloads/2023/07/Streamline7.zip-->
<div class="separator" style="clear: both; text-align: center;">
<a href="https://drive.google.com/file/d/1aKelFg1y4oSKdVqmFQk-91yQM37yCpTL/view?usp=sharing">
<img border="0" data-original-height="29" data-original-width="22" height="29" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFE4uDLH9dk3S6pEcfTkT6GW6r11XqBI4HQnLnhQFZMKFeeQt5ABV_NqvLHM68J7RacISsvbUEl0PlAq6V7i0t0mQF3iesrBuqGuvrf5CJV72OzrGZ7rdMxkxzw8FYlz-qniNySoqtqUN4/s320/zip.gif" width="22" />Streamline7.zip</a>
</div>
<!--Download Link /downloads/2023/07/Streamline7.zip-->
<br />
<h3>Streamlining Form Module Code in Standalone Class Module.</h3>
<ol>
<li><a href="https://www.msaccesstips.com/2023/04/reusing-form-module-vba-code-for-new.html" target="_Blank">Re-using Form Module VBA Code for New Projects.</a></li>
<li><a href="https://www.msaccesstips.com/2023/04/streamlining-form-module-code-part-two.html" target="_Blank">Streamlining Form Module Code Part-Two</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/05/streamlining-form-module-code-part-three.html" target="_Blank">Streamlining Form Module Code Part-Three</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/06/streamlining-form-module-code-part-four.html" target="_Blank">Streamlining Form Module Code Part-Four</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/06/streamlining-form-module-code-part-five.html" target="_Blank">Streamlining Form Module Code Part-Five</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-six.html" target="_Blank">Streamlining Form Module Code Part-Six</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-seven.html" target="_Blank">Streamlining Form Module Code Part-Seven</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-eight.html" target="_Blank">Streamlining Form Module Code Part-Eight</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part-nine.html" target="_Blank">Streamlining Form Module Code Part-Nine</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part-ten.html" target="_Blank">Streamlining Form Module Code Part-Ten</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part.html" target="_Blank">Streamlining Form Module Code Part-Eleven</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-report-module-code-in.html" target="_Blank">Streamlining Report Module Code in Class Module</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-report-module-in-class.html" target="_Blank">Streamlining Report Module Code in Class Module-2</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-form-module-code-part-14.html" target="_Blank">Streamlining Form Module Code Part-14:All Controls</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-custom-made-form-wizard.html" target="_Blank">Streamlining Custom Made Form Wizard-15</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-form-vba-custom-report.html" target="_Blank">Streamlining Custom Report Wizard-16</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-form-vba-external-files.html" target="_Blank">Streamlining Form VBA External File Browser-17</a></li>
<li><a href="https://www.msaccesstips.com/2023/11/streamlining-event-procedures.html" target="_Blank">Streamlining Event Procedures of 3D TextWizard-18</a></li>
<li><a href="https://www.msaccesstips.com/2023/11/streamlining-event-procedures-rgbcolor.html" target="_Blank">Streamlining Event Procedures RGB Color Wizard-19</a></li>
<li><a href="https://www.msaccesstips.com/2023/12/streamlining-numbers-to-words-converter.html" target="_Blank">Streamlining Event Procedures Number To Words-20</a></li>
</ol>
a.p.r. pillaihttp://www.blogger.com/profile/02627590161919120214noreply@blogger.com0tag:blogger.com,1999:blog-7457417942281874810.post-41090356550534051342023-07-04T14:48:00.036+05:302024-01-07T22:49:59.917+05:30Streamlining Form Module Code - Part Six<h4> Continued from the last Episode - Part Five.</h4><h3>Introduction.</h3><p>Streamlining Form Module VBA Code in Standalone Class Module.</p><p>Earlier Episode Links:</p>
<ol>
<li><a href="https://www.msaccesstips.com/2023/04/reusing-form-module-vba-code-for-new.html" target="_Blank">Re-using Form Module VBA Coding for New Projects.</a></li>
<li><a href="https://www.msaccesstips.com/2023/04/streamlining-form-module-code-part-two.html" target="_Blank">Streamlining Form Module Coding Part-Two</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/05/streamlining-form-module-code-part-three.html" target="_Blank">Streamlining Form Module Coding Part-Three</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/06/streamlining-form-module-code-part-four.html" target="_Blank">Streamlining Form Module Coding Part-Four</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/06/streamlining-form-module-code-part-five.html" target="_Blank">Streamlining Form Module Coding Part-Five</a>.</li>
</ol>
<p>After experimenting with the previous examples, you should now have a clear understanding of how the Events Subroutines of TextBox objects work on a Form and how they can be relocated into a standalone Class Module to run the Event Procedures for specific tasks on the Form. </p><p>Our earlier experiments were mainly focused on only one <a href="https://www.msaccesstips.com/2008/10/textbox-and-label-inner-margins.html" target="_blank">TextBox</a> to learn the fundamentals, except the last Part-Five episode where we utilized three TextBoxes to demonstrate enabling events and capturing Raised Events in a standalone Class Module. It is important to note that all these actions were performed solely within the standalone Class Module, rather than within the Form Module.
</p>
<p>I hope you recollect one important point that I mentioned earlier, how we are able to write Event Procedures on the Form Module itself. All <b>Access</b> Objects are designed in stand-alone Class Modules.</p>
<p><b>Note: </b><i>If you would like to find out what they are, then open the Object Browser in the VBA Window and select <b>Access</b> in the <allLibraries> Control. You will find the list under the heading Classes, </i><i>in the left pane. They are in alphabetical order, look for CommandButton, Form, Label, ListBox, TextBox, and others.</i></p> <p>When we insert a TextBox on the Form Access declares the TextBox object Instance with the Keyword <b>WithEvents</b> and assigns a default unique name like Text0. The Name Property Value can be changed according to our requirement manually.</p>
<!-- Google In-Article Ads start -->
<script async="" src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle" data-ad-client="ca-pub-2376623373770548" data-ad-format="fluid" data-ad-layout="in-article" data-ad-slot="6147807460" style="display: block; text-align: center;"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
<!-- Google In-Article Ads END -->
<p>When a TextBox instance is declared with the keyword <b>WithEvents</b>, it allows TextBox to capture and handle its own inbuilt collection of events and execute the Event-related User-written Code within an Event Subroutine like AfterUpdate. In this case, the event subroutine can be written in the parent Form Module, with the TextBox name as the prefix for the subroutine name.</p>
<p>However, when creating a standalone Class Module, specifically for capturing events from the TextBox from the Form, we need to declare a TextBox object instance with the keyword <b>WithEvents</b> within the <a href="https://www.msaccesstips.com/2018/10/ms-access-class-module-and-vba.html" target="_blank">Class Module</a>. Then assign the TextBox Control Reference from the Form to the TextBox Instance. This allows the Class Module to capture and handle the Events Raised by the TextBox. The event subroutine will then be written within the Standalone Class Module itself, rather than in the parent Form Module.</p>
<p>By declaring the TextBox instance with the keyword WithEvents within the Class Module, we establish the necessary connection between the TextBox control and the event handling logic defined within the Class Module. This enables us to encapsulate the event-handling functionality within the Class Module, providing a more modular and reusable approach for handling TextBox events. </p>
<p>In the earlier examples, we were able to capture events from TextBox controls by declaring them with the keyword WithEvents in the standalone Class Module. However, when there are several TextBoxes or other controls on the form, it becomes difficult to declare individual instances for each control and manage them in the Class Module.</p><p><b>A Flexible Approach for Class Module Coding.</b></p>
<p>To handle events from multiple controls efficiently, we can use a dynamic approach. Instead of declaring individual control instances in the Class Module, we can use arrays or collections to manage and iterate through the controls. This allows us to capture events from multiple controls without the need for individual declarations.</p>
<p>For example, if there are 25 TextBox controls on the form and need to handle their Events, we can declare a single TextBox Instance with the keyword WithEvents in a Standalone Class Module. Once it is done, create an Array of these TextBox Instances to hold references of several TextBox controls on the Form. </p>
<p>Similarly, other types of controls like <a href="https://www.msaccesstips.com/2011/06/creating-animated-command-button-with.html" target="_blank">Command Buttons</a> or <a href="https://www.msaccesstips.com/2009/01/combo-box-column-values.html" target="_blank">Combo Boxes</a>, follow a similar approach to manage them through arrays or Collection Object Items and capture their events in the Standalone Class Module.</p>
<p>By using this dynamic approach, it is easy to handle events from a large number of controls on the form in a more scalable and manageable way. It allows us to centralize the event handling logic in the standalone Class Module and provides a more modular and reusable solution.</p>
<h3>Re-organizing the stand-alone Class Module.</h3><p>With all those points in mind, we need to reorganize the Class Module of our last episode and split it into two Parts.</p><ol><li><p>The Event enabling Segment (Class_Init()) in one Class Module</p></li><li>In the second Class Module, a single TextBox Instance and a Form Object Instance Declaration, their Property Procedures, and their Event Subroutines will be placed.</li></ol><p></p><p>The last episode's Class Module (with the name <b>Class3_1</b>) VBA Code is listed below for reference.</p>
<pre>Option Compare Database
Option Explicit
Private frm As Form
Private WithEvents Qty As TextBox 'TextBox object Instance - 1
Private WithEvents UPrice As TextBox 'TextBox object Instance - 2
Private WithEvents Total As TextBox 'TextBox object Instance - 3
'Form's Property GET/SET Procedures
Public Property Get m_Frm() As Form
Set m_Frm = frm
End Property
Public Property Set m_Frm(ByRef mfrm As Form)
Set frm = mfrm
Call class_Init
End Property
'==================================================
Private Sub class_Init()
Dim ctl As Control
Const EP = "[Event Procedure]"
'Scan for TextBox Controls on Form
'and Enable the required Event Procedures
For Each ctl In frm.Controls
Select Case ctl.Name
Case "Quantity"
'Assign Quantity TextBox on Form to 'Qty'
'Private Property of Class_3_1 Class Module
Set Qty = ctl
Qty.Visible = True
'Enable RaiseEvent
Qty.OnExit = EP
Qty.OnGotFocus = EP
Qty.OnLostFocus = EP
Case "UnitPrice"
'Assign UnitPrice TextBox on Form to 'UPrice'
'Private Property of Class_3_1 Class Module
Set UPrice = ctl
'Enable RaiseEvent
UPrice.OnExit = EP
UPrice.OnGotFocus = EP
UPrice.OnLostFocus = EP
Case "TotalPrice"
'Assign TotalPrice TextBox on Form to 'Total'
'Private Property of Class_3_1 Class Module
Set Total = ctl
'Enable RaiseEvent
Total.OnGotFocus = EP
Total.OnLostFocus = EP
End Select
Next
End Sub
'==================================================
Private Sub Qty_Exit(Cancel As Integer)
Dim i As Integer, Msg As String
Dim info As Integer
i = Nz(Qty.Value, 0)
If i < 1 Or i > 10 Then
Msg = "Valid Value Range 1 - 10 Only."
info = vbCritical
Cancel = True
Else
Msg = "Quantity: " & i & " Valid."
info = vbInformation
End If
MsgBox Msg, vbOK + info, "Quatity_Exit()"
End Sub
Private Sub Qty_GotFocus()
With Qty
.backcolor = 65535 '&H20FFFF
.forecolor = 0
End With
End Sub
Private Sub Qty_LostFocus()
On Error Resume Next
With Qty
.backcolor = &HFFFFFF
.forecolor = 0
End With
End Sub
Private Sub UPrice_Exit(Cancel As Integer)
Dim i As Single, Msg As String
Dim info As Integer
i = Nz(UPrice.Value, 0)
Msg = ""
If i <= 0 Then
Msg = "Enter a Value greater than Zero!"
info = vbCritical
Cancel = True
End If
If Len(Msg) > 0 Then
MsgBox Msg, vbOK + info, "UnitPrice_Exit()"
Else
MsgBox "Unit Price: " & i, vbOK + vbInformation, "UPrice_Exit()"
End If
End Sub
Private Sub UPrice_GotFocus()
With UPrice
.backcolor = &H20FFFF
.forecolor = 0
End With
End Sub
Private Sub UPrice_LostFocus()
With UPrice
.backcolor = &HFFFFFF
.forecolor = 0
End With
End Sub
Private Sub Total_GotFocus()
With Total
.backcolor = &H20FFFF
.forecolor = 0
End With
frm!TotalPrice = Qty * UPrice
frm.TotalPrice.Locked = True
End Sub
Private Sub Total_LostFocus()
With Total
.backcolor = &HFFFFFF
.forecolor = 0
End With
End Sub
Private Sub Class_Terminate()
Set Qty = Nothing
Set UPrice = Nothing
Set Total = Nothing
End Sub
</pre><p>The Class_init() Subroutine segment, the area marked by double-dash lines above and below, will be placed within a separate Class Module, with the name "<b>WrapObjects_Init</b>". The Class Module will undergo changes to handle more than one TextBox and other Objects when needed. This Class Module will be dedicated to the Event enabling (RaiseEvent) of all categories of Objects when needed. This Class Module plays an intermediary role in streamlining the Form Module Event Subroutine Code in standalone Class Modules.</p><p>The Code Segment above the first double line and Event Subroutines below the second double line will be placed in a separate Class Module with the name "<b>WrapTextBox</b>". This Class Module is dedicated to the TextBox Objects and for their Event Subroutines only.</p>
<!-- Google In-Article Ads start -->
<script async="" src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle" data-ad-client="ca-pub-2376623373770548" data-ad-format="fluid" data-ad-layout="in-article" data-ad-slot="6147807460" style="display: block; text-align: center;"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
<!-- Google In-Article Ads END -->
<p>We will create the <b>WrapTextBox</b> Class Module with a single TextBox object declaration and with a Form object Instance. We are familiar with the Form and TextBox Property Procedures too. This single Class Module Instances will serve all the TextBoxes on the Form and their Event Subroutines.</p><h3 style="text-align: left;">The WrapTextBox Class Module VBA Code. </h3>
<pre>Option Compare Database
Option Explicit
Private frm As Form 'Form Object declaration
'TextBox Object declaration with keyword WithEvents
Private WithEvents Txt As TextBox 'TextBox object
'Form's Property GET/SET Procedures
Public Property Get tx_Frm() As Form
Set tx_Frm = frm
End Property
Public Property Set tx_Frm(ByRef pfrm As Form)
Set frm = pfrm 'Assin Form
End Property
'TextBox Property GET/SET Procedures
Public Property Get t_Txt() As TextBox
Set t_Txt = Txt
End Property
Public Property Set t_Txt(ByRef tTxt As TextBox)
Set Txt = tTxt
End Property
'Event Subroutines Section
'-------------------------
Private Sub Txt_Exit(Cancel As Integer)
Dim i As Integer, Msg As String
Dim info As Integer
<span style="color: #cc0000;">Select Case Txt.Name</span>
<span style="color: #cc0000;">Case "Quantity"</span>
i = Nz(Txt.Value, 0)
msg = ""
If i < 1 Or i > 10 Then
Msg = "Valid Value Range 1 - 10 Only."
info = vbCritical
MsgBox Msg, vbOK + info, "Quatity_Exit()"
Cancel = True
Else
Msg = "Quantity: " & i & " Valid."
info = vbInformation
MsgBox Msg, vbOK + info, "Quatity_Exit()"
End If
<span style="color: #cc0000;"> Case "UnitPrice"</span>
i = Nz(Txt.Value, 0)
Msg = ""
If i <= 0 Then
Msg = "Enter a Value greater than Zero!"
info = vbCritical
Cancel = True
MsgBox Msg, vbOK + info, "UnitPrice_Exit()"
Else
info = vbInformation
MsgBox "Unit Price: " & i, vbOK + vbInformation, "UPrice_Exit()"
End If
<span style="color: #cc0000;">End Select</span>
End Sub
Private Sub txt_GotFocus()
With Txt
.backcolor = &H20FFFF
.forecolor = 0
End With
<span style="color: #cc0000;">Select Case Txt.Name
Case "TotalPrice"</span>
frm!TotalPrice = (frm!Quantity * frm!UnitPrice)
frm.TotalPrice.Locked = True
<span style="color: #cc0000;">End Select</span>
End Sub
Private Sub txt_LostFocus()
<span style="color: #cc0000;">Select Case Txt.Name</span>
<span style="color: #cc0000;">Case "Quantity", "UnitPrice", "TotalPrice"</span>
With Txt
.backcolor = &HFFFFFF
.forecolor = 0
End With
<span style="color: #cc0000;">End Select</span>
End Sub </pre>
<p>Create a new Class Module and change its name to <b>WrapTextBox</b>. Copy and Paste the Code given above into the Class Module and save the file. </p>
<p>The naming convention you choose for your wrapper class, such as "<b>WrapTextBox</b>," is flexible and can be customized according to your preference and coding standards. The term "Wrap" in the class name is often used to indicate that the class serves as a wrapper or encapsulation for another class or object.</p>
<p>In this case, the "WrapTextBox" class serves as a wrapper for the <b>Access.TextBox </b>class and the <b>Access.Form</b> class objects. It encapsulates the functionality related to capturing and handling events fired from the TextBoxes on the form.</p>
<p>By declaring the TextBox instance with the <b>WithEvents</b> keyword in the WrapTextBox class, you establish a direct connection between the TextBox object on the form and the event handlers within the WrapTextBox class. This allows the WrapTextBox class to capture and execute the event subroutines associated with the TextBox on the <a href="https://www.msaccesstips.com/2008/12/custom-made-form-wizard.html " target="_blank">Form</a>.</p>
<p>The purpose of using a wrapper class like WrapTextBox is to centralize the event-handling logic for multiple TextBox objects and provide a modular and scalable approach to managing those events. The wrapper class acts as an intermediary between the form and the individual TextBox instances, making it easier to handle events and apply common functionality across multiple TextBoxes.</p>
<p>Ultimately, the specific name you choose for the wrapper class should align with your project's naming conventions and accurately represent its purpose as a wrapper for TextBox objects.</p>
<p>How to capture TextBox Events originating from the Form in a single instance of the TextBox object in the Wrapper Class? In last week's demo trial runs we declared three TextBox instances for Quantity, Unit Price, and Total Price. That is the new trick we are going to explore here.</p><p>We used a generic name Txt for the TextBox instance declaration in the WrapTextBox Class. The <a href="https://www.msaccesstips.com/2009/11/creating-using-form-custom-property.html" target="_blank">Property</a> Procedure Code of TextBox and Form is transferred as it is from Class Module Class 3_1.</p><p>Since Txt is the TextBox object instance name, we must use this name as the Event Subroutine name prefix in all cases rather than using the TextBox Control names on the Form. Besides that, in earlier trial runs there were a total of eight Event Procedures: for Quantity and UnitPrice TextBoxes (OnExit, OnGotFocus, and OnLostFocus) three Event Procedures each, and two (OnGotFocus & OnLostFocus) for TotalPrice TextBox. But here there are only three physical Event Subroutines: Sub txt_Ext(), Sub txt_GotFocus(), and Sub txt_LostFocus().</p><p>The logic is very simple when the Exit() Event fires from any of the three TextBoxes the wrapper class calls the <b>Sub txt_Exit()</b> Event Subroutine. We already enabled the OnExit Event in Quantity and UnitPrice Texboxes on the Form. At this point, it is unknown exactly from which TextBox the Event is fired. But, to identify the name of the TextBox that fired the Event, the <b>Select Case . . . Case . . . End Select</b> structure runs a test, and based on that the corresponding Event Subroutine is executed as shown above. </p><p><b>Note:</b> <i>Assume that there are 15 TextBoxes on the Form you enabled all the Events of all the TextBoxes without checking their Names. Then you write the Event Subroutine of a few TextBox for their selected Events by checking their Names, only those Event Subroutines will be executed, other TextBoxes and enabled Events are ignored.</i></p><p>The same procedure can be followed to identify the Events OnGotFocus and OnLostFocus and write their Code in the same way. Since the LostFocus Event Procedure applies to all TextBox Controls to reset the background with the same color, no testing was necessary. </p><p>Create another Class Module with the name <b>WrapObjects_Init</b>. Copy and Paste the following Event-enabling Code into the Class Module and save it.</p>
<pre>Option Compare Database
Option Explicit
Private iTxt As WrapTextBox 'Instance of WrapTextBox
Private TxtArray() As WrapTextBox 'TextBox Array
Private ifrm As Access.Form
'Form's Property GET/SET Procedures
Public Property Get m_Frm() As Form
Set m_Frm = ifrm
End Property
Public Property Set m_Frm(ByRef mfrm As Form)
Set ifrm = mfrm
Call class_Init
End Property
Private Sub class_Init()
Dim ctl As Control
Dim j As Integer
Const EP = "[Event Procedure]"
'Scan for TextBox Controls on Form
'and Enable the required Event Procedures
For Each ctl In ifrm.Controls
Select Case TypeName(ctl)
Case "TextBox"
Select Case ctl.Name
Case "Quantity"
<span style="color: #cc0000;">Set iTxt = New WrapTextBox</span> 'Create Instance
Set iTxt.tx_Frm = ifrm
Set iTxt.t_Txt = ctl
iTxt.t_Txt.OnExit = EP
iTxt.t_Txt.OnGotFocus = EP
iTxt.t_Txt.OnLostFocus = EP
<span style="color: #cc0000;"> j = j + 1
ReDim Preserve TxtArray(1 To j)
Set TxtArray(j) = iTxt 'Save it in Object Array
Set iTxt = Nothing </span> <span style="color: #cc0000;">'Erase temp Instance</span>
Case "UnitPrice"
Set iTxt = New WrapTextBox 'Create Instance
Set iTxt.tx_Frm = ifrm
Set iTxt.t_Txt = ctl
iTxt.t_Txt.OnExit = EP
iTxt.t_Txt.OnGotFocus = EP
iTxt.t_Txt.OnLostFocus = EP
j = j + 1
ReDim Preserve TxtArray(1 To j)
Set TxtArray(j) = iTxt 'Save it in Object Array
Set iTxt = Nothing 'Erase temp Instance
Case "TotalPrice"
Set iTxt = New WrapTextBox 'Create Instance
Set iTxt.tx_Frm = ifrm
Set iTxt.t_Txt = ctl
iTxt.t_Txt.OnGotFocus = EP
iTxt.t_Txt.OnLostFocus = EP
j = j + 1
ReDim Preserve TxtArray(1 To j)
Set TxtArray(j) = iTxt 'Save it in Object Array
Set iTxt = Nothing 'Erase temp Instance
End Select
Case "CommandButton"
Select Case ctl.Name
Case "cmdClose"
'Code
End Select
End Select
Next
End Sub
Private Sub Class_Terminate()
Set ifrm = Nothing
Erase TxtArray
End Sub
</pre>
<h3 style="text-align: left;">VBA Code Review.</h3><p>Let's go through the code and identify the changes compared to last week's demo code, which focused on three different TextBoxes on the Form: Quantity, UnitPrice, and TotalPrice. As you already know, it's difficult to declare 25 separate instances of the TextBox class with 25 different names if there are that many TextBoxes requiring event handling on the Form. This approach would be cumbersome and not practical.</p>
<p>Instead, the solution lies in using a more flexible and scalable approach. Creating a standalone Class Module defines a custom TextBox Class that encapsulates the event handling logic. This Class can be instantiated multiple times, allowing us to handle events for any number of TextBoxes dynamically. The WrapTextBox Class is created just for that.</p>
<p>In the updated code, you'll find the implementation of the custom TextBox class (WrapTexBox Class) in the standalone Class Module. This class will contain the necessary event procedures and any additional functionality required. The <b>WrapObjects_Init</b> Module will then instantiate instances of this custom TextBox class for each TextBox control on the Form.</p>
<p>This approach enables to centralization of the event handling logic, making it more manageable and scalable, regardless of the number of TextBoxes present on the Form.</p>
<p>On the global declaration area of the Demo Form Class Module, declare an Instance of the <b>WrapObjects_Init</b> Class. When the Form is open the WrapObjects_Init Class will be Instantiated and will become active in Memory. When this Class is active it declares two Instances of the <b>WrapTextBox</b> Class and these TextBox Class Instances will also become active in memory. So when the Form is open all three Class Objects (Form Module, WrapObjects_Init Class, and WrapTextBox Class Objects(2 instances) are active in memory. The functional diagram of all three Class Objects is given below. It shows how they are related to each other and become active in memory at the same time. </p><h4 style="text-align: left;">Class Modules in Memory.</h4>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgm8WYanZYg0h4RBEexxuRn-iPmx-s2dP54CgsGx5ElPLwj5ZuB7CIaQz4kQSd3Yqosfce8FRGR_qeghfd435eXZFzCuKVuPUXtE17k9tp9qxo0HuPL__zhh5FU1yMV8AwcVKjkZfnozuhPaYUjSooONwan1Fru5g2lTKrm9QZ0iFjN_dmJ4rDMFsQ6qPsZ/s823/ClassProtocol3.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="358" data-original-width="823" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgm8WYanZYg0h4RBEexxuRn-iPmx-s2dP54CgsGx5ElPLwj5ZuB7CIaQz4kQSd3Yqosfce8FRGR_qeghfd435eXZFzCuKVuPUXtE17k9tp9qxo0HuPL__zhh5FU1yMV8AwcVKjkZfnozuhPaYUjSooONwan1Fru5g2lTKrm9QZ0iFjN_dmJ4rDMFsQ6qPsZ/s320/ClassProtocol3.png" width="320" /></a></div>
<p>The Class_Init() Procedure segment, in our earlier Demo Class module, is now transformed into an independent Class Module with the name WrapObjects_Init. An instance of the WrapTextBox Class, with the name <b>iTxt, </b>is declared in the Global declaration area of this Module.</p><p>A second Instance of the WrapTextBox Class Module is declared as an Array object with an unspecified number of elements with the object name <b>TxtArray()</b> in there too. </p><p>The first one <b>iTxt</b> is a temporary instance to assign the TextBox's reference one by one from the Form, to access its Event Properties to enable the required Events and then pass it on to the TxtArray(). After that iTxt will be erased to make it ready to take the next TextBox reference from the Form. </p><p>The TxtArray() will be re-dimensioned dynamically, based on the number of Textboxes on the Form, their Events enabled before the TextBox is stored in the Array. In both of these global declarations, the keyword WithEvents is not used, because the TextBox object declaration in the global area of WrapTextBox Class is already qualified with the Keyword WithEvents to enable them to listen to the TextBox Events on the Form. Each TextBox Reference from the Form is assigned to different Instances of the WrapTextBox Class and required Events are enabled and stored in the TxtArray().</p><h3 style="text-align: left;">Access.TextBox Class Instances</h3>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEit4TeEbSXXxXqC0a-M1KNcGmr7d-W8vv_Gtzlh6GvLlW-TtrBSpHwfHt80W5vQ0gmXROqeIdrSsaFiVzs7owAiCbFE5y4Z8zCzmBbR9vVbdNIHLhczrooqXGG5-nE0xjpkaIf35uic1_wKQMKxs3z4iTBoEFU3UVGW9rXkG3nZBgGLoN0VnEGnjmJJ8Jat/s794/TextArray2.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="527" data-original-width="794" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEit4TeEbSXXxXqC0a-M1KNcGmr7d-W8vv_Gtzlh6GvLlW-TtrBSpHwfHt80W5vQ0gmXROqeIdrSsaFiVzs7owAiCbFE5y4Z8zCzmBbR9vVbdNIHLhczrooqXGG5-nE0xjpkaIf35uic1_wKQMKxs3z4iTBoEFU3UVGW9rXkG3nZBgGLoN0VnEGnjmJJ8Jat/s320/TextArray2.png" width="320" /></a></div>
<p>After these two declarations as usual the Form Object declaration is also inserted in the Global declaration area with the Property name <b>iFrm</b>. The Form object that is passed from the Form Class Module through the Form Property Procedure <b>m_Frm()</b> is assigned to the <b>iFrm </b>Property. This will be passed on to the <b>Frm</b> Property declaration in the global area of WrapTextBox Class through the <b>tx_Frm()</b> Property Procedure.</p><p>It is important to get familiarized with the Code in this particular Class Module and to know what it does. The <b>WrapObjects_Init</b> Class Module along with the open Form and the TextBox Wrapper Class should work all together instantly when the Form is open. It should Enable the Events of the required Controls on the Form. The <b>WrapTextBox</b> Class should capture the Events when fired from the Form and execute the Event Subroutine. There will be only one Class_Init Wrapper Class for a Form but there can be several Object Wrapper Classes, one for each type of Control on the Form, like Command Buttons, Combo boxes, and others. </p><h3 style="text-align: left;">The Class_Init() Subroutine.</h3><p>Let us check the Class_Init() Subroutine Code. There are two local variable declarations. The first one is a Control Variable <b>Ctl</b> to scan through the Form to find the Event enabling TextBoxes and the second one is an Integer Variable. The third one is a constant that holds the text "[Event Procedure]".</p><p>The <i><b>For Each ctl In ifrm.Controls</b> are </i>the start of the For . . . Next Loop and in the next step we should check whether the first Control found in the Ctl Variable is the Type of control that we are looking for, the TextBox. <b>The <i>Select Case Typename(Ctl)</i></b> does that. We are only interested in the TextBox Controls on the Form. So the <b>Case "TextBox" </b>does just that. If that is proven true, then we will further filter them by their names with an inner <i><b>Select Case Ctl.Name</b></i> check and take only the Quantity, UnitPrice, and TotalPrice TextBoxes on the Form. Let us bring that Code Segment here and take a close look at the VBA Code.</p>
<pre>For Each ctl In ifrm.Controls
Select Case TypeName(ctl)
Case "TextBox"
Select Case ctl.Name
Case "Quantity"
Set iTxt = New WrapTextBox 'Create an Instance of WrapTextBox Class
Set iTxt.tx_Frm = ifrm 'Pass the Form Object to WrapTextBox Property
Set iTxt.t_Txt = ctl 'Pass the TextBox Control 'Quantity'
iTxt.t_Txt.OnExit = EP
iTxt.t_Txt.OnGotFocus = EP
iTxt.t_Txt.OnLostFocus = EP
j = j + 1
ReDim Preserve TxtArray(1 To j)
Set TxtArray(j) = iTxt 'Save it in Object Array
Set iTxt = Nothing 'Erase temp Instance
Case "UnitPrice"
</pre>
<p>When we detect the Quantity TextBox Control in Ctl then instantiate the WrapTextBox Object in the local <b>iTxt</b> object. Remember the TextBox Class is wrapped in the WrapTextBox Class. After creating an Instance of the WrapTextBox Class we can access its TextBox and Form Properties through the Public Property Procedures. Through these Property Procedures, assigns the Form object <b>iFrm</b> and Quantity TextBox Control Reference in <b>Ctl</b>, and they are passed on to their corresponding Properties in the WrapTextBox Class through its own Public Property Procedures:</p>
<pre> Select Case ctl.Name
Case "Quantity"
Set iTxt = New WrapTextBox 'Create an Instance of WrapTextBox Class
Set iTxt.tx_Frm = ifrm 'Pass the Form Object to WrapTextBox Property
Set iTxt.t_Txt = ctl 'Pass the TextBox Control 'Quantity' Reference
</pre>
<p>The next step in this Module is to enable the required Event Properties of the Quantity TextBox to fire the Events so that they can be captured in the WrapTextBox Class Instance and execute the related Event Subroutines for the required Task. We need to enable three Events <b>OnExit, OnGotFocus</b>, and <b>OnLostFocus.</b> The VBA Code is given below.</p>
<pre> iTxt.t_Txt.OnExit = EP 'Enable Event "[Event Procedure]"
iTxt.t_Txt.OnGotFocus = EP "
iTxt.t_Txt.OnLostFocus = EP "
</pre>
<p>Remember, we are working on the first instance of the WrapTextBox Class Object. In last week's trial run, we created the TextBox instances manually with the name <b>Qty</b>, <b>UPrice,</b> and <b>Total</b> for TextBox Controls Quantity, UnitPrice, and TotalPrice TextBox on the Form. Earlier we wrote the Event Procedure in the WrapObjects_Init Class (Class3_1 Class) Module itself. That has changed now, a separate WrapTextBox Class Module with TextBox and Form Object Properties takes care of the Event handling task. The TextBox Class Object Instance is declared with the <b>WithEvents</b> keyword to capture its inbuilt Events and Execute the Event Subroutine in the WrapTextBox Class.</p><p>The next few lines of Code are very important to keep watching and pay close attention to.</p><p>Our WrapTextBox instance was created in the WrapObject_Init Class and the active Form object instance is assigned to the Frm Property and also assigned to the Quantity TextBox instance. The required events for the Quantity TextBox on the Form are enabled. This particular WrapTextBox instance is created now for the Quantity TextBox only. </p><p>We will create two more instances of WrapTextBox Class for UnitPrice and TotalPrice TextBoxes on the Form using the same iTxt WrapTextBox. </p><p>Before that the current Instance iTxt must be saved in the TxtArray() in memory, then release the iTxt instance for reuse.</p><p>We declared a WrapTextBox Array with the name TxtArray(). The required number of Array elements is not known in advance, they will be redefined as and when the need arises. Now, the Quantity TextBox is current and enabled with the required Events OnExit, OnGotFocus, and OnLostFocus (but only one of these Events will be fired at one time) and is ready to save the instance after Redimensioning the Array by incrementing its array elements count J by 1. The Code segment is given below.</p>
<pre> J = J + 1
ReDim Preserve TxtArray(1 To J)
Set TxtArray(J) = iTxt 'Save it in Object Array
Set iTxt = Nothing 'Erase temp Instance
</pre>
<p>The Array index Variable incremented by 1. The TextArray() of WrapTextBox is Redimensioned for 1 to 1 elements and the keyword <b>Preserve</b> ensures that the earlier elements, if any, with the data should be preserved. The statement <i>Set TxtArray(j) = iTxt</i> saves the WrapTextBox object instance <b>iTxt</b> in the first Array element of the TxtArray() object collection. The next statement Set iTxt = Nothing releases the memory occupied by this temporary WrapTextBox Instance for reuse.</p>
<p>When we instantiate an object in memory it occupies a certain area of the Computer's memory with a specific reference address. When we save the instance of this object in the Array, this address is copied into the array element and held there. This enables us to release the TextBox instance and reuse it for other TextBoxes on the Form. So the statement <b>Set iTxt = Nothing</b> releases it from holding on to the same memory Reference. Since, TxtArray is holding the WrapTextBox Instance reference so long as all the objects: the <b>Form</b>, <b>WrapObjects</b>_Init and <b>WrapTextBox</b> Classes are active, the saved Instance of Object in the Array will be safe and active too.</p><p>When we assign another TextBox Instance to the same temporary object <b>iTxt</b> it will look for free space in memory and will not overwrite earlier objects held in memory.</p><p>When the UnitPrice and TotalPrice Text Boxes are detected on the Form the same process is repeated and their corresponding Events will be enabled the Array is Redimensioned and the object instances are saved in the Array Elements. </p><p>When an enabled Event of Quantity or UnitPrice or TotalPrice fires from the Form it is captured in its corresponding Array element-based Event Subroutine and is executed. </p><p>Now, let us design our Demo Form.</p>
<ol>
<li><p>Make a Copy of the last episode Form, with the name Form1_RaiseEvent_4, and rename it as <b>frmMultiple_TextBoxes</b>.</p>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiY32yledMaN239-Jq3vtXrWIOMu53gzAP5xP9_iAmfb5cZEL7J-zQUQL4jDX1ANfyl6qXyb59xf09SnRUPXnd25Pt0eb3ptn17Kp8AyTPCP-ANJrF_i2EhOhsba101ss8FaCWzEfN55-zxgyy5cLPMBAVzyGG2FsEsIMDT3CTg7hEIiytvF2Xw2QGW0AJm/s640/MultipleTextbox.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="640" data-original-width="529" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiY32yledMaN239-Jq3vtXrWIOMu53gzAP5xP9_iAmfb5cZEL7J-zQUQL4jDX1ANfyl6qXyb59xf09SnRUPXnd25Pt0eb3ptn17Kp8AyTPCP-ANJrF_i2EhOhsba101ss8FaCWzEfN55-zxgyy5cLPMBAVzyGG2FsEsIMDT3CTg7hEIiytvF2Xw2QGW0AJm/s320/MultipleTextbox.png" /></a></div>
</li><li><p>Open the <b>frmMultiple_TextBoxes</b> Form in Design View and change the Heading Label Caption to '<i>Events of Multiple Text Boxes on Form</i>'.</p></li><li><p>Display the Form Module.</p></li><li><p>Copy and Paste the following VBA Code into the Form Module overwriting the existing Code:</p>
<pre>Option Compare Database
Option Explicit
Private C As WrapObjects_Init
Private Sub Form_Load()
Set C = New WrapObjects_Init
Set C.m_Frm = Me
End Sub
Private Sub Form_Unload(Cancel As Integer)
Set C = Nothing
End Sub
</pre></li>
<li><p>Select Compile from the Debug Menu to Compile the VBA Code to ensure that everything is ok with the Code. Save the Form with the Code.</p></li>
<li><p>Open the Form in Normal View.</p></li><li><p>Enter a Value 10 or less in the Quantity TextBox and press Enter Key. It will display a message displaying the entered value from the Quantity TextBox.</p></li><li><p>Enter some numerical value in the Unit Price TextBox and press Enter Key. It should display the entered value in MsgBox.</p></li></ol><p>When the Total Value Textbox receives the Focus, the GotFocus Event fires and displays the result of the (Quantity * UnitPrice) Value in the TotalPrice TextBox.</p><p>Try entering a value larger than 10 in Quantity TextBox and see what happens.</p><p><b>Demo Database Download.</b></p><p>The Demo Database Download Link is given below:</p>
<!--Download Link /downloads/2023/07/Streamline6.zip-->
<div class="separator" style="clear: both; text-align: center;">
<a href="https://drive.google.com/file/d/1ldKC0aG305QsytmavD1Z-XNjsFgXSaEf/view?usp=sharing">
<img border="0" data-original-height="29" data-original-width="22" height="29" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFE4uDLH9dk3S6pEcfTkT6GW6r11XqBI4HQnLnhQFZMKFeeQt5ABV_NqvLHM68J7RacISsvbUEl0PlAq6V7i0t0mQF3iesrBuqGuvrf5CJV72OzrGZ7rdMxkxzw8FYlz-qniNySoqtqUN4/s320/zip.gif" width="22" />Streamline6.zip</a>
</div>
<!--Download Link /downloads/2023/07/Streamline6.zip-->
<p><br /></p>
<h3>Streamlining Form Module Code in Standalone Class Module.</h3>
<ol>
<li><a href="https://www.msaccesstips.com/2023/04/reusing-form-module-vba-code-for-new.html" target="_Blank">Re-using Form Module VBA Code for New Projects.</a></li>
<li><a href="https://www.msaccesstips.com/2023/04/streamlining-form-module-code-part-two.html" target="_Blank">Streamlining Form Module Code Part-Two</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/05/streamlining-form-module-code-part-three.html" target="_Blank">Streamlining Form Module Code Part-Three</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/06/streamlining-form-module-code-part-four.html" target="_Blank">Streamlining Form Module Code Part-Four</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/06/streamlining-form-module-code-part-five.html" target="_Blank">Streamlining Form Module Code Part-Five</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-six.html" target="_Blank">Streamlining Form Module Code Part-Six</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-seven.html" target="_Blank">Streamlining Form Module Code Part-Seven</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-eight.html" target="_Blank">Streamlining Form Module Code Part-Eight</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part-nine.html" target="_Blank">Streamlining Form Module Code Part-Nine</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part-ten.html" target="_Blank">Streamlining Form Module Code Part-Ten</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part.html" target="_Blank">Streamlining Form Module Code Part-Eleven</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-report-module-code-in.html" target="_Blank">Streamlining Report Module Code in Class Module</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-report-module-in-class.html" target="_Blank">Streamlining Report Module Code in Class Module-2</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-form-module-code-part-14.html" target="_Blank">Streamlining Form Module Code Part-14:All Controls</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-custom-made-form-wizard.html" target="_Blank">Streamlining Custom Made Form Wizard-15</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-form-vba-custom-report.html" target="_Blank">Streamlining Custom Report Wizard-16</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-form-vba-external-files.html" target="_Blank">Streamlining Form VBA External File Browser-17</a></li>
<li><a href="https://www.msaccesstips.com/2023/11/streamlining-event-procedures.html" target="_Blank">Streamlining Event Procedures of 3D TextWizard-18</a></li>
<li><a href="https://www.msaccesstips.com/2023/11/streamlining-event-procedures-rgbcolor.html" target="_Blank">Streamlining Event Procedures RGB Color Wizard-19</a></li>
<li><a href="https://www.msaccesstips.com/2023/12/streamlining-numbers-to-words-converter.html" target="_Blank">Streamlining Event Procedures Number To Words-20</a></li>
</ol>
a.p.r. pillaihttp://www.blogger.com/profile/02627590161919120214noreply@blogger.com0tag:blogger.com,1999:blog-7457417942281874810.post-65046323873545731242023-06-23T14:11:00.033+05:302024-01-07T22:49:11.220+05:30Streamlining Form Module Code - Part Five<h4> Continued from the last Episode - Part Four.</h4><h3>Introduction.</h3><p>Writing Form Module VBA Code in Standalone Class Module.</p><p>Earlier Episode Links:</p>
<ol>
<li><a href="https://www.msaccesstips.com/2023/04/reusing-form-module-vba-code-for-new.html" target="_Blank">Re-using Form Module VBA Coding for New Projects.</a></li>
<li><a href="https://www.msaccesstips.com/2023/04/streamlining-form-module-code-part-two.html" target="_Blank">Streamlining Form Module Coding Part-Two</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/05/streamlining-form-module-code-part-three.html" target="_Blank">Streamlining Form Module Coding Part-Three</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/06/streamlining-form-module-code-part-four.html" target="_Blank">Streamlining Form Module Coding Part-Four</a>.</li>
</ol>
<p>Readers still need to go through the earlier Posts on this topic may please read them before continuing.</p><p>So far we worked with only one TextBox object on the Form and found how it can be programmed in several ways by writing VBA Code in a standalone Class Module rather than on the Form Module. We are familiar with the rules that govern defining <i>Events</i>, <i>RaiseEvent </i>to announce the Event, and declaring a TextBox object instance with the keyword <i>WithEvents,</i> for capturing the Events in the Class Module, raised on the TextBox control on the Form. </p><p>Let us see what it takes to deal with Events for more than one TextBox on the Form. This time we will introduce three TextBoxes on the Form for our experiments of Event handling in the standalone Class Module. Normally, we can expect 10 to 20 or more TextBoxes on a Form, but very unlikely that all of them need to be programmed with Event Procedures. But, if all of them are required we can do that too. There are several inbuilt Events in a TextBox but all of them may not be used, but several of them may be used depending on the requirements. We must be prepared to take that challenge too.</p>
<!-- Google In-Article Ads start -->
<script async="" src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle" data-ad-client="ca-pub-2376623373770548" data-ad-format="fluid" data-ad-layout="in-article" data-ad-slot="6147807460" style="display: block; text-align: center;"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
<!-- Google In-Article Ads END -->
<p>Let's start with three TextBoxes and see how we can handle Coding for a few Event Procedures. We will start with a crude approach first that works okay in the beginning, even though it is not an elegant solution, but is easy to understand.</p><h3 style="text-align: left;">Demo Form Design.</h3><div>Create a new Form with the following design in your database:<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjUMsO3jOlwRZi7RfNR7NU2vwHLsyF3hDqSF2Qn4-8PgBfYfh7XXS2ZPVqMnpBF_gCx1ThKW_Nr1dmt6k4NlxxtE1xyflGaeTK7bsCRQtAQSh-04_NNMyvQNOeaCa4juTi9WOM3OyUysW2H7PS9gl4TBotWuZbc0weiPrPYvaoYAGsnB58pyTVmNektKA/s494/MultipleTxtbox1.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="450" data-original-width="494" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjUMsO3jOlwRZi7RfNR7NU2vwHLsyF3hDqSF2Qn4-8PgBfYfh7XXS2ZPVqMnpBF_gCx1ThKW_Nr1dmt6k4NlxxtE1xyflGaeTK7bsCRQtAQSh-04_NNMyvQNOeaCa4juTi9WOM3OyUysW2H7PS9gl4TBotWuZbc0weiPrPYvaoYAGsnB58pyTVmNektKA/s320/MultipleTxtbox1.png" width="320" /></a></div><ol><li><p>Create a new Form and insert 3 unbound TextBoxes, one below the other, on the Detail Section of the Form. </p></li><li><p>Insert a Label control above the Textboxes and write the description as shown in the Form Image given above, in its Caption Property.</p></li><li><p>Select the first TextBox and display the Property Sheet. Change the Name Property Value to <i>Quantity</i> and change its Child-label caption to Quantity(1-10).</p></li><li><p>Select the second TextBox change its Name Property Value to <i>UnitPrice</i> and change the child-label Caption to Unit Price.</p></li><li><p>Change the Name Property value of the third TextBox as <i>TotalPrice</i> and enter Total Price in the child-label Caption.</p></li><li><p>Display the VBA Code Module of the Form.</p></li><li><p>Copy and Paste the following VBA Code into the Form Module, overwriting existing lines of Code, if any:</p>
<pre>Option Compare Database
Option Explicit
<b>Private C As New Class3_1</b> 'create an instance of Class3_1 Class Object
Private Sub Form_Load()
Set C.m_Frm = Me 'Pass current Form Object to C.m_Frm Property Procedure
End Sub
Private Sub Form_Unload(Cancel As Integer)
Set C = Nothing 'Release the Class Object instance from memory
End Sub
</pre>
</li><li><p>Save the Form with the name <b>Form1_RaiseEvent_4</b></p></li></ol>
<p>We made some changes, in the earlier Global Declaration of Class Object C to create an Instance of the Class Module <b>Class3_1</b> object. This is the short form (<i><b>Private C As New Class3_1</b></i>) of the following two-line statements we used in earlier cases:</p>
<pre>Private C As Class3_1 'create an instance of Class3_1 Class Object
Private Sub Form_Load()
Set C = New Class3_1
End Sub</pre><p>The <i><b>New</b> </i>keyword<i> </i>is used in the class object declaration itself to create an instance of the Class Object in memory. The current Form Object <b>Me</b> is passed to the SET Property Procedure <b>C.m_Frm()</b> as a Parameter in the Form_Load() Event Procedure. You will see the <b>m_Frm() </b>Property Procedure when we create the <b>Class3_1</b> Class Module Code. The Class Object declaration in the Global area alone will not load the Class Object into the Memory. We instantiated the class object with the keyword <b>New </b>to load an instance of the class object into memory. When we open a Form its Class Module is also loaded into the memory, when the Form's <b>Has Module</b> Property is set to <b>Yes</b>.</p><p>The Form_Unload() Event Procedure removes the Class Object Instance <b>C</b> from memory to release the memory for other uses, when the Form is closed.</p><h3 style="text-align: left;">The New Class Module: Class3_1.</h3><p>We will find a better way to name our Class Modules so that by looking at them, we can easily guess where it belongs, or what category of object it is. But, we are on a learning curve now, and understanding the new concept is more important than such cosmetic changes. </p><p>Create a new Class Module, display its Property Sheet (Select <i>Properties Window</i> from the View Menu), and change its <i>Name</i> Property Value to <b>Class3_1</b>.</p><p>Copy and Paste the following VBA Code into the Class Module and Save the Module:</p>
<pre>Option Compare Database
Option Explicit
Private frm As Form
Private WithEvents Qty As TextBox 'TextBox object Instance - 1
Private WithEvents UPrice As TextBox 'TextBox object Instance - 2
Private WithEvents Total As TextBox 'TextBox object Instance - 3
'---------------------------------------------------------------------
'Form Object's Property GET/SET Procedures
Public Property Get m_Frm() As Form
Set m_Frm = frm
End Property
Public Property Set m_Frm(ByRef mfrm As Form)
Set frm = mfrm
'After receiving Form Object, scan for TextBoxes on Form
'and Enable them with required Events.
'
Call class_Init
End Property
'---------------------------------------------------------------------
'Scan for TextBox Controls on Form
'and Enable the required Events
'
Private Sub class_Init()
Dim ctl As Control
Const EP = "[Event Procedure]"
For Each ctl In frm.Controls
Select Case ctl.Name
Case "Quantity"
'Assign Quantity TextBox on Form to 'Qty'
'Private Property of Class_3_1 Class Module
Set Qty = ctl
'Enable RaiseEvent
Qty.OnExit = EP
Qty.OnGotFocus = EP
Qty.OnLostFocus = EP
'------------------------------
Case "UnitPrice"
'Assign UnitPrice TextBox on Form to 'UPrice'
'Private Property of Class_3_1 Class Module
Set UPrice = ctl
'Enable RaiseEvent
UPrice.OnExit = EP
UPrice.OnGotFocus = EP
UPrice.OnLostFocus = EP
'------------------------------
Case "TotalPrice"
'Assign TotalPrice TextBox on Form to 'Total'
'Private Property of Class_3_1 Class Module
Set Total = ctl
'Enable RaiseEvent
Total.OnGotFocus = EP
Total.OnLostFocus = EP
End Select
Next
End Sub
'---------------------------------------------------------------------
'Qty.Exit() Qty.GotFocus() & Qty.LostFocus() Event Procedures
Private Sub Qty_Exit(Cancel As Integer)
Dim i As Integer, Msg As String
Dim info As Integer
i = Nz(Qty.Value, 0)
If i < 1 Or i > 10 Then
Msg = "Valid Value Range 1 - 10 Only."
info = vbCritical
Cancel = True 'I-bar to stay in the TextBox
Else
Msg = "Quantity: " & i & " Valid."
info = vbInformation
End If
MsgBox Msg, vbOK + info, "Quatity_Exit()"
End Sub
Private Sub Qty_GotFocus()
With Qty
.BackColor = &H20FFFF
.ForeColor = 0
End With
End Sub
Private Sub Qty_LostFocus()
On Error Resume Next
With Qty
.BackColor = &HFFFFFF
.ForeColor = 0
End With
End Sub
'---------------------------------------------------------------------
'UPrice.Exit() Qty.GotFocus() & UPrice.LostFocus() Event Procedures
Private Sub UPrice_Exit(Cancel As Integer)
Dim i As Single, Msg As String
Dim info As Integer
i = Nz(UPrice.Value, 0)
Msg = ""
If i <= 0 Then
Msg = "Enter a Value greater than Zero!"
info = vbCritical
Cancel = True 'I-bar to stay in the TextBox
End If
If Len(Msg) > 0 Then
MsgBox Msg, vbOK + info, "UnitPrice_Exit()"
Else
MsgBox "Unit Price: " & i, vbOK + vbInformation, "UPrice_Exit()"
End If
End Sub
Private Sub UPrice_GotFocus()
With UPrice
.BackColor = &H20FFFF
.ForeColor = 0
End With
End Sub
Private Sub UPrice_LostFocus()
With UPrice
.BackColor = &HFFFFFF
.ForeColor = 0
End With
End Sub
'---------------------------------------------------------------------
'Total.GotFocus() & Total.LostFocus() Event Procedures
Private Sub Total_GotFocus()
With Total
.BackColor = &H20FFFF
.ForeColor = 0
End With
frm!TotalPrice = Qty * UPrice
frm.TotalPrice.Locked = True
End Sub
Private Sub Total_LostFocus()
With Total
.BackColor = &HFFFFFF
.ForeColor = 0
End With
End Sub
Private Sub Class_Terminate()
Set Qty = Nothing
Set UPrice = Nothing
Set Total = Nothing
End Sub
'---------------------------------------------------------------------</pre><p>You can ignore/delete the dashed lines I put in between segments for better readability on this Page.</p>
<h3>Segment-wise Review of VBA Code.</h3>
<h4>Declaration of Objects and Get/Set Property Procedures of Form Object.</h4>
<p>Let us review the Class Module Code segment-wise to understand what they do. This VBA Code structure will undergo some major changes altogether later to make them easy to manage.</p>
<pre>Option Compare Database
Option Explicit
Private frm As Form
Private WithEvents Qty As TextBox 'TextBox object Instance - 1
Private WithEvents UPrice As TextBox 'TextBox object Instance - 2
Private WithEvents Total As TextBox 'TextBox object Instance - 3
'---------------------------------------------------------------------
'Form Object's Property GET/SET Procedures
Public Property <b>Get</b> m_Frm() As Form
Set m_Frm = frm
End Property
Public Property <b>Set</b> m_Frm(ByRef mfrm As Form)
Set frm = mfrm
'After receiving Form Object, scan for TextBoxes on Form
'and Enable them with required Event Procedures.
'
Call class_Init
End Property
'---------------------------------------------------------------------
</pre><p>In the Global declaration area, a Form object <b>Frm </b>is declared with Private Scope. That means this Form object cannot be accessed directly from outside the Class Module except through Property Procedures declared with Public Scope. We can pass the current Form Object <b>Me </b>to the <b>SET</b> <b>m_Frm()</b> Property Procedure through the <b>Form_Load()</b> Event Procedure. When we get the current Form instance in the <b>Frm</b> object in the Class3_1 Class Module we get access to all Controls or get their values from the Form. We can capture the Events fired (RaiseEvent) from the Controls on the Form, update values in the Textbox or access other properties of the Controls on the Form. </p><p>We will create a Form Object in every standalone Class Module we create in the demo of the '<i>Streamlining the Form Module VBA Code</i>' procedure. You can track how the current active Form Object is passed from the Form_Load() Event Procedure to the form object Instance in the standalone Class Module, through the <i>Set Property</i> Procedure parameter. You may keep an eye on its usage in the standalone Class Module (Class3_1) and in the Event Procedure Subroutines.</p>
<!-- Google In-Article Ads start -->
<script async="" src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle" data-ad-client="ca-pub-2376623373770548" data-ad-format="fluid" data-ad-layout="in-article" data-ad-slot="6147807460" style="display: block; text-align: center;"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
<!-- Google In-Article Ads END -->
<p>The next three TextBox Object declarations: <b>Qty</b>, <b>UPrice</b>, and <b>Total</b> will be assigned with the three TextBox object References on the Form Quantity, UnitPrice, and TotalPrice respectively. </p><p>Next, the <b>Set</b> Property Procedure will receive the Form Object into the <b>Frm</b> Property, through the Form_Load() Event Procedure through the <b>m_frm</b> Property Procedure. The <b>Get</b> Property Procedure will stand by for servicing the external request for the Form object when received.</p><p>The <b>Call Class_Init()</b> statement calls the Subroutine Class_Init().</p>
<p>The Class_Init() subroutine scans the Form for TextBox Objects, and when found their References are assigned to the three TextBox Control Instances with the names, Qty, UPrice, and Total we created in the Global declaration area.</p>
</div><h4>Scan the Form to find the Quantity, UnitPrice, and TotalPrice TextBoxes.</h4>
<pre>'Scan for TextBox Controls on Form
'and Enable the required Event Procedures
'
Private Sub class_Init()
Dim ctl As Control
Const EP = "[Event Procedure]"
For Each ctl In frm.Controls
Select Case ctl.Name
Case "Quantity"
'Assign Quantity TextBox on Form to 'Qty'
'Private Property of Class_3_1 Class Module
Set Qty = ctl
'Enable RaiseEvent
Qty.OnExit = EP
Qty.OnGotFocus = EP
Qty.OnLostFocus = EP
'------------------------------
Case "UnitPrice"
'Assign UnitPrice TextBox on Form to 'UPrice'
'Private Property of Class_3_1 Class Module
Set UPrice = ctl
'Enable RaiseEvent
UPrice.OnExit = EP
UPrice.OnGotFocus = EP
UPrice.OnLostFocus = EP
'------------------------------
Case "TotalPrice"
'Assign TotalPrice TextBox on Form to 'Total'
'Private Property of Class_3_1 Class Module
Set Total = ctl
'Enable RaiseEvent
Total.OnGotFocus = EP
Total.OnLostFocus = EP
End Select
Next
End Sub
'---------------------------------------------------------------------
</pre><p>Let us check the Code Segmentwise. We declared one general purpose Control Object <b>Ctl</b> and a Constant EP with the text "[Event Procedure]". In the next step the <b>For Each Ctl In Frm.Controls . . . Next</b> check through the controls on the Form one by one and read in the <b>Ctl</b> Control Variable. We are only interested in three TextBoxes with the name <b>Quantity</b>, <b>UnitPrice</b>, and <b>TotalPrice</b> on the Form. Other TextBoxes, if any, or any other controls on the Form are ignored. So, we need the <b>Select Case Ctl.Name</b> ... <b>Case ... End Select</b> Structure to test the name the <b>Ctl </b>Control represents in each cycle of the For . . . Next Loop to find the required one.</p><p>When the<b> Case "Quantity"</b> is found the Ctl-object reference is assigned to the <b>Qty</b> TextBox Instance (<b>Set Qty = Ctl</b>), declared in the global declaration area. The next three statements are enabled, by setting the constant EP ([Event Procedure]) text in all the three Event Procedures (OnExit, OnGotFocus, and OnLostFocus) of the Quantity Textbox on the Form.</p><p>This time we took the <b>OnExit</b> Event instead of <b>AfterUpdate</b>. The OnExit Event has some added advantages, if the value entered into the TextBox is not within the allowed range we can keep the I-bar in the same TextBox, after displaying an Error message, by executing the line <b>Cancel = True</b>, till the user enters a valid entry.</p><p>The UnitPrice Textbox object is assigned to the <b>UPrice</b> TextBox object instance and enables the same set of Event Procedures.</p><p>The TotalPrice TextBox Reference is assigned to the <b>Total</b> TextBox object instance and enables the OnGotFocus and OnLostFocus Events only.</p>
<h4>The Quantity TextBox Events</h4><h4 style="text-align: left;">The Qty_Exit, Qty_Gotfocus, and Qty_LostFocus Event Procedures.</h4>
<pre>'---------------------------------------------------------------------
Private Sub Qty_Exit(Cancel As Integer)
Dim i As Integer, Msg As String
Dim info As Integer
i = Nz(Qty.Value, 0)
If i < 1 Or i > 10 Then
Msg = "Valid Value Range 1 - 10 Only."
info = vbCritical
Cancel = True 'I-bar to stay in the TextBox
Else
Msg = "Quantity: " & i & " Valid."
info = vbInformation
End If
MsgBox Msg, vbOK + info, "Quatity_Exit()"
End Sub
Private Sub Qty_GotFocus()
With Qty
.BackColor = &H20FFFF
.ForeColor = 0
End With
End Sub
Private Sub Qty_LostFocus()
On Error Resume Next
With Qty
.BackColor = &HFFFFFF
.ForeColor = 0
End With
End Sub
'---------------------------------------------------------------------
</pre>
<p>In the Qty_Exit() Subroutine three Variables are declared. The <b>i = Nz(Qty.Value,0)</b> statement reads the Quantity value from the Qty TextBox object instance. In the next step, it checks whether the received value is within the valid range of 1 to 10, if not the <b>Msg</b> and <b>Info</b> String Variables are assigned with Message Text for the MsgBox. The <b>Cancel = True</b> ensures that the I-Bar stays in the Quantity TextBox till a valid value is entered.</p><p>Next, the GotFocus Event Procedure changes the background Color and LostFocus resets the TextBox Color to its default value.</p>
<p>The <b>UnitPrice</b> TextBox goes through the same process.</p>
<pre> '---------------------------------------------------------------------
Private Sub UPrice_Exit(Cancel As Integer)
Dim i As Single, Msg As String
Dim info As Integer
i = Nz(UPrice.Value, 0)
Msg = ""
If i <= 0 Then
Msg = "Enter a Value greater than Zero!"
info = vbCritical
Cancel = True 'I-bar to stay in the TextBox
End If
If Len(Msg) > 0 Then
MsgBox Msg, vbOK + info, "UnitPrice_Exit()"
Else
MsgBox "Unit Price: " & i, vbOK + vbInformation, "UPrice_Exit()"
End If
End Sub
Private Sub UPrice_GotFocus()
With UPrice
.BackColor = &H20FFFF
.ForeColor = 0
End With
End Sub
Private Sub UPrice_LostFocus()
With UPrice
.BackColor = &HFFFFFF
.ForeColor = 0
End With
End Sub
'---------------------------------------------------------------------
</pre>
<h4>The TotalPrice TextBox Event Procedures.</h4>
<pre>'---------------------------------------------------------------------
Private Sub Total_GotFocus()
With Total
.BackColor = &H20FFFF
.ForeColor = 0
End With
frm!TotalPrice = Qty * UPrice
frm.TotalPrice.Locked = True
End Sub
Private Sub Total_LostFocus()
With Total
.BackColor = &HFFFFFF
.ForeColor = 0
End With
End Sub
</pre>
<p>The <b>TotalPrice</b> TextBox is enabled with the GotFocus and LostFocus Events only. The OnGotFocus Event Procedure will change the background Color. Next, the calculated value <b>Qty * UPrice</b> is updated in the TotalPrice TextBox. Immediately after updating the value the TextBox is locked by executing the statement <b>frm.TotalPrice.Locked = True</b><i>.</i> The OnLostFocus Event resets to its earlier background Color.</p><p>We could write the full Event Procedure Code in the standalone Class Module, except the Form_Load() Event Procedure, which works the way we planned. This is the first time we are implementing Event Procedure Code for more than one Textbox in the standalone Class Module and it works the way we wanted it now. With only three TextBoxes and three Events for each TextBox.</p>
<pre>Private Sub Class_Terminate()
Set Qty = Nothing
Set UPrice = Nothing
Set Total = Nothing
End Sub</pre>
<p> The <b>Sub Class_Terminate () </b>is a very special Subroutine that executes automatically when the Class Module Class3_1 is in the closing phase and is used for clearing the memory occupied by the Objects.</p><p>There is another Subroutine with the name <b>Sub Class_Initialize(), </b>if present in the Class Module,<b> </b>that also executes automatically, when the Class Module is loaded into memory, to instantiate some other Class Object or any other object like Collection, Dictionary in memory. </p>
<p>But, in totality it is crowded with Code in the Class3_1 Class Module, defining TextBox instances, Enabling Event Procedures, writing Event Procedure codes, and all these actions stuffed into a single Standalone Class Module. We need to be better organized for handling more than three Instances of the TextBoxes with ease. </p><p>For the Quantity TextBox, there were three Events OnExit, OnGotFocus, and OnLostFocus. All three Event Procedures are written in the Class3_1 Module. The UnitPrice TextBox has 3 Events, and TotalPrice 2 Events. Total 8 separate Event Procedures as we normally write in the Form Class Module. Okay, all of them are taken out from the Form Module and put in the Standalone class module. But, if we have 15 Textboxes on the Form having 3 Event Procedures each then what we will do? We will write all 45 Event Procedures on the Form Module, or we will transfer them to the standalone Class Module.</p><p>We must find a better way to organize the VBA Code of several other Control types on the Form. We are now considering only the TextBox objects. What about other Controls, like Command Buttons, combo boxes, and others? </p><p>But First, we will concentrate on refining our current Coding approach for our TextBox Objects. </p><p>We will make a detailed study of this aspect next week. When we are familiar with that procedure then we can handle other controls on the Form very easily.</p><h4>Demo Database Download Link:</h4>
<!--Download Link /downloads/2023/06/Streamline5.zip-->
<div class="separator" style="clear: both; text-align: center;">
<a href="https://drive.google.com/file/d/1_so-HJWcwJ2BRTi1nKlynzquvDmg_Y1a/view?usp=sharing">
<img border="0" data-original-height="29" data-original-width="22" height="29" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFE4uDLH9dk3S6pEcfTkT6GW6r11XqBI4HQnLnhQFZMKFeeQt5ABV_NqvLHM68J7RacISsvbUEl0PlAq6V7i0t0mQF3iesrBuqGuvrf5CJV72OzrGZ7rdMxkxzw8FYlz-qniNySoqtqUN4/s320/zip.gif" width="22" />Streamline5.zip</a>
</div>
<!--Download Link /downloads/2023/06/Streamline5.zip-->
<h3>Streamlining Form Module Code in Standalone Class Module.</h3>
<ol>
<li><a href="https://www.msaccesstips.com/2023/04/reusing-form-module-vba-code-for-new.html" target="_Blank">Re-using Form Module VBA Code for New Projects.</a></li>
<li><a href="https://www.msaccesstips.com/2023/04/streamlining-form-module-code-part-two.html" target="_Blank">Streamlining Form Module Code Part-Two</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/05/streamlining-form-module-code-part-three.html" target="_Blank">Streamlining Form Module Code Part-Three</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/06/streamlining-form-module-code-part-four.html" target="_Blank">Streamlining Form Module Code Part-Four</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/06/streamlining-form-module-code-part-five.html" target="_Blank">Streamlining Form Module Code Part-Five</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-six.html" target="_Blank">Streamlining Form Module Code Part-Six</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-seven.html" target="_Blank">Streamlining Form Module Code Part-Seven</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-eight.html" target="_Blank">Streamlining Form Module Code Part-Eight</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part-nine.html" target="_Blank">Streamlining Form Module Code Part-Nine</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part-ten.html" target="_Blank">Streamlining Form Module Code Part-Ten</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part.html" target="_Blank">Streamlining Form Module Code Part-Eleven</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-report-module-code-in.html" target="_Blank">Streamlining Report Module Code in Class Module</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-report-module-in-class.html" target="_Blank">Streamlining Report Module Code in Class Module-2</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-form-module-code-part-14.html" target="_Blank">Streamlining Form Module Code Part-14:All Controls</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-custom-made-form-wizard.html" target="_Blank">Streamlining Custom Made Form Wizard-15</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-form-vba-custom-report.html" target="_Blank">Streamlining Custom Report Wizard-16</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-form-vba-external-files.html" target="_Blank">Streamlining Form VBA External File Browser-17</a></li>
<li><a href="https://www.msaccesstips.com/2023/11/streamlining-event-procedures.html" target="_Blank">Streamlining Event Procedures of 3D TextWizard-18</a></li>
<li><a href="https://www.msaccesstips.com/2023/11/streamlining-event-procedures-rgbcolor.html" target="_Blank">Streamlining Event Procedures RGB Color Wizard-19</a></li>
<li><a href="https://www.msaccesstips.com/2023/12/streamlining-numbers-to-words-converter.html" target="_Blank">Streamlining Event Procedures Number To Words-20</a></li>
</ol>
a.p.r. pillaihttp://www.blogger.com/profile/02627590161919120214noreply@blogger.com0tag:blogger.com,1999:blog-7457417942281874810.post-70141306081730510752023-06-08T22:38:00.032+05:302024-01-07T22:47:51.582+05:30Streamlining Form Module Code Part Four<h4 style="text-align: left;"> Continued from the earlier episodes on Streamlining Form Module VBA Code Part Three.</h4><p>Links to the earlier Episodes of the above topic are given below for Reference:</p>
<ol>
<li><a href="https://www.msaccesstips.com/2023/04/reusing-form-module-vba-code-for-new.html" target="_Blank">Re-using Form Module VBA Coding for New Projects.</a></li>
<li><a href="https://www.msaccesstips.com/2023/04/streamlining-form-module-code-part-two.html" target="_Blank">Streamlining Form Module Coding Part-Two</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/05/streamlining-form-module-code-part-three.html" target="_Blank">Streamlining Form Module Coding Part-Three</a>.</li>
</ol>
<h3>A Quick Look at Last Week's Session.</h3>
<p>A quick look at how the Quantity TextBox object Events were enabled (RaiseEvent) by selecting the "[Event Procedure]" Option from the drop-down list in the Event Property of TextBox. When the Events are fired in the TextBox object instance, they are captured in the Form Module itself and the Event Procedure is executed, as depicted in the graphical image given below. </p><p>When the Quantity TextBox Object's Events are fired on the Form those Events can be captured in the Class1 standalone Class Module also. For doing that we must define an Instance of the TextBox Object and qualify it with the keyword <i>WithEvents</i> in the Class Module. Then the Quantity TextBox object Reference from the Form is assigned to the new object instance. In this way when the Events of the TextBox are fired in the Form Module they can be captured in the Class1 Class Module also. The same Event Procedures, which we normally write in the Form Module, where the Quantity TextBox Instance resides, can be written in the Class1 Class Module. The Graphic image of these actions is shown below for reference. </p>
<!-- Google In-Article Ads start -->
<script async="" src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle" data-ad-client="ca-pub-2376623373770548" data-ad-format="fluid" data-ad-layout="in-article" data-ad-slot="6147807460" style="display: block; text-align: center;"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
<!-- Google In-Article Ads END -->
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhSTYKj5vW36C4tue1SpJzBSNYZYZkZ63Dupym8KnFXkgXVhYDtQOZOaBM7VRuOvjajWNrKhmtXK14G7XMF9DMoxMpIP0Jd8StyUaaHkx043FaukppansQkIlJ1K28KFvSp-96CuVOuV9KjBlfbSIEce-q4xvEzJZcuzIgTTKkULRA4vd0tT69iR2iQig/s891/EmptyEventStub310.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="749" data-original-width="891" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhSTYKj5vW36C4tue1SpJzBSNYZYZkZ63Dupym8KnFXkgXVhYDtQOZOaBM7VRuOvjajWNrKhmtXK14G7XMF9DMoxMpIP0Jd8StyUaaHkx043FaukppansQkIlJ1K28KFvSp-96CuVOuV9KjBlfbSIEce-q4xvEzJZcuzIgTTKkULRA4vd0tT69iR2iQig/s320/EmptyEventStub310.png" width="320" /></a></div>
<p>We want the AfterUpdate Event and others to fire from the Quantity Textbox in the Form to check the validity of the value entered into the TextBox to ensure that the criteria range of values between 1 and 10, both included is met. The GotFocus Event changes the background color of the active TextBox. The LostFocus Event ensures that the background color is reset to its earlier color. The "[Event Procedure]" option selected in all three Event Properties of the Textbox will ensure that these Events will fire (RaiseEvent) at the appropriate Time.</p><p>When the Events are fired the Event Procedure Code we write within the empty program stub is executed. But, in our case, we would like to redirect these Events to a stand-alone Class Module-based Event Procedure. Why, because we want the Form for User Interface designing only and take away the Coding task into separate Class Module(s) for ease of Coding and Maintenance.</p><p>To achieve that we must devise a new procedure for the Coding task, which is what we plan to do. VBA beginners may find it difficult to understand the concept fully. The full story of this concept cannot be explained in a few pages, which is why this task has been taken in the form of a Tutoring Session so that each level of concept and practical implementation can be explained in detail.</p><h3 style="text-align: left;">The new <i>RaiseEvent</i> Procedure.</h3><p>We can set, the "[Event Procedure]" option in real-time, rather than manually setting, in the AfterUpdate, OnGotFocus, and OnLostFocus, and others in the Event Property for invoking the RaiseEvent action to happen in the Object. To try out that, we have created a new Form (with the name: Form1_RaiseEvent_2), by copying the earlier Form1 and renaming it <b>Form1_RaiseEvent_2</b>, to implement the new method in its Class Module.</p><div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiahChuYGKX-StcK96ElMSDsv5XnMcWLTp892j3gnvMEzq8VN-YgHdfxwn-ABB1pB0tRmwbp0k-TOjvbx7Yn77BzvBmNa8ThwtYAfjezOuJ-0G_-3yFf4h5me6qhpE2gd1nOIhJtRTxZ41nuLEeyaZi13yzQZZR_Ok30DmMollWxhc6-VI8EFvUvjNv6Q/s604/QuantityRaiseEvent2.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="553" data-original-width="604" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiahChuYGKX-StcK96ElMSDsv5XnMcWLTp892j3gnvMEzq8VN-YgHdfxwn-ABB1pB0tRmwbp0k-TOjvbx7Yn77BzvBmNa8ThwtYAfjezOuJ-0G_-3yFf4h5me6qhpE2gd1nOIhJtRTxZ41nuLEeyaZi13yzQZZR_Ok30DmMollWxhc6-VI8EFvUvjNv6Q/s320/QuantityRaiseEvent2.png" width="320" /></a></div><p>Make a copy of Form1 as explained above. Open the new Form in Design View and change the Detail Section background color then display its VBA Module. Copy the following Codes and Paste them over the existing Code to replace them.</p><pre>Option Compare Database
Option Explicit
'Decare an Object Variable C as Type Class1
Private C As Class1
Private Sub Form_Load()
'Instantiate Class1 Class Object
Set C = New Class1
'Assign Quantity TextBox Object to C.Txt Property
Set C.Txt = Me.Quantity
'Enable Event Procedures of Quantity TextBox
'This method replaces the empty
'Event Procedure stub used earlier.
Me.Quantity.AfterUpdate = "[Event Procedure]"
Me.Quantity.OnGotFocus = "[Event Procedure]"
Me.Quantity.OnLostFocus = "[Event Procedure]"
End Sub
Private Sub Form_Unload(Cancel As Integer)
'Release Class1 Object instance C from memory
Set C = Nothing
End Sub</pre><h3 style="text-align: left;">VBA Code Change Review.</h3><p>As you can see at the end of the Form_Load() Event Procedure we have added three lines of Code to set the "[Event Procedure]" option, in real-time, in their respective Event Property, when the Form is loaded into memory, and will keep it there till the Form is Closed. This approach will eliminate the need for keeping an empty Event Procedure stub on the Form Module to trigger the RaiseEvent Action. </p><p>Save and Close the Form. Open it in Normal View. Try entering a value greater than 10 to test whether the AfterUpdate Event fires and an invalid Quantity message is displayed or not.</p><p>Now, try entering a valid value within the range of 1 to 10. All of these tests will work normally as before.</p><p>But, again we have made all of these changes on the Form Module itself to capture the action in the Class1 Class Module-based TextBox object instance. Our main targeted plan is to remove the VBA Coding from the Form Module and capture them into the stand-alone Class Module and do all the coding there, leaving the Form for User Interface design only, except for a few lines of essential Code is unavoidable. </p>
<!-- Google In-Article Ads start -->
<script async="" src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle" data-ad-client="ca-pub-2376623373770548" data-ad-format="fluid" data-ad-layout="in-article" data-ad-slot="6147807460" style="display: block; text-align: center;"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
<!-- Google In-Article Ads END -->
<h3 style="text-align: left;">The real-time RaiseEvent is enabled in the Class1 Module.</h3><p>Now, we understand the need for the real-time Event enabling Property option setting on the Form and have successfully done that with VBA Code. Let us see how we can implement this method in the stand-alone Class Module rather than Coding in the Form Module.</p><p>Make a copy of the Class1 Module and rename it as <b>Class2.</b> We will preserve the Code in the Class1 Class Module to refer them to see what changes we made in the new Copy. Make a copy of the earlier <b>Form1_RaiseEvent_2</b> Form and rename it as <b>Form1_RaiseEvent_3.</b></p>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjlR2I0YwuctWYGCd0nbCLQPeqpsgrnqjdB_8697bVWGzB-P32olCGDZn48lzN05IkHoNNBpDGXjFFgkhStGuc6elDhNaacTC8ngovMXW7sgr8VFpWiSdROEs1hFOdRMi1d4fy8smEBeUYmoAEXSJH7V62875wbc2erXC1x6eRPkdjczxcGIPX_sW-UdQ/s495/QuantityRaiseEvent3.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="445" data-original-width="495" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjlR2I0YwuctWYGCd0nbCLQPeqpsgrnqjdB_8697bVWGzB-P32olCGDZn48lzN05IkHoNNBpDGXjFFgkhStGuc6elDhNaacTC8ngovMXW7sgr8VFpWiSdROEs1hFOdRMi1d4fy8smEBeUYmoAEXSJH7V62875wbc2erXC1x6eRPkdjczxcGIPX_sW-UdQ/s320/QuantityRaiseEvent3.png" width="320" /></a></div>
<h3 style="text-align: left;">New Form Module Code Change.</h3><p>Open the new Form and open its VBA Module. Copy the following Codes and Paste them over the existing Form Module Code.</p>
<pre>Option Compare Database
Option Explicit</pre><pre>Private C As Class2
Private Sub Form_Load()
Set C = New Class2
Set C.m_Frm = Me 'What is m_Frm member in Class2 Module?
End Sub
Private Sub Form_Unload(Cancel As Integer)
Set C = Nothing
End Sub</pre><p>Save the Form with the new Code. </p><p>Now that we have removed the Me.Quantity.AfterUpdate = "[Event Procedure]" Event enabling lines from the Form Module and these will be run from the new Class2 Class Module. This will retain the earlier Class1 Class Module Code and you can compare what kind of transformation took place in the Class2 Class Module.</p><p>The second line in the Form_Load() Event Procedure is new and needs some explaining. As you can see on the left side of the = symbol the Class2 Class Module object C has a Property Procedure with the name <b>m_Frm</b> that demands a Form Object to be passed to it as a parameter. </p><p>On the right side of the = symbol, the Form Object Me is passed to the <b>C.m_Frm</b> property Procedure of the Class2 Class Module. It simply means that we are passing our Form Object to a Property of the same type in the Class2 Class Module. In earlier examples we pass the current Form Object directly to the Form Object Property declared in the Class1 Class Module with Public scope. In the new Class2 Class Module, we declared the <b>Frm</b> object with Private Scope. Form Object assignment to the Frm Property we routed it through the Public Property Procedure so that external Programs cannot change the Frm Property value and will remain safe. It will be clear when we see the new VBA Code of the Class2 Class Module.</p><h3 style="text-align: left;">Class2 Class Module Code Change.</h3><p>Open Class2 Class Module (normally the Class Modules will be given some meaningful name, instead of Class1, Class2). Copy the following VBA Code and Paste them overwriting the existing Code in the Module:</p><pre>Option Compare Database
Option Explicit
Private Frm As Form
Private WithEvents Txt As TextBox
Public Property Get m_Frm() As Form 'GET Property Procedure
Set m_Frm = Frm
End Property
Public Property Set m_Frm(ByVal mFrm As Form) 'SET Property Procedure
If Left(TypeName(mFrm), 4) = "Form" Then
Set Frm = mFrm
Else
MsgBox "Invalid Object " & mFrm & " passed as Form!"
Exit Property
End If
Call Class_Init 'Run Class_Init() Subroutine
End Property
Private Sub Class_Init()
Set Txt = frm.Quantity
Txt.AfterUpdate = "[Event Procedure]"
Txt.OnGotFocus = "[Event Procedure]"
Txt.OnLostFocus = "[Event Procedure]"
End Sub
Private Sub txt_AfterUpdate()
Dim i As Integer, msg As String
Dim info As Integer
i = Nz(Txt.Value, 0)
If i < 1 Or i > 10 Then
msg = "Valid Value Range 1 - 10 Only."
info = vbCritical
Else
msg = "Quantity: " & i & " Valid."
info = vbInformation
End If
MsgBox msg, vbOK + info, "txt_AfterUpdate()"
End Sub
Private Sub txt_GotFocus()
With Txt
.backcolor = &H20FFFF
.forecolor = 0
End With
End Sub
Private Sub txt_LostFocus()
With Txt
.backcolor = &HFFFFFF
.forecolor = 0
End With
End Sub
</pre><p>Let us check the Code segment-wise to understand them. We are already familiar with the AfterUpdate and other Event Procedures.</p><pre>Private Frm As Form
Private WithEvents Txt As TextBox</pre><p>We declared two object Properties in the global declaration area of the Class Module: <b>Frm</b> the Form object Property and the earlier Property <b>Txt</b> the TextBox Object. Both are declared with Private scope here rather than with the Public scope, which we preferred in earlier examples.
As I stated earlier the Public scope declaration is not advisable for Class Object Properties because their values can be changed by external programs.</p><p>In this case, we should route the Form Object reference through the Property Procedure, as shown in the Code segment given below and we can check the validity of the data passed to the Property Procedure, before assigning it to the Property.</p>
<pre>Public Property Get m_Frm() As Form
Set m_Frm = Frm
End Property
Public Property Set m_Frm(ByRef mFrm As Form)
If Left(TypeName(mFrm), 4) = "Form" Then
Set Frm = mFrm
Else
MsgBox "Invalid Object " & mFrm & " passed to Form"
Exit Property
End If
Call Class_Init 'Run Class_Init() Subroutine
End Property</pre>
<p>We are receiving a Form Object in the <b>mFrm</b> Parameter in the <i>Public Property <b>Set</b> m_Frm()</i> Property Procedure. We check the received object Type by using the TypeName() Function and extracting the first four characters of the object name <b>Form_Form1_RaiseEvent_3</b>. If the first Four Characters match the text 'Form' then it is the correct object for the Frm Property declared in the Global area. The <b>Set Frm = mFrm</b> assigns the Form object to the Property <b>Frm. </b> </p><p>When we want the Form reference in the <b>Frm</b> Property we call for the <i>Public Property <b>Get</b> m_frm()</i> Property Procedure, like <i><b>Debug.Print m_Frm.Name</b>. </i> But, from within the Class2 Class Module, we can refer to the <b>Frm</b> Property directly like <i><b>Debug.Print Frm.Name</b></i></p><p><b>The SET and GET Property Procedures.</b></p>
<p>Both the GET and SET Property Procedures must have the same name <b>m_Frm,</b> or any suitable name you prefer, and its Data Type is shown as Form Object. If you remember our Form Module entry: <i>Set C.m_Frm = Me</i> passes the Form Object Me as a parameter to the Set Property Procedure in Class2 Class Module. In this Property Set Procedure, we perform a validation check to ensure that the parameter value is a Form Object and then assign it to the <b>Frm</b> Property. The Set Key word is used for Objects only, and the LET keyword is used for other Property Types, like String, Integer, and others.</p>
<p><b>Note:</b> To know more about Property Procedures and how they work in the Class Module visit an earlier Page: <a href="https://www.msaccesstips.com/2018/10/ms-access-class-module-and-vba.html" target="_blank">MS Access Class Module And VBA</a>
</p><p>Once we get the Form Object reference in our Class Module we can easily address the controls on the Form and get their references in the Class Module to work with them. We need the reference of the TextBox with the name <i>Quantity</i> to enable its Events and validate the value entered into the Textbox, by running the Event Procedures in the Class Module. For these actions, we have created a separate Subroutine called <b>Class_Init()</b></p><p>The Class_Init() Subroutine is called from the <i>Property Set m_Frm()</i> Procedure, immediately after assigning the Form (<b>Form_Form1_RaiseEvent_3) </b>reference to the Global Property <b>Frm. </b></p><pre>Private Sub Class_Init()
Set Txt = Frm.Quantity
Txt.AfterUpdate = "[Event Procedure]"
Txt.OnGotFocus = "[Event Procedure]"
Txt.OnLostFocus = "[Event Procedure]"
End Sub</pre>
<p>In this procedure, we first assigned the Quantity TextBox reference to the T<b>xt </b>TextBox Object Instance declared in the global declaration area of the Class3 Class Module. Check how we address the Quantity Textbox on the Form from Class3 Class Module (Frm.Quantity like Me.Quantity). If you would like to assign some value to the Quantity TextBox on the Form you can do that with the statement Frm!Quantity = 25, from the Class Module.</p><p>The next three statements enable the AfterUpdate, OnGotFocus, and OnLostFocus Event Procedures by setting the "[Event Procedure]" Option and setting them dynamically when the Form is Open. Earlier this procedure executed on the Form Module itself is now transferred to the Class Module.</p><p>Did you notice the change in the Event Procedure Names? In the Form Module, the AfterUpdate Event Procedure name is written as Private Sub Quantity_AfterUpdate(), the Textbox name as a name prefix follows the Event name, and both these elements are joined with an underscore character. </p><p>In the Class Module, the TextBox object instance declaration name Txt becomes the Event Procedure name prefix: Private Sub Txt_AfterUpdate()</p><p>Instead of repeating the text "[Event Procedure]" we can define this as a Constant and use a simple name instead, like:</p>
<pre style="text-align: left;">Private Sub Class_Init()
Const EP = "[Event Procedure]"
Set Txt = frm.Quantity
Txt.AfterUpdate = EP
Txt.OnGotFocus = EP
Txt.OnLostFocus = EP
End Sub
</pre><h3 style="text-align: left;"><b>Loading Standalone Class Module in Memory.</b></h3><p><b>Note:</b> One important point you should know about is that the standalone Class Module Object cannot load itself into memory like the Form or Report-based Class Modules. </p><p>We have to instantiate the Class Module Object, in any other Module that will be active in memory, like Form Module, or in another stand-alone Class Module that loads into memory first, to use its Properties, Methods, and Events. We already did this in the Form_Load() Event Procedure of the Form, with the following declarations:</p><pre>Option Compare Database
Option Explicit
Private C As Class2
Private Sub Form_Load()
Set C = New Class2
Set C.m_Frm = Me 'What is m_Frm member in Class2 Module?
End Sub</pre><p>Coming back to the remaining Code in the Class2 Class Module we are familiar with the Event Procedure Code given below used to validate the Quantity entered into its TextBox on Form.</p>
<pre>Private Sub txt_AfterUpdate()
Dim i As Integer, msg As String
Dim info As Integer
i = Nz(Txt.Value, 0)
If i < 1 Or i > 10 Then
msg = "Valid Value Range 1 - 10 Only."
info = vbCritical
Else
msg = "Quantity: " & i & " Valid."
info = vbInformation
End If
MsgBox msg, vbOK + info, "txt_AfterUpdate()"
End Sub
Private Sub txt_GotFocus()
With Txt
.backcolor = &H20FFFF
.forecolor = 0
End With
End Sub
Private Sub txt_LostFocus()
With Txt
.backcolor = &HFFFFFF
.forecolor = 0
End With
End Sub</pre><p>There is no change in the above Event Procedure Code for the time being. So far we have played around with a single TextBox object on the Form. If you could follow the concept so far then that would be very helpful to go further deep into this concept and to learn more tricks.</p><p>Open Form1_RaiseEvent_3 in normal view and try it out by entering a value greater than 10 first, then retry with a new value from the range 1 to 10 to ensure that all three Event Procedures work as planned. </p><h4 style="text-align: left;">Event Procedure of all Controls on Form in Standalone Class Module.</h4><p>If you are getting impatient, bored with the details and explaining, and wondering what this is all about, I will give a sample of the Final Product. Open the Link: <a href="https://www.msaccesstips.com/2019/08/withevents-and-all-form-control-types.html" target="_blank">WithEvents and all Form Control Types</a> for a sample demo of the final product we plan to implement.</p><p>At the end of the Page, you will find a Demo Database Download Link. Download the Demo Database and open it. Open the <b>frmControls_All</b> Form in Design View and then display its Form Class Module. You will find only three or four lines of Code. Open the Form in Normal View, enter some Data in TextBoxes, and select options from ComboBox, ListBox, and other controls on the Form. You will find responses either by displaying the data you entered in the Message Box or on the colored Help text display Label control at the bottom of the Form, indicating that the Events in the Form are tracked and responded to by the Class Module objects in memory. This is only a sample database demonstrating how the Events of the Form-based controls are captured in the Class Module and display messages.</p><h4>Demo Database Download</h4>
<!--Download Link /downloads/2023/06/Streamline4.zip-->
<div class="separator" style="clear: both; text-align: center;">
<a href="https://drive.google.com/file/d/1nLB-bBLPs92kQ47eKuZy3x9C0mdEDNRq/view?usp=sharing">
<img border="0" data-original-height="29" data-original-width="22" height="29" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFE4uDLH9dk3S6pEcfTkT6GW6r11XqBI4HQnLnhQFZMKFeeQt5ABV_NqvLHM68J7RacISsvbUEl0PlAq6V7i0t0mQF3iesrBuqGuvrf5CJV72OzrGZ7rdMxkxzw8FYlz-qniNySoqtqUN4/s320/zip.gif" width="22" />Streamline4.zip</a>
</div>
<!--Download Link /downloads/2023/06/Streamline4.zip-->
<h3>Streamlining Form Module Code in Standalone Class Module.</h3>
<ol>
<li><a href="https://www.msaccesstips.com/2023/04/reusing-form-module-vba-code-for-new.html" target="_Blank">Re-using Form Module VBA Code for New Projects.</a></li>
<li><a href="https://www.msaccesstips.com/2023/04/streamlining-form-module-code-part-two.html" target="_Blank">Streamlining Form Module Code Part-Two</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/05/streamlining-form-module-code-part-three.html" target="_Blank">Streamlining Form Module Code Part-Three</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/06/streamlining-form-module-code-part-four.html" target="_Blank">Streamlining Form Module Code Part-Four</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/06/streamlining-form-module-code-part-five.html" target="_Blank">Streamlining Form Module Code Part-Five</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-six.html" target="_Blank">Streamlining Form Module Code Part-Six</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-seven.html" target="_Blank">Streamlining Form Module Code Part-Seven</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-eight.html" target="_Blank">Streamlining Form Module Code Part-Eight</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part-nine.html" target="_Blank">Streamlining Form Module Code Part-Nine</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part-ten.html" target="_Blank">Streamlining Form Module Code Part-Ten</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part.html" target="_Blank">Streamlining Form Module Code Part-Eleven</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-report-module-code-in.html" target="_Blank">Streamlining Report Module Code in Class Module</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-report-module-in-class.html" target="_Blank">Streamlining Report Module Code in Class Module-2</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-form-module-code-part-14.html" target="_Blank">Streamlining Form Module Code Part-14:All Controls</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-custom-made-form-wizard.html" target="_Blank">Streamlining Custom Made Form Wizard-15</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-form-vba-custom-report.html" target="_Blank">Streamlining Custom Report Wizard-16</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-form-vba-external-files.html" target="_Blank">Streamlining Form VBA External File Browser-17</a></li>
<li><a href="https://www.msaccesstips.com/2023/11/streamlining-event-procedures.html" target="_Blank">Streamlining Event Procedures of 3D TextWizard-18</a></li>
<li><a href="https://www.msaccesstips.com/2023/11/streamlining-event-procedures-rgbcolor.html" target="_Blank">Streamlining Event Procedures RGB Color Wizard-19</a></li>
<li><a href="https://www.msaccesstips.com/2023/12/streamlining-numbers-to-words-converter.html" target="_Blank">Streamlining Event Procedures Number To Words-20</a></li>
</ol>
a.p.r. pillaihttp://www.blogger.com/profile/02627590161919120214noreply@blogger.com0tag:blogger.com,1999:blog-7457417942281874810.post-71234342007347642852023-05-12T15:40:00.038+05:302024-03-16T22:56:28.581+05:30Streamlining Form Module Code Part Three<h3 style="text-align: left;">Introduction. </h3><p>This Article is the continuation of the preceding articles addressing the topic of Streamlining Form Module Code. Assuming familiarity with the previously discussed title topic, "<a href="https://www.msaccesstips.com/2023/04/streamlining-form-module-code-part-two.html?m=1" target="_blank">Streamlining Form Module Code,</a>" readers are expected to have a foundational understanding of our trajectory. Specifically, we have delved into the concepts of Event, RaiseEvent, and <a href="https://www.msaccesstips.com/2019/04/withevents-button-combo-list-textbox-tab.html?m=1" target="_blank">WithEvents</a> declarations, elucidating their interplay and collective functionality.</p><p>The singular objective is to transition VBA coding from the Form Module to a stand-alone Class Module, reserving the Form solely for user interface design purposes. Is this feasible? Absolutely. Our intention is to demonstrate this feasibility through the forthcoming pages. Furthermore, upon implementation of the new VBA coding procedure, segments of the work can be seamlessly transferred to new projects, facilitating coding endeavours therein.</p>
<!--Google In-Article Ads start-->
<script async="" src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle" data-ad-client="ca-pub-2376623373770548" data-ad-format="fluid" data-ad-layout="in-article" data-ad-slot="6147807460" style="display: block; text-align: center;"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
<!--Google In-Article Ads END-->
<p>With this new method of Coding procedure, we will be able to open the Code Modules independently and work without struggling with the Form Design every time to reach the Code, saving a good deal of manual effort. This new approach in Coding will serve the purpose of faster completion of Projects. The procedure that we plan to devise will work together with Objects on the Form as they do normally & efficiently as far as the end user is concerned. Besides that, the backbone of this procedure can be exported into other Projects for ease of Coding there.</p><p>But, first I think it is interesting to learn how we are made to do the Coding always on the Form Module. Once we know that we can implement the procedure the way we plan to do it and make it useful in other Projects also.</p><p>For achieving that it needs to lay down a proper procedure so that Access Developers understands them and use them routinely. Before doing that, we should know how the existing method of coding works on the Form Module, and what tricks are kept hidden behind the <a href="https://www.msaccesstips.com/2013/10/form-footer-section-dropping-down-magic.html" target="_blank">Form</a> to make us do the coding in the Form Module itself.</p><h4>The earlier Article Links:</h4><ol>
<li><a href="https://www.msaccesstips.com/2023/04/reusing-form-module-vba-code-for-new.html" target="_Blank">Re-using Form Module VBA Coding for New Projects.</a></li>
<li><a href="https://www.msaccesstips.com/2023/04/streamlining-form-module-code-part-two.html" target="_Blank">Streamlining Form Module Coding Part-Two</a>.</li>
</ol>
<p>Do you have any idea as to how and why we are asked to write the <a href="https://www.msaccesstips.com/2015/10/runsql-action-in-macro-and-vba.html" target="_blank">VBA</a> Code on the Form Module itself for Event Procedures? How the <i>Events</i> are Raised and the Code is executed when an Event occurs. It is interesting to explore and find out these aspects of Coding. Once we know the inner workings of this trick and a few basic rules that go with it, then we can think of refining the existing procedure and doing it differently. </p><p>What happens when we add a control to the <a href="https://www.msaccesstips.com/2015/12/microsoft-access-form-move-size-action.html " target="_blank">Form</a>? How does the code we write in the Event Procedure get executed when an event occurs? Is there any other alternative approach that can give us the same result and ease of Coding?</p><h3>Objects And Their Builtin Events.</h3><p>Let us study what happens when you add a Textbox Object to the Form. The diagram of a Textbox on the Form and all things associated with it, which we should know about are shown below.</p><div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1k8U2aIBszDwzfd4a9_lhNvQqeMmbNYjGx64aya0uew_zlP2fmNE0M8V5JlWj5mmNtkQf2DBB7m2tR5e1FJpxk8_UczVMNEwzgvFBvjJ5Gez5QdKlvGsMe1pW-5M3EL1QdZo1A--ON8XNesZZ4lX1HctiqS1bNnePFOQC4Gz1qVTXpyNLb1lyOmsT_g/s925/TextBoxEvents.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="565" data-original-width="925" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1k8U2aIBszDwzfd4a9_lhNvQqeMmbNYjGx64aya0uew_zlP2fmNE0M8V5JlWj5mmNtkQf2DBB7m2tR5e1FJpxk8_UczVMNEwzgvFBvjJ5Gez5QdKlvGsMe1pW-5M3EL1QdZo1A--ON8XNesZZ4lX1HctiqS1bNnePFOQC4Gz1qVTXpyNLb1lyOmsT_g/s320/TextBoxEvents.png" width="320" /></a></div><p>As shown in the above diagram, we have added a Textbox Control in the Detail Section of the Form. To be more specific and technically correct to say that we have added a Textbox object <i>Instance</i> in the Form. </p><p>We normally create several instances of MS Access <a href="https://www.msaccesstips.com/2008/10/textbox-and-label-inner-margins.html" target="_blank">Textbox</a> objects on the Form. Every time we add a Textbox object instance to the Form, the Access System redefines the <i>Name </i>Property of the Textbox Object as <b>WithEvents <i>Text0</i> As TextBox</b> as shown in the diagram above, the Label with a dark background and in grey letters. We can change the <b>Name</b> Property value Text0, or any other default name inserted by MS Access, to a meaningful name like Quantity.</p><p>Why do we need such a declaration of Textbox object instance with the keyword <i><a href="https://www.msaccesstips.com/2019/05/withevents-in-class-module-for-sub-form.html" target="_blank">WithEvents</a>, w</i>e have already seen that in the earlier episodes of this topic. It enables capturing the inbuilt <i>Events </i>of the Textbox when fired (RaiseEvent) and executes the Event Procedure Code written on the Form Module.</p><p>Two selected Properties of the Textbox are shown in the diagram above: <i>Change</i> Event and <i>On Change</i> Properties. The <b>Change</b> <i>Event</i> Property will not appear in the<i> </i>Property Sheet of the Textbox object. We have seen how an Event Property is declared in our User-defined Event examples in earlier articles.</p><p>The <i>On Change </i>Event<i> </i>Property (with String Type data) only appears in the Property Sheet of the Textbox. When we select the text <i>[Event Procedure]</i> option from the drop-down control in this Property, we could write the Event Procedure<i> </i>Code in the Form Module for the intended task of <i>Change</i> Event. Therefore, we assume that this has something to do with the <i>RaiseEvent</i> (Announcer) action for the Event <i>Change</i>. </p><p>Whenever we type some text in <i>msg</i> Textbox, the Change Event fires (or triggers the Subroutine, with the Event name <i>Change</i> and the parent object name msg as Prefix - <i>Sub msg_Change()</i>) for each character typed. </p><pre>Private Sub Msg_Change()
'Announce/Transmit the Event
RaiseEvent Message(Me!Msg.Text)
End Sub
</pre><p>So both the Event and the WithEvents Property declarations are not normally shown on the Property Sheet of any Object Instance created within the Form, except the Name Property. The <b>WithEvents</b> declaration goes with the Name Property only when an object instance is added to the Form.</p><p>Text0 object's inbuilt Event-related Procedure runs with its Parent Object name (Text0) as the event name prefix, and the Event Procedure is written in the Form Module, in other words, the Event Procedure Code must be written in the Parent Class Module of the Textbox Object instance. </p><p>So when we visualise the hierarchy of objects and Events, the Form Class Module is the top-level container of other Objects on the Form, like Textbox. Conversely, Textbox's Parent Object is the Form Class Module. The Textbox object is the parent object of its inbuilt Events and captures them when Events like AfterUpdate, Change, and GotFocus fire.</p><h3 style="text-align: left;">Events On Form and Class Module.</h3><p>The Event Procedure name must be written with the Parent Object name as a Prefix like <b>Sub Text0_AfterUpdate()</b>. Following the same rule, the Event Procedure must be coded in the Textbox object's Parent Object - the Form Class Module. When an Event of the Textbox Object is fired the parent object Textbox captures it (the WithEvents declaration enables it to do that) and the Event Procedure, which is written in the Textbox Object's Parent Form Class Module, executes the Event Procedure-based task. Here you can see a pattern forming as to how the object event handling is concerned.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQkt_pIHEsubbP_eRxL70l3dEUU93zmIFAyBp5iuXPb9KXIiTlrDzJBLjOErnYe-SFgRvNpdbE6bTW9lPtUS5T2H9Dun4X0rVdqTxdWcYtYZy8Sq-xcn2yzqd0091bzUwW7YOPGfVKoCT3V52FvN1lyI7WZEDuxasEEdao10Q6fNwi_bToN6Kh7-Tqrw/s804/Text0RaiseEvent.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="566" data-original-width="804" height="225" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQkt_pIHEsubbP_eRxL70l3dEUU93zmIFAyBp5iuXPb9KXIiTlrDzJBLjOErnYe-SFgRvNpdbE6bTW9lPtUS5T2H9Dun4X0rVdqTxdWcYtYZy8Sq-xcn2yzqd0091bzUwW7YOPGfVKoCT3V52FvN1lyI7WZEDuxasEEdao10Q6fNwi_bToN6Kh7-Tqrw/s320/Text0RaiseEvent.png" width="320" /></a></div><p>Text0 has its own inbuilt Event Collection. When any of them is Raised, the parent object Text0 captures it. Because the Textbox Object instance Text0 is declared on the Form (or wherever an instance is created) with the <i>WithEvents</i> keyword, then the VBA Code must be written in the Parent Class Module of the Text0 object. Our example is concerned, with the Textbox object's parent Class Module is the Form's Class Module.</p><p>The Event Procedure Code can be written only on the Text0 object’s Parent Class Module, that is the Form's Class Module. Hence, the Event Subroutines in this case are written in the Form’s Class Module as shown above. This is what we do normally on the Form Module.</p><p>Create an Instance of the same Text0 object in a stand-alone Class Module (say Class1) qualify it with the WithEvents keyword, and assign it with the Reference of the TextBox on the Form. Capture the Text0 Events fired from the Form Class Module and execute the Event Subroutines in the Class1 Class Module.</p><h3 style="text-align: left;">Create a Demo Form.</h3><p>Let us try an example to prove this rule that is applied to the stand-alone Class Module too.</p>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhHnBe0v8LPkFjv0o099u-Zadr6PMaQtbxOyRkw-YVN3WCN-RxxFEmFw99LZfaY9VEqxXYF7isSlksIPo-eO3JZ7p5yxEwbfxkCS9w88YwnzPO41PfKgU8PAerTM8MBD57xQ_Z2efziKVtytl0--UlO7SRb0uO-j2R1YC-pejGB2RM3qscnlbnCbLnHzw/s496/Form1Class1.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="Form1 Class1" border="0" data-original-height="450" data-original-width="496" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhHnBe0v8LPkFjv0o099u-Zadr6PMaQtbxOyRkw-YVN3WCN-RxxFEmFw99LZfaY9VEqxXYF7isSlksIPo-eO3JZ7p5yxEwbfxkCS9w88YwnzPO41PfKgU8PAerTM8MBD57xQ_Z2efziKVtytl0--UlO7SRb0uO-j2R1YC-pejGB2RM3qscnlbnCbLnHzw/s320/Form1Class1.png" width="320" /></a></div>
<ol><li><p>Create a new Form.</p></li><li><p>Add two Text Boxes on the Form, one below the other.</p></li><li><p>Click on the First Text Box and display its Property Sheet.</p></li><li><p>Change the Name Property value to <i>Quantity</i>.</p></li><li><p>Change the Caption of the Child Label to Max Quantity (1 - 10).</p></li><li><p>Select the Quantity control's Property Sheet, select the <i>[Event Procedure]</i> Option in the <i>After Update</i> Event Property, and click on the Build (. . .) Button to open the Form's Class Module.</p><h3>The Form Module VBA Code. </h3></li><li><p>Copy the following VBA codes and Paste them into the Form's Class Module, overwriting existing lines.</p>
<pre>Option Compare Database
Option Explicit
Private C As Class1 'Declare a Class1 Object Variable
Private Sub Form_Load()
Set C = New Class1 'Instantiate the Class1 Class Module<br />
Set C.Txt = Me.Quantity 'Assign Quantity Textbox Object to txt Property
End Sub
Private Sub Form_Unload(Cancel As Integer)
Set C = Nothing
End Sub
Private Sub Quantity_AfterUpdate()
'Code
End Sub
Private Sub Quantity_GotFocus()
'Code
End Sub
Private Sub Quantity_LostFocus()
'Code
End Sub
</pre></li><li><p>Save the Form with the Name Form1 and Close the Form.</p><h3>The Stand-alone Class Module.</h3></li></ol><p>Now, we need to create a stand-alone Class Module with the name Class1.</p>
<ol style="text-align: left;"><li><p>Open VBA Editing Window (ALT+F11)</p></li><li><p>Select Class Module from Insert Menu.</p><p>If the Class Module name is not Class1, Click on the Properties Button in the Toolbar above to display the Property Sheet then change the name to Class1.</p><p><b>Note:</b> If you already have a Class Module with the name Class1, then do not change the Class Module Name, instead change the <b>Class1</b> name in Form Module to match the name of the new stand-alone Class Module Name.</p><h3>The Class Module VBA Code.</h3></li><li><p>Copy the following VBA Code and Paste it into the Class1 Class Module:</p>
<pre>Option Compare Database
Option Explicit
Public WithEvents Txt As TextBox
Private Sub txt_AfterUpdate()
Dim i As Integer, msg As String
Dim info As Integer
i = Nz(Txt.Value, 0)
If i < 1 Or i > 10 Then
msg = "Valid Value Range 1 - 10 Only."
info = vbCritical
Else
msg = "Quantity: " & i & " Valid."
info = vbInformation
End If
MsgBox msg, vbOK + info, "txt_AfterUpdate()"
End Sub
Private Sub txt_GotFocus()
With Txt
.backcolor = &H20FFFF
.forecolor = 0
End With
End Sub
Private Sub txt_LostFocus()
With Txt
.backcolor = &HFFFFFF
.forecolor = 0
End With
End Sub
</pre></li><li><p>Select Save from the File Menu or Click on the Save Toolbar Button.</p></li><li><p>Select Compile from the Debug Menu to compile the Code and to make sure that everything is in order.</p><p>We will do a test run first and see how it works. Take note of this Point, we have selected the option <i><b>[Event Procedure]</b></i> in the Event Property to add the empty Subroutine Stubs on the Form Module (for the RaiseEvent action) for After Update, Got Focus, and Lost Focus Events.</p>When we Compile the VBA Code the empty Program stubs will be removed from the Form Module and the [Event Procedure] option selected on the Event Properties will be deleted by Access System. If that happens then our idea will not work as planned. To prevent that we have added a Rem line <b>'Code</b> in between the empty Event Procedure Stub.</li></ol><p>There are other methods we can use for the RaiseEvent action rather than creating empty Subroutine stubs, which we will explore later.</p><p>We have written VBA Code in the stand-alone Class Module <b>Class1</b> to validate the entered Quantity Value (the valid value range is set as 1 to 10 only) and display a message based on the validity of the entered value. This is the trick that you have to watch for, capturing the Event of the Quantity Textbox control on the Form and executing the related Event Procedure Code in the Class1 Class Module.</p><p>The GotFocus Event will change the Quantity Textbox's background color to Yellow, and the LostFocus Event will reset the color.</p><p>The second Textbox is only a support control, for setting the focus on this control, when the LostFocus event occurs on the Quantity Textbox.</p><p>Now, we are all set.</p><ol><li><p>Open <b>Form1</b> in Normal View.</p><p>You will see the Textbox's background is now in Yellow Color.</p></li><li><p>Enter the Quantity value 25 in the first Textbox and press Enter Key. You will see the Validation Error message saying that "Valid Value Range 1 - 10 only".</p><p>The first Textbox background color is now reset.</p></li><li><p>Now enter any value from the range 1 and 10 in the Quantity Textbox again.</p><p>This time you will get the message saying that the Value entered is Valid.</p></li></ol>
<p>Let us see how this works?</p>
<!--Google In-Article Ads start-->
<script async="" src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle" data-ad-client="ca-pub-2376623373770548" data-ad-format="fluid" data-ad-layout="in-article" data-ad-slot="6147807460" style="display: block; text-align: center;"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
<!--Google In-Article Ads END-->
<p>Check the Declaration line of Code for the TextBox Control in the Class1 Class Module:</p><pre>Public WithEvents Txt As TextBox </pre><p>We are creating an Instance of the Textbox object, with the object variable name <b>txt</b> as a Listener object with the declaration of <b>WithEvents</b> Keyword and with <b>Public</b> scope.</p><p>Class Module Properties are normally declared with Private scope, to ensure that their value is not changed from other programs, and access to them is allowed through Public Property Procedures. This approach will ensure that the value received through the Property Procedure is valid before assigning it to the Property. But we are on the learning curve and it is ok this way for now. </p><p>The rest of the Event Procedure Code is similar to what we normally write on the Form Module. But, one thing you might have noticed is that we are not using the original Textbox name Quantity as the prefix in the Event Procedure subroutine name: <b>Private Sub txt_AfterUpdate()</b></p><p>Because we are capturing the Quantity Textbox Events in the <b>txt</b> Textbox object Instance in Class Module Class1.</p><p></p><ul style="text-align: left;"><li>Creating a Textbox object with the name <b>txt</b> and with the <i>WithEvents</i> keyword alone will not make any automatic relationship with the Quantity Textbox on the Form to capture its Events. We must assign the Quantity <a href="https://www.msaccesstips.com/2019/04/withevents-button-combo-list-textbox-tab.html" target="_blank">Textbox</a>'s Reference to the <b>txt</b> object in the Class1 Module.</li><li><p>To do that the Class1 Class Module must be loaded into memory first and then assign the reference of the Quantity Textbox to the <b>txt</b> object, to capture the Events of the Quantity Textbox on the Form. The Class1 Class Module will remain in memory till we close Form1. In our earlier examples, we used two open Forms and their Class Modules for our experiments.</p></li><li><p>This is what we do in the Form's Class Module, in the following Code Segment:</p></li></ul><pre>Option Compare Database
Option Explicit
Private C As Class1
Private Sub Form_Load()
<span> Set C = New Class1</span>
Set C.Txt = Me.Quantity
End Sub
Private Sub Form_Unload(Cancel As Integer)
Set C = Nothing
End Sub
</pre><p>The statement <i>Private C As Class1 </i>declares a Class1 Object Variable C in the global declaration area of the Form Module. This is like declaring an inbuilt Variable, say <b>Dim City As String</b> in the declaration area of Form Module. But it is not loaded into memory till you assign a Value into the City Variable, like City="New York". Similarly the declared object must be Instantiated into memory with the keyword New.</p><p>On the <b>Form_Load()</b> event Procedure the statement <b>Set C = New Class1, </b>the New keyword instantiates the Class1 Object in memory. </p><p>The next statement <b>Set C.txt = Me.Quantity </b>assigns<b> </b>the Reference of the Quantity Textbox object on the Form to the <b>txt</b> Textbox instance in the Class1 Module. The txt object becomes the replica of the Quantity Textbox on the Form. The txt Object is declared with the keyword <i>WithEvents</i> so that when an Event, like AfterUpdate, is fired from the Quantity Textbox object on the Form it can be captured in the Class1 Module and execute the Event Procedure Code written within the Class1 Class Module. To trigger that Event firing action (RaiseEvent) we created the AfterUpdate empty Event Procedure stub on the Form Module.</p><p>When the Form is closed the Class1 Class Module Instance in object variable C is cleared from memory.</p><p>Next week we will explore other methods to invoke the RaiseEvent action, without keeping the empty Event Procedure stubs on the Form Module. </p>
<h4>Download Demo Database.</h4>
<!--Download Link /downloads/2023/05/Streamline3.zip-->
<div class="separator" style="clear: both; text-align: center;">
<a href="https://drive.google.com/file/d/1cHXhzcGc7scm-P3Tggn-Z9I7tPZsmhUS/view?usp=sharing">
<img border="0" data-original-height="29" data-original-width="22" height="29" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFE4uDLH9dk3S6pEcfTkT6GW6r11XqBI4HQnLnhQFZMKFeeQt5ABV_NqvLHM68J7RacISsvbUEl0PlAq6V7i0t0mQF3iesrBuqGuvrf5CJV72OzrGZ7rdMxkxzw8FYlz-qniNySoqtqUN4/s320/zip.gif" width="22" />Streamline3.zip</a>
</div>
<!--Download Link /downloads/2023/05/Streamline3.zip-->
<h3>Streamlining Form Module Code in Standalone Class Module.</h3>
<ol>
<li><a href="https://www.msaccesstips.com/2023/04/reusing-form-module-vba-code-for-new.html" target="_Blank">Re-using Form Module VBA Code for New Projects.</a></li>
<li><a href="https://www.msaccesstips.com/2023/04/streamlining-form-module-code-part-two.html" target="_Blank">Streamlining Form Module Code Part-Two</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/05/streamlining-form-module-code-part-three.html" target="_Blank">Streamlining Form Module Code Part-Three</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/06/streamlining-form-module-code-part-four.html" target="_Blank">Streamlining Form Module Code Part-Four</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/06/streamlining-form-module-code-part-five.html" target="_Blank">Streamlining Form Module Code Part-Five</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-six.html" target="_Blank">Streamlining Form Module Code Part-Six</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-seven.html" target="_Blank">Streamlining Form Module Code Part-Seven</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-eight.html" target="_Blank">Streamlining Form Module Code Part-Eight</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part-nine.html" target="_Blank">Streamlining Form Module Code Part-Nine</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part-ten.html" target="_Blank">Streamlining Form Module Code Part-Ten</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part.html" target="_Blank">Streamlining Form Module Code Part-Eleven</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-report-module-code-in.html" target="_Blank">Streamlining Report Module Code in Class Module</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-report-module-in-class.html" target="_Blank">Streamlining Report Module Code in Class Module-2</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-form-module-code-part-14.html" target="_Blank">Streamlining Form Module Code Part-14:All Controls</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-custom-made-form-wizard.html" target="_Blank">Streamlining Custom Made Form Wizard-15</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-form-vba-custom-report.html" target="_Blank">Streamlining Custom Report Wizard-16</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-form-vba-external-files.html" target="_Blank">Streamlining Form VBA External File Browser-17</a></li>
<li><a href="https://www.msaccesstips.com/2023/11/streamlining-event-procedures.html" target="_Blank">Streamlining Event Procedures of 3D TextWizard-18</a></li>
<li><a href="https://www.msaccesstips.com/2023/11/streamlining-event-procedures-rgbcolor.html" target="_Blank">Streamlining Event Procedures RGB Color Wizard-19</a></li>
<li><a href="https://www.msaccesstips.com/2023/12/streamlining-numbers-to-words-converter.html" target="_Blank">Streamlining Event Procedures Number To Words-20</a></li>
</ol>
a.p.r. pillaihttp://www.blogger.com/profile/02627590161919120214noreply@blogger.com0tag:blogger.com,1999:blog-7457417942281874810.post-89952463632298175182023-04-25T23:25:00.122+05:302024-03-14T21:16:23.621+05:30Streamlining Form Module Code Part Two<h3>Understanding Form Controls Event.</h3>
<p>I trust you found last week’s article and demonstration on <a href="https://www.msaccesstips.com/2023/04/reusing-form-module-vba-code-for-new.html?m=1" target="_blank">user-defined custom events</a> in the Form’s Class Module enjoyable. We delved into how events are defined, raised, or fired within the same Form Module, with the raised events subsequently captured by another open Form’s <a href="https://www.msaccesstips.com/2018/10/ms-access-class-module-and-vba.html?m=1" target="_blank">Class Module</a> to execute the related custom event procedure code. I anticipate that you may have several questions regarding this topic of events, and I eagerly await exploring it further with you to provide additional insight into this technique.</p><p>Typically when designing an Access Form, we add various objects, such as TextBoxes, Command Buttons, and ComboBoxes, each with their own pre-defined Events and corresponding Event firing actions. These events are captured by their respective Event Procedures and execute VBA code to accomplish specific tasks.</p>
<!--Google In-Article Ads start-->
<script async="" src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle" data-ad-client="ca-pub-2376623373770548" data-ad-format="fluid" data-ad-layout="in-article" data-ad-slot="6147807460" style="display: block; text-align: center;"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
<!--Google In-Article Ads END-->
<p>In the past, we kept adding Access objects to the form used their inbuilt events, and wrote VBA Code without fully understanding how they are made to work. Coding with whatever knowledge we attained through these processes in the form module, it become the second nature to continue doing that to get the task done. I tried to delve a little deeper into the underlying mechanisms and tried to understand them, in the practical point of view, a little bit of the inner workings of these processes. </p><p>When discussing Events on a form, it is important to keep the keywords <b>Event</b>, <b>RaiseEvent</b>, and <b>WithEvents</b> in mind. These Keywords are required to define an <b>Event</b>, invoke (or Fire, or <b>RaiseEvent</b> or Announce), and Capture the Event (<b>WithEvents</b>) in an Event Subroutine. These keywords are utilized in the following examples, as well as in forthcoming Articles.</p><ol style="text-align: left;"><li>Defines an event in the Form1 Class Module using the keyword Event: Public Event QtyUpdate(). This is the conventional method for defining Access events.</li><li><p>Fire the Event with the Keyword <b>RaiseEvent</b>: <i style="font-weight: bold;">RaiseEvent </i>QtyUpdate() in the Form1 Class Module.</p></li><li><p>To capture the event in the Form2 Class Module, instantiate an object of the Form1 Class Module: Public <b>WithEvents</b> ofrm as Form_Form1. The user-defined event cannot be captured within the same form where it’s initially defined. This limitation arises because the Fired Event can only be captured in an instance of the Form Object declared with the <b>WithEvents</b> keyword.</p><p>The resultant Event Procedure of ofrm_QtyUpdate() Event must be written in the target Form Module. The <i><b>ofrm_</b></i> prefix to the Subroutine name is taken from the Event Capturing declaration: <b>Private WithEvents ofrm As Form_Form1.</b></p><pre>Private Sub ofrm_QtyUpdate()
'VBA Code
End Sub</pre></li></ol>
<h3>User-Defined Event Example-2.</h3>
<p>To enhance comprehension of the mentioned keywords, let’s explore a new example involving user-defined events. Imagine we have a textbox enabling users to input a quantity with specific constraints. Our objective is to transmit the entered value to another form through a user-defined event for validation, and subsequently receive a relevant message in response.</p>
<p>To achieve this goal, we can define a Public Event named <b>QtyUpdate()</b> using the Event keyword. Subsequently, we can raise this event using the <b>RaiseEvent</b> keyword, passing the entered value as an argument. The event can then be captured in the class module of another open form using <b>WithEvents</b>, where the parameter value can be validated within an event procedure. Based on the validity of the parameter value, a suitable message can be displayed to the user.</p><p>To implement our events, let’s create two straightforward forms. In essence, we’ll define the events in the first form and execute the corresponding user-defined event procedure code in the second form’s class module.</p><p>The Event will be defined in the first form and it is fired from the first Form. The Raised Event in the first form is captured in the second Form's Class Module and executes the Event Subroutine Code. </p><p>With this foundational understanding, our intention is to restructure the code typically written within the Form Module into separate Class Modules, thereby allowing the Form to focus solely on user-interface design. This strategic approach is anticipated to significantly streamline project development timelines.</p><p>Let us try something simple to understand the concept a little more.</p>
<ol style="text-align: left;">
<li><p>Open your Database.</p></li>
<li><p>Create a new Form.</p></li>
<li><p>Insert a Textbox.</p></li>
<li><p>Display the Property Sheet of the Textbox.</p></li>
<li>
<p>Change the Name Property value: <b>OrderQty </b>(stands for Quantity).</p>
</li>
<li>
<p>
Change the child Label Caption value: <i>Quantity (1 - 5 only)</i>.
</p>
</li>
<li>
<p>
Insert a Command Button below the Textbox and change its Name Property value to
<b>cmdClose.</b>
</p>
</li>
<li><p>Change its Caption property to 'Close Form'.</p></li>
<li>
<p>
Add another Command Button below the earlier one and change its Name
Property Value to cmdOpen.
</p>
</li><li><p>Change the Caption Property value to 'Open Event Capture Form'</p></li><li><p>Display the Form's Property Sheet. Set the <i>Record Selectors</i> Property Value No.</p></li><li><p>Set <i>Navigation Buttons</i> to No.</p></li><li><p>Set <i>Scroll Bars</i> to Neither.</p></li><li><p>Right-click on the TextBox and select Properties from the displayed Menu.</p>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijb6K-C5xKHhrYnXwYqhqSTaPDx0kzgeNsIGo5zOtu1xQb80GV1cFUxo7nHZJ7YxIaeY2ePt5Oxfe6DVgK3X7cFo97ZXXuCdaoukom8ccB1VumkUwLnE-ZUuv5tOwxZrjpXPuuTDIt1JtvEyJuamsmvlBLjgdNgbMWISRYgHW18vdewEFu92GuY1I_2A/s506/UDFined12.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="Event Announcer Form" border="0" data-original-height="436" data-original-width="506" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijb6K-C5xKHhrYnXwYqhqSTaPDx0kzgeNsIGo5zOtu1xQb80GV1cFUxo7nHZJ7YxIaeY2ePt5Oxfe6DVgK3X7cFo97ZXXuCdaoukom8ccB1VumkUwLnE-ZUuv5tOwxZrjpXPuuTDIt1JtvEyJuamsmvlBLjgdNgbMWISRYgHW18vdewEFu92GuY1I_2A/s320/UDFined12.png" width="320" /></a></div>
</li><li><p>Select AfterUpdate [Event Procedure] option on the TextBox Property and Click on the Build (. . .) Button to open the VBA Module.</p></li>
<li>
<p>Copy and Paste the following VBA Code into the Form's Module,
overwriting existing lines, if any:</p>
<pre>Option Compare Database
Option Explicit
'User-Defined Events
'Captures in Second Form Module.
Public Event QtyUpdate(mqty As Single)
Public Event formClose(txt As String)
Private Sub cmdOpen_Click()
DoCmd.OpenForm "frmUDCapture", acNormal
End Sub
Private Sub OrderQty_AfterUpdate()
'Announce the Event and pass the OrderQty as Prameter
RaiseEvent QtyUpdate(Me!OrderQty)
End Sub
Private Sub cmdClose_Click()
'Announce the Event and pass the Text as Parameter
RaiseEvent formClose("Close the Form?")
End Sub
Private Sub Form_Unload(Cancel As Integer)
DoCmd.Close acForm, "frmUDCapture"
End Sub
</pre></li>
<li>
<p>Save the Form with the Name <b>frmUserDefined</b>.</p></li>
</ol>
<h3>User-Defined Events.</h3>
<p>In the global declaration area of the form's class module, we have defined two user-defined events that closely resemble functions.</p>
<pre>Public Event QtyUpdate(mqty As Single)
Public Event formClose(txt As String)
</pre>
<!--Google In-Article Ads start-->
<script async="" src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle" data-ad-client="ca-pub-2376623373770548" data-ad-format="fluid" data-ad-layout="in-article" data-ad-slot="6147807460" style="display: block; text-align: center;"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
<!--Google In-Article Ads END-->
<p>Access has built-in events that also look like functions, such as Text0_Exit(Cancel As Integer), Form_Unload(Cancel As Integer), and Form_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single).</p>
<p>The user-defined event declarations both begin with the keywords <b>Public Event</b>, followed by the event name and any associated parameters enclosed in parentheses. It is important to note that the event name should not contain an underscore character (e.g., Qty_Update). Additionally, the declaration must have a Public scope.</p><p>The first user-defined event is designed to validate the contents of the OrderQty textbox whenever it is updated on the form. The second event is triggered by the first command button to close the form and passes a text message as the parameter.</p><p>After entering a numeric value into the OrderQty TextBox and pressing the enter key, the OrderQty_AfterUpdate event procedure of the textbox is executed as usual. Within this event procedure, the RaiseEvent QtyUpdate(Me!OrderQty) statement is executed, which triggers the first user-defined event and passes the OrderQty textbox value as the parameter.</p><pre>Private Sub OrderQty_AfterUpdate()
'RaiseEvent and pass the OrderQty as a parameter
RaiseEvent QtyUpdate(Me!OrderQty)
End Sub</pre>
<p>In Access, we typically write event procedures for objects such as TextBoxes and <a href="https://www.msaccesstips.com/2006/09/command-button-animation.html" target="_blank">Command Buttons</a> in the same form module. These objects are part of the Access system and have their built-in events and mechanisms for capturing those events. We write event procedures for these <a href="https://www.msaccesstips.com/2018/12/ms-access-and-collection-object-basics.html" target="_blank">Objects</a> in the Form's Class Module where they reside. Later, we will review how this process works and why it differs from the approach we take with user-defined events. You may have questions about these differences, and we will explore them in more detail shortly.</p>
<p>When you declare the Form1 object Instance with the <i>WithEvents</i> keyword in the form2 module, the Events Raised by the Form1 Module can be captured in the Event Subroutine written in the Form2 Module. Instead of the Form1 Module, it can be from a Stand-alone Class Module too. This is because the <i>WithEvents</i> keyword allows the Form Module/Class Module to receive notifications about events raised by the Class object, and to respond to them by running the corresponding event procedures.</p>
<p>The user-defined events are not related to any object-based events like the built-in events of a TextBox. The only mechanism for raising a user-defined event is to explicitly call the RaiseEvent statement in the code, as we did in the example.</p>
<p>Additionally, a user-defined event declaration doesn't have any inherent mechanism of its own like a TextBox. It simply defines the event's name, any parameters, it may have, and its public accessibility. It's up to the programmer to implement the code that raises the event and the code that handles the event when it's raised.</p>
<p>It's also worth noting that a user-defined event can be raised only from the Class Module of a Form or from a stand-alone Class Module. However, as I mentioned earlier, in order to capture the event in another module, that module must use the <b>WithEvents</b> keyword, to declare an Instance of the Class Module of the first Form. </p>
<p>The <i>RaiseEvent</i> statement within the OrderQty_AfterUpdate() event procedure announces the <i>QtyUpdate()</i> user-defined event and passes the OrderQty value as a parameter. This QtyUpdate() event is then captured in the second form's module, the Event Procedure is executed, and validates the OrderQty value.</p>
<p>The cmdClose_Click() event procedure in the <b>frmUserDefined</b> Form raises the <i>formClose()</i> event with a message text as the parameter. Then, the second Form's Module captures this event and shows the message text to the User to determine whether to close the <b>frmUserDefined</b> Form or not, based on the User's responses.</p><p>This is a simple example of how user-defined events can be used to communicate between different Forms or Class Modules in VBA.</p>
<pre>Private Sub cmdClose_Click()
'Announce the Event and pass the Text as Parameter
RaiseEvent formClose("Close the Form?")
End Sub
Private Sub Form_Unload(Cancel As Integer)
DoCmd.Close acForm, "frmUDCapture"
End Sub</pre>
<p>The <b>Form_Unload()</b> event of the frmUserDefined form is triggered first, as the form is closed, and the frmUDCapture Form is closed first. </p><p> The <b>Form_Close()</b> event is triggered next after the <b>frmUDCapture</b> form is closed and closes the <b>frmUserDefined</b> Form in the <b>cmdClose_Click()</b> event procedure. </p>
<h3>The User-Defined Event Capturing Form.</h3>
<ol style="text-align: left;">
<li><p>Create a new Form and open it in Design View. </p> </li>
<li><p>Insert two labels as shown in the Image of the Form's design view given below. </p>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhaixoLW_k5srRSGWYPurMfXPi6KWXV7MIq1qm4BcBvPmGXIw0k3WDTeV6IGeRtf0PY0f6RftKMUYcwN3LMrsKVxibexYY02nILw9wa6MP7UfjmEedd3v45sWTMQ6QJbNDdqyMQrNlRgyVIXix-h7uLrrqI0WW-ccdPMa4gGKYaSOOnjHIZwyojtInSLw/s600/UDFined22.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="Event Capturing Form" border="0" data-original-height="425" data-original-width="600" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhaixoLW_k5srRSGWYPurMfXPi6KWXV7MIq1qm4BcBvPmGXIw0k3WDTeV6IGeRtf0PY0f6RftKMUYcwN3LMrsKVxibexYY02nILw9wa6MP7UfjmEedd3v45sWTMQ6QJbNDdqyMQrNlRgyVIXix-h7uLrrqI0WW-ccdPMa4gGKYaSOOnjHIZwyojtInSLw/s320/UDFined22.png" width="320" /></a></div>
</li>
<li><p>Change the first label caption to EVENT MESSAGES. </p> </li>
<li><p>Select the second label and display its Property Sheet. </p> </li>
<li><p>Change the Name <a href="https://www.msaccesstips.com/2008/09/source-connect-str-property-and-odbc.html" target="_blank">Property</a> Value to Label2, if it is different. </p> </li>
<li><p>Change the Caption Property Value to Event. </p> </li>
<li><p>Display the Form's Property Sheet. Set the <i>Record Selectors</i> Property Value: No. </p> </li>
<li><p>Set <i>Navigation Buttons</i> to No. </p> </li>
<li><p>Set <i>Scroll Bars</i> to Neither. </p> </li>
<li><p>Change the Detail Section Area of the Form as small as shown in the above Image. </p> </li>
<li><p>Display the Form's VBA Module.</p> </li>
<li><p>Copy the following VBA Code and Paste it over the existing lines, if any, in the Module.</p>
<pre>Option Compare Database
Option Explicit
Public WithEvents ofrm As Form_frmUserDefined
Private Sub Form_Load()
On Error Resume Next
<blockquote style="border: medium; margin: 0px 0px 0px 40px; padding: 0px;">Set ofrm = Forms("frmUserDefined")</blockquote>End Sub
Private Sub ofrm_QtyUpdate(sQty As Single)
Dim Msg As String
If Nz(sQty, 0) < 1 Or Nz(sQty, 0) > 5 Then
Msg = "Valid Qty Range: 1 - 5 Only"
Else
Msg = "Order Qty: " & sQty & " Approved."
End If
Me.Label2.Caption = Msg
MsgBox Msg, vbInformation, "QtyUpdate()"
End Sub
Private Sub ofrm_formClose(txt As String)
Me.Label2.Caption = txt
If MsgBox(txt, vbYesNo, "FormClose()") = vbYes Then
DoCmd.Close acForm, ofrm.Name
Else
Me.Label2.Caption = "Close Action = Cancelled."
End If
End Sub
</pre>
</li>
<li><p>Save the Form with the Name frmUDCapture and Close it.</p></li></ol><h3>Event Capturing Form Module VBA Code Review.</h3><pre>Public WithEvents ofrm As Form_frmUserDefined
Private Sub Form_Load()
On Error Resume Next
Set ofrm = Forms("frmUserDefined")
End Sub
</pre>
<ol>
<li><p>The line of code <i>Public WithEvents ofrm As Form_frmUserDefined</i> is a crucial global declaration in the second Form's Class Module. It declares a new instance of the first Form, <b>frmUserDefined</b>, as an Event Listener Object variable named <b>ofrm</b>.</p>
<p>This is important because it enables the second Form Module to capture and respond to events that are raised by the first Form. By the above declaration, the Listener Object in the second Form can intercept and handle user-defined events raised by the frmUserDefined.</p>
<p>Without this declaration, the second Form would be unable to capture and respond to events raised by the first Form, and the user-defined event functionality would not work as intended.</p>
<p>To make a long story short, we are declaring an Instance of the frmUserDefined Form's Class Module Object Variable name <i>ofrm</i>. The <i>Form_ </i>prefix to the Form Name indicates that we are referring to the Form's Class Module and will create an Instance of the Class Module.</p><p>The first Form's Class Module Object Instance will be assigned to the declared <i>ofrm</i> Object Variable through the frmUDCapture Form's <i>Form_Load()</i> Event Procedure.</p>
<p>The <i><b>WithEvents</b> </i>keyword declares the <i>ofrm</i> as a Listener Object to Capture the Events Raised from its original Class Module-based user-defined Events.</p><p><b>Note: </b>Remember the <i>WithEvents</i><b style="font-style: italic;"> </b>classification can be made only when an instance of any Object, like Form, TextBox, <a href="https://www.msaccesstips.com/2008/03/refresh-dependant-combo-box-contents.html" target="_blank">ComboBox</a>, etc., is created.</p><p>The general rule of Object Instance creation as a Listener Property is that:</p>
<ul><li><p>The <i>WithEvents</i> keyword is used in the parent Class Module to create a connection between the object and its events. This connection allows the Class Module to capture events fired by the object and call the corresponding event procedure in response.</p>
<p>The Event Subroutine name must start with the object instance name, followed by an underscore, and then the event name. This naming convention ensures that Access can properly map the event to the correct event procedure.</p>
<p>For example, if we have a TextBox object Instance named "txtName" and we want to capture its LostFocus event, we would declare it in the parent Class Module like this:</p>
<pre>Private WithEvents txtName As Access.TextBox
Private Sub txtName_LostFocus()
' Do something when the txtName TextBox loses focus
End Sub
</pre>
<p>In this example, the object instance name "txtName" is prefixed to the event name "LostFocus" to create the event procedure name "txtName_LostFocus".</p></li><li><p>To capture the user-defined event QtyUpdate() from the frmUserDefined form module, the parent form module instance should be declared with the WithEvents keyword. This allows the frmUDCapture form to listen to the event transmission from the frmUserDefined form and execute the corresponding event procedure.</p></li>
<li><p><b>Note:</b> When you add a TextBox (or any other control) to a form in Access, the Access system creates an instance of the TextBox object and adds it as a control to the form. The Access system also creates a property for the TextBox in the form's module, which is declared with the <i>WithEvents</i> keyword. The Name property will be filled with a default name followed by the text "As TextBox" appended to it. For example, if the default name of the TextBox is "Text0", the property declaration of the TextBox in the form's module will be:</p>
<pre>Private WithEvents Text0 As TextBox</pre>
<p>You can change the name Text0 of the TextBox control to something more meaningful like Quantity, and the name of the property declaration in the form's module will automatically update to reflect the name change. This declaration is not directly visible to us except the Name Property Value. But can be displayed through the Object Browser.</p></li>
<li><p>Check the last two lines at the bottom of the Object Browser image below and see how the TextBox Object Instance name OrderQty declaration appears on our Form frmUserDefined. Check the declaration starting with the Keyword WithEvents OrderQty As TextBox.</p>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2vVPM4ljhX6GOQ4w2RivTCqfqBu-m0gF6sVnKqxMYQPlTT9Y-17-hkjbbhEfbNrMXEExpTpwilyCZPN-A3fb4n49vRqXaIO0N4GMV8kQ7MvXvaBSpjmYu8Q4B_MCO_P1r3anREXMO5b_n8lAusyfxQ114z6iDfa-aUm4H2Rnx-NxMaSmk5MQC4ns8Vw/s738/WithEventsOrderQty.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="402" data-original-width="738" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2vVPM4ljhX6GOQ4w2RivTCqfqBu-m0gF6sVnKqxMYQPlTT9Y-17-hkjbbhEfbNrMXEExpTpwilyCZPN-A3fb4n49vRqXaIO0N4GMV8kQ7MvXvaBSpjmYu8Q4B_MCO_P1r3anREXMO5b_n8lAusyfxQ114z6iDfa-aUm4H2Rnx-NxMaSmk5MQC4ns8Vw/s320/WithEventsOrderQty.png" width="320" /></a></div>
<p>To inspect the Property of the TextBox in the Object Browser or any other control on the Form, you can follow these steps:</p>
<ol><li><p>Right-click on the VBA Editing Window and select Object Browser Option, or Click on the Object <a href="https://www.msaccesstips.com/2006/10/file-browser-in-msaccess.html" target="_blank">Browser</a> Button from the Toolbar above to Open the Object Browser.</p></li>
<li><p>In the Object Browser, select the Database Name in the <All Libraries> Control.</p></li>
<li><p>The Object Browser will display the Properties and Methods of the selected control, including the declaration for the control's Object Instance.</p></li>
<li><p>Select the Form Name <i>Form_frmUserDefined</i> from the left panel of the Browser Window.</p></li>
<li><p>The Form's Property List appears in alphabetical order in the right-side Panel.</p></li>
<li><p>Scroll down and find the OrderQty Property of the TextBox and select it.</p></li><li><p></p><p>For example, the declaration for a TextBox with the name OrderQty on a form named frmUserDefined would look like this:</p><pre>WithEvents OrderQty As TextBox
</pre>
<p>At the bottom panel of the Object Browser in the detail view of the selected <a href="https://www.msaccesstips.com/2009/11/creating-using-form-custom-property.html" target="_blank">Property</a>. </p><p></p></li>
</ol>
<ul>
<li><p>You may look for the user-defined Event QtyUpdate() declaration and how it appears in the right panel with a lightning icon and check the details view of the event declaration in the bottom panel.</p></li>
<li><p>Both user-defined events and built-in events of Access objects require an event procedure to be created on their parent VBA module. For built-in events, the event procedure name will be based on the name of the Access object and the event name. For example, if the Access object is a Textbox named "Text0" and the Event is "LostFocus", the event procedure name will be "Sub Text0_LostFocus()".</p>
<p>The User-defined Event Subroutine name consists of two parts. The first part is the Event Subroutine name Prefix, and the second part is the Event Name. The first part is the First Form Module's Instance name declared with the WithEvents keyword in the second Form Module - objFrm. The second part is the QtyUpdate, like <b>objFrm_QtyUpdate()</b>, both parts are separated with an underscore character. </p></li><li><p>The LostFocus event is an inbuilt event of the TextBox control in Access. When you add a TextBox control to a form, Access creates an instance of the TextBox object and assigns it a default name such as <b>Text0</b>. This TextBox instance is then declared as a property of the form, with the <b>WithEvents</b> keyword, which allows it to capture and handle its inherent events.</p>
<p>As I mentioned, the Text0 object's parent object is the class module of the form, which means that the event handling code in the LostFocus event (or any other inbuilt event) should be written in the form's class module. The naming convention for event procedures for controls is that they start with the name of the control instance, followed by an underscore, and then the name of the event. In the case of the <a href="https://www.msaccesstips.com/2008/10/textbox-and-label-inner-margins.html" target="_blank">TextBox</a> control named Text0, the event procedure for the LostFocus event would be named <b>Sub Text0_LostFocus()</b>.</p>
<p>Several Textbox object instances can be created in the same Form with appropriate names, and their Event Subroutines are written in the same Form's Class Module with TextBox names as the prefix. This is what we do normally on Form Module.</p></li>
<li><p>The QtyUpdate() event is a user-defined event and is not part of any built-in object like the LostFocus event of a TextBox. It is defined in the Class Module of frmUserDefined Form and the parent object of the event is the form itself.</p>
<p>In the frmUDCapture form, we have declared an instance of the frmUserDefined form as a listener parent object using the <b>WithEvents</b> keyword. Since the Form Class Module instance is named <b>"ofrm"</b>, the event procedure for the QtyUpdate() event will have the format "Sub ofrm_QtyUpdate()". This allows the frmUDCapture form to capture and respond to the QtyUpdate() event raised by the frmUserDefined form.</p></li>
</ul><p>The statement <i>Set ofrm = Forms("frmUserDefined") </i>statement within the Form_Load() Event Procedure of the frmUDCaptured Form Module, and if the <a href="https://www.msaccesstips.com/2008/10/wave-shaped-reminder-ticker.html" target="_blank">Form</a> is in an open state, then assigns it to the <i>ofrm </i>Class Module object. If this form is not in the open state, then it will run into Error. The <i>On Error Resume Next</i> statement will suppress the error message, but the Event capturing will not work as expected. </p><p>Ensure that you open the <b>frmUserDefined</b> Form first, then the second form frmUDCapture next.</p></li>
<li><p>We enter some Numeric Quantity Value into the <b>OrderQty</b> Textbox on the Form frmUserDefined and press the Enter Key to complete the data entry. The <i>RaiseEvent QtyUpdate(Me!OrderQty)</i> is fired from within the OrderQty_AfterUpdate() Event Procedure.</p></li>
<li><p>This Event is captured in the frmUDCapture Form's Event Procedure Subroutine Code given below: </p>
<pre>Private Sub ofrm_QtyUpdate(sQty As Single)
Dim Msg As String
If Nz(sQty, 0) < 1 Or Nz(sQty, 0) > 5 Then
Msg = "Valid Qty Range: 1 - 5 Only"
Else
Msg = "Order Qty: " & sQty & " Approved."
End If
Me.Label1.Caption = Msg
MsgBox Msg, vbInformation, "QtyUpdate()"
End Sub
</pre>
</li>
<li><p>The Valid value Range accepted in the TextBox is 1 to 5 only. Normally we write this validation check procedure in the same Form Module Event Subroutine only. Here, we are running the validity check in the second Form <a href="https://www.msaccesstips.com/2011/11/vba-module-object-and-methods.html" target="_blank">Module</a>, and an appropriate message is written on the Msg String Variable and displayed in the <a href="https://www.msaccesstips.com/2021/02/message-box-that-closes-itself-after.html" target="_blank">MsgBox</a> too. It will update the same message text in the caption of Label Control on the second Form, to view the message after closing the Message Box. </p></li>
<li><p>Check the Event Subroutine line: <i>Private Sub ofrm_QtyUpdate(sQty As Single)</i></p><p>The frmUserDefined Form's Class Module Object Instance name <i>ofrm</i> is used as the Subroutine name prefix combined with the User-Defined Event name QtyUpdate(), both separated with an underscore character. </p></li>
<li><p>Similarly, the FormClose() Event is captured and runs the <i>Private Sub ofrm_formClose(txt As String) Event Procedure</i> and it displays a message to the User, whether to close the Form frmUserDefined (first form) now, or not. If the response is Yes then it closes the First Form.</p>
<pre>Private Sub ofrm_formClose(txt As String)
Me.Label2.Caption = txt
If MsgBox(txt, vbYesNo, "FormClose()") = vbYes Then
DoCmd.Close acForm, ofrm.Name
Else
Me.Label2.Caption = "Close Action = Cancelled."
End If
End Sub </pre></li><li><p>In the formClose() Event Procedure header line you can see that the <i>ofrm </i>object prefix is appearing.<i> </i>In this subroutine, the parameter text is shown in the message box and the user responds whether the frmUserDefined Form (the first Form) is to be closed now or not. The Message text is displayed from the second Form's Event Procedure. If the response is Yes then it runs the Code to close the first Form.</p> <p>When we issue Docmd.OpenForm "FormName"<i> </i>Command<i>, </i>a series of Events takes place in a certain order, <i>Form Open</i>, <i>Form Load</i>, <i>Form Current</i> in that sequence. Similarly, when we run the DoCmd.Close acForm, "FormName"<i>, </i>the <i>Form_Unload</i> Event runs first before the <i>Form_Close</i> event, if both events are run in VBA. </p></li><li><p>So when we run the above Form_Close Event Procedure to close the first Form, the first Form prepares to close down. Since we have written the Form_Unload Event Procedure in the first Form, to Close the second Form, it runs first and closes the second Form, before closing the first Form.</p></li>
</ul></li></ol>
<h3>The User-Defined Events Trajectory View.</h3>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkgwo20bGF0rB39sdxTWpuESXMDW8BrgVAjoYNRuBcKcqIpQO2OgPWI6b78Idx5JuFT1PkH-o7vfkYI5qxggQuGTFh7LNBrYqwZfAy14ZDqPIHaxZVkKNMgvbKf6p_BF2EHSMFl168FvhC-vPdx57zD9wogNpwcV_5eSB1xxGZSWAY-3G1XwleN36Igw/s911/EventsTragectary.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="Events Trajectory View." border="0" data-original-height="563" data-original-width="911" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkgwo20bGF0rB39sdxTWpuESXMDW8BrgVAjoYNRuBcKcqIpQO2OgPWI6b78Idx5JuFT1PkH-o7vfkYI5qxggQuGTFh7LNBrYqwZfAy14ZDqPIHaxZVkKNMgvbKf6p_BF2EHSMFl168FvhC-vPdx57zD9wogNpwcV_5eSB1xxGZSWAY-3G1XwleN36Igw/s320/EventsTragectary.png" width="320" /></a></div>
<p>From the above graphical image, we can easily see at a glance how the user-defined Event is declared with the <i>Event</i> Keyword, how the Events are <i>Announced </i>with the <i>RaiseEvent </i>Keyword<i>,</i> and how the Event <i>Listener </i>is declared with the Keyword <i>WithEvents </i>that<i> </i>Captures the announced Event, identify it with the Event Procedure, and executes the correct Event Procedure Code. </p><p>If we learn the inner workings of the Events and Event Procedure we can write Event Procedures of one Form in another Form's Class module, or Event Procedures can be in stand-alone Class Modules, rather than writing all the Event Procedures on the same Form Module. We are really heading for that. But before touching the tricks at that level, we need to know a few basics of Event handling on Forms. How we could do things differently on Form, other than the traditional method we follow now.</p><p>Once we know how these tricks work together, our target is to take all the VBA codes out of the Form Module and organize them in such a way that Coding becomes very easy. We will use the Forms for User Interface design only. </p><p>Besides that, once we streamline and adopt the new Coding procedure, we can easily take the backbone of the new procedure to other Projects and coding will become easier, enabling faster completion of new Projects.</p><h4>Download Demo Database</h4>
<!--Download Link /downloads/2023/04/UserDefined2.zip-->
<div class="separator" style="clear: both; text-align: center;">
<a href="https://drive.google.com/file/d/1wxQpU8Fy5rvGWaMtRXWFdcMh2-IfL-WX/view?usp=sharing">
<img border="0" data-original-height="29" data-original-width="22" height="29" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFE4uDLH9dk3S6pEcfTkT6GW6r11XqBI4HQnLnhQFZMKFeeQt5ABV_NqvLHM68J7RacISsvbUEl0PlAq6V7i0t0mQF3iesrBuqGuvrf5CJV72OzrGZ7rdMxkxzw8FYlz-qniNySoqtqUN4/s320/zip.gif" width="22" />UserDefined2.zip</a>
</div>
<!--Download Link /downloads/2023/04/UserDefined2.zip-->
<h3>Streamlining Form Module Code in Standalone Class Module.</h3>
<ol>
<li><a href="https://www.msaccesstips.com/2023/04/reusing-form-module-vba-code-for-new.html" target="_Blank">Re-using Form Module VBA Code for New Projects.</a></li>
<li><a href="https://www.msaccesstips.com/2023/04/streamlining-form-module-code-part-two.html" target="_Blank">Streamlining Form Module Code Part-Two</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/05/streamlining-form-module-code-part-three.html" target="_Blank">Streamlining Form Module Code Part-Three</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/06/streamlining-form-module-code-part-four.html" target="_Blank">Streamlining Form Module Code Part-Four</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/06/streamlining-form-module-code-part-five.html" target="_Blank">Streamlining Form Module Code Part-Five</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-six.html" target="_Blank">Streamlining Form Module Code Part-Six</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-seven.html" target="_Blank">Streamlining Form Module Code Part-Seven</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-eight.html" target="_Blank">Streamlining Form Module Code Part-Eight</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part-nine.html" target="_Blank">Streamlining Form Module Code Part-Nine</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part-ten.html" target="_Blank">Streamlining Form Module Code Part-Ten</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part.html" target="_Blank">Streamlining Form Module Code Part-Eleven</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-report-module-code-in.html" target="_Blank">Streamlining Report Module Code in Class Module</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-report-module-in-class.html" target="_Blank">Streamlining Report Module Code in Class Module-2</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-form-module-code-part-14.html" target="_Blank">Streamlining Form Module Code Part-14:All Controls</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-custom-made-form-wizard.html" target="_Blank">Streamlining Custom Made Form Wizard-15</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-form-vba-custom-report.html" target="_Blank">Streamlining Custom Report Wizard-16</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-form-vba-external-files.html" target="_Blank">Streamlining Form VBA External File Browser-17</a></li>
<li><a href="https://www.msaccesstips.com/2023/11/streamlining-event-procedures.html" target="_Blank">Streamlining Event Procedures of 3D TextWizard-18</a></li>
<li><a href="https://www.msaccesstips.com/2023/11/streamlining-event-procedures-rgbcolor.html" target="_Blank">Streamlining Event Procedures RGB Color Wizard-19</a></li>
<li><a href="https://www.msaccesstips.com/2023/12/streamlining-numbers-to-words-converter.html" target="_Blank">Streamlining Event Procedures Number To Words-20</a></li>
</ol>
a.p.r. pillaihttp://www.blogger.com/profile/02627590161919120214noreply@blogger.com0tag:blogger.com,1999:blog-7457417942281874810.post-35967451007369960462023-04-12T16:49:00.067+05:302024-03-13T20:52:30.259+05:30Reusing Form Module VBA Code for New Projects.<h3 style="text-align: left;">Streamlining Form Module Event Procedures.</h3>
<h4>The Existing Form Module Coding Procedure.</h4>
<p>Access Forms incorporate a variety of controls, including Textboxes and Command Buttons, each serving specific functions. Typically, we create numerous Event Procedures in the Form's Class Module to execute various tasks related to these controls. However, a common challenge arises when handling multiple Event Subroutines for a single TextBox. The code tends to be dispersed, lacking organization, and intermixed with Event Procedure Codes for other controls in the Form Module.</p>
<p>This often results in a less-than-optimal development experience, requiring us to repeatedly navigate to the Form in design view. Whether making user interface design adjustments or refining code in the Form Module, we find ourselves frequently accessing the Form to locate and modify specific Event Procedure Code through its associated Event related Property.</p>
<!--Google In-Article Ads start-->
<script async="" src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle" data-ad-client="ca-pub-2376623373770548" data-ad-format="fluid" data-ad-layout="in-article" data-ad-slot="6147807460" style="display: block; text-align: center;"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
<!--Google In-Article Ads END-->
<h3>The Streamlining of Class Module Code.</h3><p>This topic is intricate, demanding your careful attention to the series of examples provided on these pages. It’s essential to grasp the concept and procedures thoroughly by experimenting with them firsthand before advancing to the next level.</p><p>For those unfamiliar with Microsoft Access Class Module and constructing Access Class Objects, it’s recommended to review the earlier blog posts, starting from the <a href="https://www.msaccesstips.com/2018/10/ms-access-class-module-and-vba.html" target="_blank">MS Access Class Module and VBA </a>series of articles. These initial articles were designed for beginners to grasp the fundamentals of building Class Module Objects through tutorials. Links to these earlier articles are provided at the end of this page for your convenience.</p><p>This topic will be explored through a series of articles distributed over several weeks, aiming to elucidate the fundamentals of this intricate concept through practical examples. Readers can expect to find external Class Module VBA code samples, event flow diagrams, and downloadable demo databases with ready-to-run examples in the upcoming weeks.</p>
<h3>Control's Event Procedures on the Form. </h3>
<p>
But, let us try to understand a few things we take for granted, like writing Event Procedures for controls on the Form. When we want to do some task, like check the validity of the data in the Text Box we take the OnExit or BeforeUpdate Event
Property and use one of the three following choices:
</p>
<p>1. Gives the name of a Macro in the Event Property to run the Macro Code when the Event (like BeforeUpdate) takes place.</p>
<p>2. Call a Public Function from the Event Property to run.</p>
<p>
3. Or write an Event Procedure in the Form Module, like BeforeUpdate(), to execute the Code when the Event is fired. </p>
<p>
If one of the first two options is used, then the Form doesn't need a Class
Module. But, when the <i>[Event Procedure]</i> option is selected, the MS Access
System automatically adds a Class Module to the Form.
</p>
<p>
It’s fascinating to understand how the system triggers the event action from the form’s controls, captures it within the form’s Class Module, and executes the VBA code tailored for that control’s specific task.
</p>
<p>
All Access objects or controls, such as TextBoxes, ComboBoxes, ListBoxes, and others, are structured as objects within standalone Class Modules. Each of these objects possesses properties that determine their appearance, colors, and inherent events. </p><p>When we select a TextBox control from the menu and position it as desired, Access generates it with a default name, such as Text0. However, we have the option to replace this default name with a more descriptive one. The TextBox we’ve added to the form is essentially a copy (instance) of the Access.TextBox Class. By selecting the [Event Procedure] option in the BeforeUpdate Event Property, we trigger the event (using the RaiseEvent action) and subsequently write the event subroutine code within the empty event subroutine stub automatically provided by the Access System. Notably, the control’s name is consistently prefixed in the event procedure subroutine name, like so:</p><pre> Sub<i> Quantity_BeforeUpdate()
End Sub</i></pre><p>In the realm of Access Objects designed with standalone Class Modules, they inherently possess their own set of built-in Events. While the inner workings of how Microsoft Access manages these events for controls on a Form may not be explicitly transparent, a notable observation lies in the configuration of the <i>On LostFocus</i> Event Property.</p>
<p>When this String Data Type property is set with the text <i>[Event Procedure]</i>, a sequence unfolds: the Object's Event <b>Announcer</b> activates, announcing the event (<b>RaiseEvent</b>). Simultaneously, the Object Module <b>Listener</b> (utilizing <b>WithEvents</b>) captures this announcement. Consequently, an empty subroutine stub emerges in the Parent Form Module, resembling '<b>Sub Quantity_LostFocus()</b>', awaiting the VBA Code to be seamlessly integrated within this empty procedural framework. This methodology facilitates a structured approach to incorporating code within the designated Event Procedure stub, triggered by the LostFocus event.</p>
<p>
Have you ever thought of finding out how the inbuilt <b>Events</b> are defined and
what it does to fire an Event to execute a small block of VBA Code to do
some task?
</p>
<p>
Let's delve into a straightforward example to grasp how object events are defined, raised, and captured to write code in the form module and execute the assigned task. Over the upcoming weeks, we'll extensively explore this topic of event firing and capturing in various contexts. Our goal is to introduce a fresh approach to VBA coding within the standalone class module, enabling swift and seamless coding. Furthermore, this approach facilitates the export and reuse of VBA code segments for other projects, thereby reducing database development time.
</p>
<h3 style="text-align: left;">User-defined Custom Events.
</h3>
<p>Key Words<b>: Event, RaiseEvent, </b>and<b> WithEvents.</b></p><ol><li><p>Open your Database.</p></li><li><p>Create a new Form.</p>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjVibyOQhOuzjJ_sBkaYAKDgD7hSo1IES4ggUaZSPIMzmBWIwc322KGEtEzr6i8geZxpd4WzcWyKnC-sNGKil4E-lOC8SfOL5Ts3ltNMuzYfDmXRs2cf1Q-01Msjfp2k4kLzgPJS6rYzf9MQV7Mx09ZJUZHH_nXxzItG20k0ysX3wZnqc5sooTZMdvHiA/s645/EventForm1.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="465" data-original-width="645" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjVibyOQhOuzjJ_sBkaYAKDgD7hSo1IES4ggUaZSPIMzmBWIwc322KGEtEzr6i8geZxpd4WzcWyKnC-sNGKil4E-lOC8SfOL5Ts3ltNMuzYfDmXRs2cf1Q-01Msjfp2k4kLzgPJS6rYzf9MQV7Mx09ZJUZHH_nXxzItG20k0ysX3wZnqc5sooTZMdvHiA/s320/EventForm1.png" width="320" /></a></div>
</li><li><p>Insert a Textbox.</p></li><li><p>Display the Property Sheet of the Textbox.</p></li><li><p>Change the Name Property value to <b>Msg.</b></p></li><li><p>Select the <i>On Change</i> Event Property and select [Event Procedure] from the drop-down list.</p></li><li><p>Click on the build (. . .) Button to open the Form Module. </p></li><li><p>Copy and Paste the following VBA Code into the Form Module:</p>
<pre>'Define user-defined Event Message
Public Event Message(txt As String)
Private Sub Msg_Change()
'Announce/Transmit the Event
RaiseEvent Message(Me!Msg.Text)
End Sub
</pre>
<!--Google In-Article Ads start-->
<script async="" src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<ins class="adsbygoogle" data-ad-client="ca-pub-2376623373770548" data-ad-format="fluid" data-ad-layout="in-article" data-ad-slot="6147807460" style="display: block; text-align: center;"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
<!--Google In-Article Ads END-->
<p>The initial declaration statement in the VBA code above introduces a user-defined event named Message(), accompanied by a single parameter of String type to be passed when invoked. The event must be defined with a public scope, followed by the keyword Event, and then the event name (not containing the underscore character, such as txt_Message), along with any parameter list enclosed in parentheses.</p><p>In the Change Event Procedure of the <i>Msg</i> TextBox, we trigger the <i>Message</i> Event Procedure using the statement <i>RaiseEvent</i> Message(Me.Msg.Text). By running the Event within the <i>Change</i> Event Procedure, each time a character is typed in the TextBox on the form, the Event is triggered. We’ll capture this action in another Form Module and display the TextBox contents there to ensure that our User-defined Event is functioning correctly.</p></li><li><p>Change the Text Box's child Label Caption value to <i>Msg:</i>.</p></li><li><p>Save the Form with the name <b>Form1 </b>and close the Form.</p></li><li><p>Create a new Form with the Name <b>Form2</b> and open it in Design View.</p></li><li><p>Change the size of the form to as small as Form1.</p></li><li><p>Insert a Label control on the Form, and enter some text in the label's Caption to prevent Access from removing the Label Control from the Form.</p></li><li><p>Change the Popup property value of the Form to <b>Yes</b></p></li><li><p>Select the Form Load Event Property and select [Event Procedure] and click on the build (. . .) Button to open the Form Module.</p></li><li><p>Copy and Paste the following VBA Code into the Form2 Module overwriting the existing Code Lines:</p><pre>Option Compare Database
Option Explicit
'Declare the listener Form1 Class Object with the name frm.
Private WithEvents frm As Form_Form1
Private Sub Form_Load()
On Error Resume Next
Set frm = Forms("Form1") 'assign open Form Form1 object
End Sub
'Execute frm_Message Event, with Listener frm object as prefix
Private Sub frm_Message(str As String)
Me.Label0.Caption = str
End Sub
</pre>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjz0OyE58mYL2V1nCWTIBNw9ZFXVCIHhCRbPEMBUHLJLgIj-C-fJxROgADlcf41exNsKZ4YVhmkh4RrM8YIiQIZnR_oV8f3bHmpxPjO66PJSvQaQkHjefe1h1VlLAB9iers6raQNIqIiKUl6tics5aguhCLPfLLfyO3TuX13zLNJI9zRSkd-vobqervwQ/s551/EventForm2.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="405" data-original-width="551" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjz0OyE58mYL2V1nCWTIBNw9ZFXVCIHhCRbPEMBUHLJLgIj-C-fJxROgADlcf41exNsKZ4YVhmkh4RrM8YIiQIZnR_oV8f3bHmpxPjO66PJSvQaQkHjefe1h1VlLAB9iers6raQNIqIiKUl6tics5aguhCLPfLLfyO3TuX13zLNJI9zRSkd-vobqervwQ/s320/EventForm2.png" width="320" /></a></div>
<p>Check the line with the <i>WithEvents</i> keyword. This declaration line establishes a Form object named <b>frm</b> and assigns a reference to Form1’s Class Module, with the prefix Form_ (Form_Form1). In VBA or elsewhere, you cannot reference a form in this manner if the form lacks a Class Module.</p><p>The <b>WithEvents</b> keyword is known as a Listener (like a <span style="color: #cc0000;">Radio Receiver</span>) of Events that takes place on Form1.</p><p>Merely defining a form object with the WithEvents keyword, similar to a Dim statement, is not sufficient. We must initialise the <b>frm</b> object variable with the active Form1 (the RaiseEvent - <i>Transmitter</i>) object reference in memory.</p><p>In the Form_Load() Event Procedure, we accomplish this with the statement Set frm = Forms(“Form1”). However, if Form2 is opened before Form1, it will result in an error. To circumvent this issue, we’ve included an error trap line to ignore the error and proceed with the subsequent line of code.</p><p>The next Subroutine is our actual user-defined event's action-packed Code.</p><p>Every time we type a character in the text box on Form1; this will immediately appear on the Label Control on Form2.</p><p>The user-defined Event <b>Message()</b> will be fired every time we type a character in the Text box on Form1 and will be captured in Form2 and displayed in the Label Control.</p><p>Note: <i>Now, the <b>Form_Form1 </b>Module is a complete Object like a TextBox, with all the three Event Firing and Capturing mechanisms: Event, RaiseEvent, and the WithEvents (Event Capturing ability) added when Instatiated in Form2 Class Module. The Event Procedure Code must be written on the parent module (Form2 Module) of the Form1 Object Instance.</i></p>
</li><li><p>Save and Close Form2. Close Form1, if it is kept open. Let us test our user-defined Event Message().</p></li><li><p>Open Form1 in Normal View.</p></li><li><p>Open Form2 in Normal View and drag it away from Form1.</p></li><li><p>Type Hello World or anything you like in the TextBox on Form1. The typed text should appear in the Label control on Form2, each character as you type them in the Text box.</p>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgMFpTgEke0ar-xk1pXZjOeJCUCoIxWkkd_ADPsn6PYCyURpreGcMARMl2UO5_sRfuEHklxklDyoaBcx9FAaFJmZ17eZk-H3Y43CW5oGKKgktqM0AYNiWnT2Shsegju1BmChetWNCrFSxI0DaBKCaVbBYdtwUZjTfZ4RMOaDHrlQJFGErbGIZj8a8KiqA/s567/Form1Form2.png" style="display: block; padding: 1em 0px; text-align: center;"><img alt="" border="0" data-original-height="567" data-original-width="493" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgMFpTgEke0ar-xk1pXZjOeJCUCoIxWkkd_ADPsn6PYCyURpreGcMARMl2UO5_sRfuEHklxklDyoaBcx9FAaFJmZ17eZk-H3Y43CW5oGKKgktqM0AYNiWnT2Shsegju1BmChetWNCrFSxI0DaBKCaVbBYdtwUZjTfZ4RMOaDHrlQJFGErbGIZj8a8KiqA/s320/Form1Form2.png" /></a></div>
</li></ol>
<p>Hope you understood the concept of how an Event is defined in Form1 and how it is invoked and captured in Form2 and executes the related Subroutine Code in Form2 Module to do some task. </p><p>Note: On Form2, we monitor Form1 by establishing a reference to it in the <b>frm</b> object using the WithEvents keyword. When the Message Event is triggered (RaiseEvent) on Form1, the <b>frm</b> object associated with Form2 (a replica of the Form1 Module object) promptly captures it, and the corresponding subroutine prefixed with <b>frm_ </b>(Private Sub frm_Message()) is executed promptly.</p><p>You may try this or similar procedure with two different Forms to understand the relationship and logic of declaring Event, RaiseEvent, and WithEvents for capturing and Executing Event Procedure VBA Code.</p><p>Next week we will try how the <i><b>RaiseEvent</b></i> predefined Event of TextBox, like LostFocus is enabled at Run-time. </p>
<p>Download the Demo Database.</p>
<!--Download Link /downloads/2023/04/UserDefinedEvents.zip-->
<div class="separator" style="clear: both; text-align: center;">
<a href="https://drive.google.com/file/d/1whUnoYiYaf9lfxfUfgL8GUZo0KAu3kXQ/view?usp=share_link">
<img border="0" data-original-height="29" data-original-width="22" height="29" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFE4uDLH9dk3S6pEcfTkT6GW6r11XqBI4HQnLnhQFZMKFeeQt5ABV_NqvLHM68J7RacISsvbUEl0PlAq6V7i0t0mQF3iesrBuqGuvrf5CJV72OzrGZ7rdMxkxzw8FYlz-qniNySoqtqUN4/s320/zip.gif" width="22" />UserDefinedEvents.zip</a>
</div>
<!--Download Link /downloads/2023/04/UserDefinedEvents.zip-->
<ol>
<h3>MS Access Class Module Object Lessons for Beginners.</h3>
<li><a href="https://www.msaccesstips.com/2018/10/ms-access-class-module-and-vba.html" target="_Blank">MS-Access Class Module and VBA</a></li>
<li><a href="https://www.msaccesstips.com/2018/10/ms-access-vba-class-object-arrays.html" target="_Blank">MS-Access VBA Class Object Arrays</a></li>
<li><a href="https://www.msaccesstips.com/2018/10/ms-access-base-class-and-derived-objects.html" target="_Blank">MS-Access Base Class and Derived Objects</a></li>
<li><a href="https://www.msaccesstips.com/2018/11/vba-base-class-and-derived-object-2.html" target="_Blank">VBA Base Class and Derived Object-2</a></li>
<li><a href="https://www.msaccesstips.com/2018/11/base-class-and-derived-object-variants.html" target="_Blank">Base Class and Derived Object Variants</a></li>
<li><a href="https://www.msaccesstips.com/2018/11/ms-access-recordset-and-class-module.html" target="_Blank">MS-Access Recordset and Class Module</a></li>
<li><a href="https://www.msaccesstips.com/2018/12/access-class-module-and-wrapper-classes.html" target="_Blank">Access Class Module and Wrapper Classes</a></li>
<li><a href="https://www.msaccesstips.com/2018/12/wrapper-class-functionality.html" target="_Blank">Wrapper Class Functionality Transformation</a></li>
</ol>
<h3>Streamlining Form Module Code in Standalone Class Module.</h3>
<ol>
<li><a href="https://www.msaccesstips.com/2023/04/reusing-form-module-vba-code-for-new.html" target="_Blank">Re-using Form Module VBA Code for New Projects.</a></li>
<li><a href="https://www.msaccesstips.com/2023/04/streamlining-form-module-code-part-two.html" target="_Blank">Streamlining Form Module Code Part-Two</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/05/streamlining-form-module-code-part-three.html" target="_Blank">Streamlining Form Module Code Part-Three</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/06/streamlining-form-module-code-part-four.html" target="_Blank">Streamlining Form Module Code Part-Four</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/06/streamlining-form-module-code-part-five.html" target="_Blank">Streamlining Form Module Code Part-Five</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-six.html" target="_Blank">Streamlining Form Module Code Part-Six</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-seven.html" target="_Blank">Streamlining Form Module Code Part-Seven</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/07/streamlining-form-module-code-part-eight.html" target="_Blank">Streamlining Form Module Code Part-Eight</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part-nine.html" target="_Blank">Streamlining Form Module Code Part-Nine</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part-ten.html" target="_Blank">Streamlining Form Module Code Part-Ten</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/08/streamlining-form-module-code-part.html" target="_Blank">Streamlining Form Module Code Part-Eleven</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-report-module-code-in.html" target="_Blank">Streamlining Report Module Code in Class Module</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-report-module-in-class.html" target="_Blank">Streamlining Report Module Code in Class Module-2</a>.</li>
<li><a href="https://www.msaccesstips.com/2023/09/streamlining-form-module-code-part-14.html" target="_Blank">Streamlining Form Module Code Part-14:All Controls</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-custom-made-form-wizard.html" target="_Blank">Streamlining Custom Made Form Wizard-15</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-form-vba-custom-report.html" target="_Blank">Streamlining Custom Report Wizard-16</a></li>
<li><a href="https://www.msaccesstips.com/2023/10/streamlining-form-vba-external-files.html" target="_Blank">Streamlining Form VBA External File Browser-17</a></li>
<li><a href="https://www.msaccesstips.com/2023/11/streamlining-event-procedures.html" target="_Blank">Streamlining Event Procedures of 3D TextWizard-18</a></li>
<li><a href="https://www.msaccesstips.com/2023/11/streamlining-event-procedures-rgbcolor.html" target="_Blank">Streamlining Event Procedures RGB Color Wizard-19</a></li>
<li><a href="https://www.msaccesstips.com/2023/12/streamlining-numbers-to-words-converter.html" target="_Blank">Streamlining Event Procedures Number To Words-20</a></li>
</ol>
<p></p>a.p.r. pillaihttp://www.blogger.com/profile/02627590161919120214noreply@blogger.com1