[AccessD] Framework Discussion - Using System Variables

John W. Colby jwcolby at colbyconsulting.com
Tue Mar 23 23:34:06 CST 2004


The demo code for this lecture can be found on my site.  Click the C2DbFW3G
button, then the C2DbFW3G-DemoCtlClassV6.zip link.

Framework Discussion – Using System Variables
Now that we have System Variables available to control program execution it
is time to demonstrate how we use this and how useful it can be.  First
though, I need to display the new code to the Framework class that loads,
cleans up and allows access to the SysVar class.

The first thing we do is add a private variable in the header of
clsFramework

Private mclsSV As clsSysVars

We set the variable instance in clsFramework’s Initialize method.

Private Sub Class_Initialize()
    Set mclsSV = New clsSysVars
End Sub

We do our cleanup in Term().

Public Sub Term()
    mclsSV.Term
    Set mclsSV = Nothing
End Sub

Then we create a function to return a pointer to the framework’s SysVar
class instance.  Notice that while we may eventually have a handful of
SysVar classes instantiated to handle other functionality, the one for the
framework is a special case used throughout the Framework and thus has its
own variable and methods in clsFramework.

'
'Get a pointer to the FRAMEWORK'S SV class instance
'
Public Function cSV() As clsSysVars
    Set cSV = mclsSV
End Function

We also build a method for clsFramework to return framework SysVars.

'
'Return SysVars from the Framework's SysVar class
'
Public Function SV(strSVName As String) As Variant
    SV = mclsSV.SV(strSVName)
End Function

Having done that we can go to the debug window, initialize the framework,
then call the framework SV method and get a specific SysVar from the
framework.

FWInit
?fw.SV("gCtlDteFormat")
dd/mm/yyyy
We have demonstrated that the framework is initializing the SysVar class for
the Framework itself and that we can now access this SysVar class instance
through methods of the Framework.

Demonstrating SysVar Usage
Having SysVars available to control program flow, we can now start putting
then to use.  One of the uses discussed on the AccessD list was to determine
the data type of a bound control and use that knowledge to set a text boxes
format and input mask strings.  This allows us to set these properties at
the framework level rather than having to go through the application setting
them one by one.  In order to do this I added code in the control scanner of
the framework to get the data type of the field a control is bound to.

First I create a dao recordset variable in dclsFrm’s header.

Private mrst As DAO.Recordset

In the control scanner I create an integer variable to hold the data type,
then set the class’ recordset to the form’s Recordset clone.

Dim intCtlData type As Integer
    Set mrst = mfrm.RecordsetClone

Inside the loop that scans all of the controls I add code to get the data
type of each control.

For Each ctl In mfrm.Controls   'Find each control in the form's control
collection
        With ctl
            On Error Resume Next
            intCtlData type = mrst.Fields(ctl.ControlSource).Type

Now that I know that, I have to be able to use it.  In each control class
for data aware controls I added a new private variable in the class header.

Private mintData type As Integer                         'The data type of
the field the control is bound to

And added a new parameter to each of these class’ init().  This code is for
the text box class.  Inside the Init we save the passed in value so that it
is available to the class.

Public Sub Init(ByRef robjParent As Object, lfrm As Form, ltxt As TextBox,
lintData type As Integer)
    mintData type = lintData type
    If mintData type = dbDate Then
        If FW.SV("gCtlDteFormatUse") Then
            mtxt.Format = FW.SV("gCtlDteFormat")
        End If
        If FW.SV("gCtlDteMaskUse") Then
            mtxt.InputMask = FW.SV("gCtlDteMask")
        End If
    End If

Now we can set the format and inputmask properties of the control to match
what is in the SysVar.   Notice I have a SysVar that says use the format,
and another that specifies a format.  We can turn on/off using the format
simply by setting the gCtlDteFormatUse variable True/False.  We then change
the format itself by changing the gCtlDteFormat SysVar.  Likewise we can do
the same thing for the mask.

This is still a somewhat simplified example but demonstrates what is
possible using the SysVars.  The next thing I want to demonstrate is
overriding a SysVar at the form level.
Form level SysVar overrides
It occasionally happens that certain behaviors need to be set for specific
forms but not others.  In order to do this we need to be able to have the
form class load SysVars that apply to its behaviors or control behaviors,
then look for the form’s name in the SysVar name.

For example, we might have a SysVar named gCtlDteMaskUse.  The form class
has a function that loads this SysVar into a variable in the class so that
its controls can poll the form class to decide whether to use a date mask.
However if also looks for a SysVar with the same name but the form’s name at
the end – gCtlDteMaskUsefrmPeople.

As forms load, each look for its own name in the each SysVar, but only
frmPeople finds its name embedded in a SysVar.  It strips its name out of
the SysVar and uses the remainder of the SysVar name to set that SysVar
value.  Thus only that specific form gets an “override” of that SysVar.
This will be a little easier to see as I go through the code.

Please remember that this is not a science by any means.  Since I have never
talked to anyone else doing this in their framework, there are undoubtedly
other or better ways to do this, and other things that could be used with
this idea.  Any positive suggestions on the implementation are appreciated.

In fact I have never been particularly happy with my implementation since it
required adding a new variable to the form class header for each new
behavior / property I want to control, and a new Property Get to read that
variable out, at least for those properties where a control will be using
the SysVar.  As always happens, I started implementing these and ended up
with many additional variables in the class header and many additional
property gets for the class just to handle the form / control SysVars.

I am looking at throwing all of these into a collection keyed on the base
SysVar name.  That would allow a single collection to hold all form /
control specific SysVars and a single property get to read any SysVar in the
collection, allowing extremely easy expansion of the system as new SysVars
are needed in the form / controls.

To implement this functionality we add a collection variable in the header
of dclsFrm.

'+SysVar overrides
Private mcolSysVars As Collection           'A collection to hold all the
form / control specific SysVars
'-SysVar overrides

Initialize it in the class’ Initialize.

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

In Init() call a function to read the SysVars we want to use.

Public Sub Init(ByRef robjParent As Object, lfrm As Form)
    'Read all of the SysVars specific to forms / controls
    ReadSysVarBehaviorEnbls
End Sub

Cleanup in Term()

Public Sub Term()
    Set mcolSysVars = Nothing
End Sub

ReadSysVarBehaviorEnbls reads (or tests for) exactly the SysVars that we
need for form / control handling.

'The system variables can turn On and Off behaviors globally - for example
combo dbl-click handling.
'However the developer may wish to override that behavior for certain forms.
In order to accomplish
'this the form has to search the SysVar tables for specific switches defined
at framework design time
'and handle them appropriately
'
'gCboDblClick - Gobal Dbl-Click turns on / off dbl-click handling for
combos.  May be over-ridden at the form level.
'gCboNotInList - Global NotInList turns on / off NotInList handling for
combos.  May be over-ridden at the form level.
'gCboEnter - Global Enter turns on / off Enter AND EXIT event handling for
combos.
'
'fCboDblClickFrmClient - an example of an override - frmClient handles
CboDblClick differently that the rest
'
Private Function ReadSysVarBehaviorEnbls()
On Error GoTo Err_ReadSysVarBehaviorEnbls
    'The call to the function SysVarBehaviorEnbl requires the name of the
SysVar minus the leading g, f, etc.
    SysVarBehaviorEnbl "CboDblClick"
    SysVarBehaviorEnbl "CboNotInList"

    SysVarBehaviorEnbl "EnblCtlBackGndColorChg"
    SysVarBehaviorEnbl "CtlBackGndColor"
    SysVarBehaviorEnbl "EnblLblBackGndColorDblClk"
    SysVarBehaviorEnbl "LblBackGndColorDblClk"

    SysVarBehaviorEnbl "CtlValidateBackGndColor"

    SysVarBehaviorEnbl "CtlDteFormat"
    SysVarBehaviorEnbl "CtlDteMask"
    SysVarBehaviorEnbl "CtlDteFormatUse"
    SysVarBehaviorEnbl "CtlDteMaskUse"


    SysVarBehaviorEnbl "PrpFrmCloseButton", mfrm.CloseButton, True
    SysVarBehaviorEnbl "PrpFrmMinMaxButtons", mfrm.MinMaxButtons, True
    SysVarBehaviorEnbl "PrpFrmControlBox", mfrm.ControlBox, True
    SysVarBehaviorEnbl "PrpFrmBorderStyle", mfrm.BorderStyle, True

Exit_ReadSysVarBehaviorEnbls:
    On Error Resume Next
Exit Function
Err_ReadSysVarBehaviorEnbls:
        MsgBox Err.Description, , "Error in Function
dclsFrm.ReadSysVarBehaviorEnbls"
        Resume Exit_ReadSysVarBehaviorEnbls
    Resume 0    '.FOR TROUBLESHOOTING
End Function

And finally SysVarBehaviorEnbl performs the magic of finding the SysVar,
checking for an override etc.

'
'This function takes a passed in variable and the base name of a
systemvariable, and builds up the
'global or form name, looks for the SysVars and sets the variable
accordingly.
'
'The global switch starts with the character g, then the SysVar name
'the form switch starts with the letter f, then the SysVar name, then the
name of the form
'
'For example, in the SysVar table we have 'gCboDblClick' which if set turns
on combo dbl-clicks globally
'however a form can override the global if there is an 'fCboDblClickXXXXX'
where XXXX is the name of the form
'
'This function basically just enforces a strict naming convention and wraps
it all in an easy to use function
'
Private Function SysVarBehaviorEnbl(strSysVarName As String, Optional
varVariable As Variant, _
                                    Optional blnIsFrmProperty As Boolean =
False)
On Error GoTo Err_SysVarBehaviorEnbl
Dim var As Variant
Dim varTemp As Variant

    'get the global enable if any
    var = FW.SV("g" & strSysVarName)
    If Not IsNull(var) Then varVariable = var
    'add the name of this form to 'fCboDblClick' to see if there's an
override for this form specifically
    varTemp = FW.SV("f" & strSysVarName & mfrm.Name)
    If Not IsNull(varTemp) Then
        var = FW.SV("f" & strSysVarName & mfrm.Name)
    End If
    '
    'If this is a form property handle it as such.
    '
    If blnIsFrmProperty Then
        varVariable = var
    Else
        '
        '
        'Otherwise just add it to the SysVar collection keyed to the
SysVarName
        If Not IsNull(var) Then
            mcolSysVars.Add var, strSysVarName
        End If
    End If
Exit_SysVarBehaviorEnbl:
    On Error Resume Next
Exit Function
Err_SysVarBehaviorEnbl:
        MsgBox Err.Description, , "Error in Function
dclsFrm.SysVarBehaviorEnbl"
        Resume Exit_SysVarBehaviorEnbl
    Resume 0    '.FOR TROUBLESHOOTING
End Function

We now have a single Property Get SV() to retrieve any SysVar that is loaded
into the form’s SysVars collection.

'
'This property reads a SysVar if it exists in mcolSysVars,
'or returns a null if not in the collection
'
Property Get SV(strSVName As String) As Variant
    On Error Resume Next
    SV = mcolSysVars(strSVName)
    If Err <> 0 Then
        SV = Null
    End If
End Property

Now we can add new SysVars for form / control use simply by adding a single
line of code in ReadSysVarBehaviorEnbls which allows much easier
modifications to the form and control classes to use SysVar control.
Form Properties
Another use for SysVars and form level overrides is setting form properties
through SysVars.  Since properties are just some value – true/false, an
integer etc. – we can if we desire set specific form properties using
SysVars.  If the property name exists then that value gets loaded in that
property of any form that loads, or for a specific form or forms.  I never
really explored this idea fully but I implemented it in case I ran into a
use for it.

Just remember that some properties are only settable in design view, and
these we cannot affect using SysVars.  Things like the CloseButton property,
MinMaxButton etc can be set through VB and thus can be controlled by SysVars
if you find that useful.

The Syntax is simply:

    SysVarBehaviorEnbl mfrm.CloseButton, "PrpFrmCloseButton"
    SysVarBehaviorEnbl mfrm.MinMaxButtons, "PrpFrmMinMaxButtons"
    SysVarBehaviorEnbl mfrm.ControlBox, "PrpFrmControlBox"
    SysVarBehaviorEnbl mfrm.BorderStyle, "PrpFrmBorderStyle"

Control SysVars
Controls obviously need to be able to read SysVars to modify their behaviors
as well, as we saw demonstrated at the beginning of this lecture.  In order
to reduce complexity and place all form / control SysVar loads in a common
location I do the load of all Control SysVars in the form class as well.  By
exposing a SV property that can read any SysVar value back out of the form’s
SysVar collection, the controls can “poll” the form class for SysVars that
they need.  To demonstrate this we will modify the text box class to ask the
form class about the date mask.

In the form’s Init():

    If mintData type = dbDate Then
        If mobjParent.SV("CtlDteFormatUse") Then
            mtxt.Format = mobjParent.SV("CtlDteFormat")
        End If
        If mobjParent.SV("CtlDteMaskUse") Then
            mtxt.InputMask = mobjParent.SV("CtlDteMask")
        End If
    End If

To see the effects of using the SysVar, simply select the HOUSAC WATER
QUALITY DISTRICT company, then notice that the DOB field is formatted per
the gCtlDteFormat in usystblFWSysVars.  You can turn on/off this
functionality by setting gCtlDteFormatUse True or False as you desire, then
in the debug window calling fw.csv.RefreshSysVars.  This calls the
framework, gets a pointer to the SysVars class using the framework’s csv
property, then calls the RefreshSysVars method of that class.
Summary
SysVars provide a powerful tool for modifying the Framework or Application
behaviors.  We can use generic behaviors, setting default behaviors that we
can then modify behaviors for an entire application, or on a form by form
basis.  The controls can modify their behaviors by calling the parent form
class’ SV method to get a SysVar value.

In the future we will be adding dozens of behaviors to the form class and
the various control classes.  Virtually all of these behaviors will be
programmable by setting up SysVars in usystblFWSysVars and then reading out
the SysVars in the form class.

Our code will then use these values to set control and label background
colors when behaviors are enabled, enable these various behaviors, enable
things like JIT subforms and so forth.

A framework is far more than a simple hammer.  Any time we program a
behavior that we have needed forever or are using over and over in our
applications, consider placing that behavior in the control or form class as
appropriate, then turn on / off that behavior with SysVars.  Suddenly you
will be able to offer options to the client literally at the “push of a
button” – or the setting of a SysVar.

To a man with a hammer, everything may very well look like a nail.  To a
developer with a framework and SysVars, the search and replace tool becomes
the last resort instead of the first.

John W. Colby
www.ColbyConsulting.com





More information about the AccessD mailing list