[AccessD] Framework Discussion - Classes - Supervisors

John W. Colby jwcolby at colbyconsulting.com
Sat Mar 6 16:22:51 CST 2004


We have discussed why we would use classes and seen demos of text box code
running natively in a form, then the equivalent written as a control class
for a text box.  We then looked at these classes dimensioned in the form
header and instantiated directly and using class factory functions.  The
next step is what I call the supervisor class.  Supervisor classes are
classes that load and “supervise” other classes.

Notice that the form’s built in class is doing a lot of work setting up
these control classes.  It will only get worse as we add new control type
classes for combo boxes, check boxes etc.  and then try to load a class
instance for each of 50 controls.  All of the code we saw in frmPeople4 will
have to be replicated over and over in each form you want to build.  So why
don’t we build a form class of our own and place this code in our class.
Then we just set up and tear down that supervisor class in each form.
Basically all we do is transfer the code we have in the form into a class,
with an Init() and Term() method to call for setting up and tearing down our
new class form.

OK, so the code for the new dclsFrm (form class) can be seen in dclsFrm in
the demo database and looks like this (comments in-line):
The dclsFrm form class (Supervisor)

Option Compare Database
Option Explicit

By now you should be getting used to class headers.  Here we dimension the
Event Procedure string, the collection and the form variable.  Notice that
we are declaring the form variable WithEvents meaning that this class will
be sinking form events inside this class

Private Const mstrEventProcedure = "[Event Procedure]"  'A constant to hold
the string [Event Procedure]

Private mcolClasses As Collection
Private WithEvents mfrm As Form

We haven’t seen this yet.  All classes may have an Initialize and Terminate
event which are run automatically as the class opens.  It is roughly the
equivalent of a form’s Open and Close events.  I use them to run the set
statements for all objects that the class will use, just to get these
statements grouped together and run.

Private Sub Class_Initialize()
    Set mcolClasses = New Collection
End Sub

In the Terminate of the class we call the classes Term() method which the
developer writes.

Private Sub Class_Terminate()
    Term
End Sub
The init simply takes the passed in pointer to the form and stores it in our
local variable.  We now have a pointer to the form so we can call
FindControls to iterate the controls control collection and load the control
classes.  And finally, set the form’s OnClose property so that we can sink
the Close event in this class.

Function Init(lfrm As Form)
    Set mfrm = lfrm
    FindControls
    mfrm.OnClose = mstrEventProcedure
End Function

Term calls ClsDestroy which cleans up the control classes, then sets out
pointer to the form to nothing.  Forgetting to clean up pointers to
collections and controls is one of the prime reasons for memory leaks and
Access failing to close.

Function Term()
On Error Resume Next
    ClsDestroy
    Set mfrm = Nothing
End Function

The form’s Close event will transfer control to this event sink, where we
call this class’ term event to clean up our pointers

Private Sub mfrm_Close()
    Term
End Sub

The class factory we saw in “Classes – why would we use them”.   It simply
creates an instance for the text box passed in and stores the pointer to
that class in the collection.

Function ClassFactory(txt As TextBox)
Dim ldclsCtlTextBox As dclsCtlTextBox
    Set ldclsCtlTextBox = New dclsCtlTextBox
    ldclsCtlTextBox.Init txt
    mcolClasses.Add ldclsCtlTextBox, txt.Name
End Function

Class destroy cleans out the control class collection, calling term of each
control class then destroying the pointer to that class.

Function ClsDestroy()
Dim obj As Object
    On Error Resume Next
    For Each obj In mcolClasses
        obj.Term
        Set obj = Nothing
    Next obj
    Set mcolClasses = Nothing
End Function
FindControls is the major enhancement that the dclsFrm brings to the table.
This function iterates the forms controls collection.  It examines each
control for its control type and instantiates a class instance for each
control.  The type of control class instantiated depends on the control’s
controltype property.  So far we only have a control class for text boxes
but we will soon be adding more control classes for combos, check boxes,
lists and so forth.  This function will then be used to load those other
control classes as well.

'
'THIS FUNCTION SEARCHES THE FORM FOR CONTROLS OF VARIOUS TYPES.
'
'The framework will build functionality using controls with consistant
naming.  We can
'find these controls simply by searching the form's control collection
looking for
'controls named something specific.  In other cases we might want to load a
class
'to handle a specific type of control - perhaps a text box class or a
dependent combo
'class.
'
'This function will be used to do the search through the form's control
collection
'looking for controls that we know how to handle and setting up the hooks to
handle
'those controls
'
'Parameters:
'Created by: Colby Consulting
'Created   : 4/26/98 10:23:44 AM
Private Sub FindControls()
On Error GoTo Err_FindControls
Dim ctl As Control
Dim intIndex As Integer
Dim col As Collection

    Set col = New Collection

    For Each ctl In mfrm.Controls   'Find each control in the form's control
collection
        With ctl
            Select Case .ControlType    'Determine it's type
            Case acTextBox  'Find all text boxes and load class to change
backcolor
                mcolClasses.Add New dclsCtlTextBox, .Name
                mcolClasses(.Name).Init ctl
            Case acSubform
            Case acTabCtl   'tab pages are handled in the tab control
            Case acOptionGroup
            Case acCheckBox
            Case acOptionButton
            Case acCommandButton
            Case acToggleButton
            Case acListBox
            Case acComboBox
            Case Else
            End Select
        End With
NextCtl:
    Next ctl
Exit_FindControls:
    On Error Resume Next
    Set ctl = Nothing
Exit Sub

Err_FindControls:
   Select Case Err
   Case 0     'insert Errors you wish to ignore here
      Resume Next
   Case Else   'All other errors will trap
      Beep
      MsgBox Err.Description, , "Error in function Forms.FindControls"
   Resume Exit_FindControls
   End Select
   Resume 0 'FOR TROUBLESHOOTING
End Sub

The form’s built-in class
That takes care of the dclsFrm form class.  To use this class we modify our
form’s built-in class as follows – See frmPeople6:

Option Compare Database
Option Explicit

Dimension our new form class

Public fdclsFrm As dclsFrm

Set it and initialize it, passing a pointer to the form

Private Sub Form_Open(Cancel As Integer)
    Set fdclsFrm = New dclsFrm
    fdclsFrm.Init Me
End Sub

If you remember the first form with the control handling built into the form
’s class you can see we have dropped the total code resident in our form by
a huge amount.  Additionally, should we have 1 or 100 controls on the form,
the generic processing code on the form doesn’t expand by even a single line
of code.  Now your form can concentrate on functionality that is specific to
the application rather on functionality that is generic to all applications.

The dclsFrm is one of a handful of supervisor classes we will look at as we
work on the framework.

We now have what could be called a minimal framework.  We have a form class
that you can build upon to hang form functionality on, and we have one
control class dctlTextBox that demonstrates the concept of the control
scanner in dclsFrm loading control classes for us automatically.  This class
can have additional functionality added to it to allow it to automatically
perform other generic functionality for our applications.  In fact we will
be adding more functionality to both of these classes in addition to adding
other controls classes.  More importantly though, we need to get a framework
foundation established, which will take the shape of another supervisor
class which I call clsFW.

Note: frmPeople5 uses the form’s built-in class’ Close event to cleanup the
pointer to dclsFrm which causes a page fault.  I just wanted to point out
that you can’t do this in Access 2K, and that this is a bug in Access that
was fixed in AccessXP.

John W. Colby
www.ColbyConsulting.com





More information about the AccessD mailing list