jwcolby
jwcolby at colbyconsulting.com
Wed Feb 18 04:35:55 CST 2009
It is often useful to expose child classes to parent classes. clsFrm is a base class which is responsible for among other things scanning all of the controls on the form and automatically loading a class for each control for which a class is available. clsFrm will be manipulated from the parent form’s class in order to program clsFrm itself and also the clsCtlXXX classes that clsFrm loads. In order to allow the developer to program the various classes, we need to expose the control classes. That will be the subject of this lecture. Before we get into the code I want to discuss class organization. I like to organize my classes such that I have areas or regions of the class where I know to look for specific parts of the class. Just like any other organizational task, organizing the class makes your programming more efficient. Classes can get quite large, and knowing where in the class to go to look for specific pieces simply makes the maintenance of the class easier. • The Header. The top of every class is called the header and I like to place constants first in the header, then variables, then Event declarations. • Directly underneath the header I like to place the Class_Initialize and Class_Terminate event stubs. For whatever reason, if you create these event stubs late in the process of building a class, the editor will place them down at the end of the class. I simply cut and paste them to the top. • Directly underneath Class_Terminate I place mInit() • Underneath mInit() I place all properties. Properties consist of Property Get / Let / Set statements, and occasionally functions that I “label” as a property (rare, but there). I always start the property name with p (lower case). If the property will return a class pointer I start the property name with a lower case c. I start a property name with a lower case col if it returns a collection. I do this simply because I like my properties and methods to group in intellisense and I also can simply read the property name and know that it will return a variable, or a class or a collection. • Underneath the properties I place the object event sinks if any. • Underneath the event sinks I like to group private methods of the class, which I prefix with a lower case m. • Underneath the private methods I like to group the public methods, also prefixed with a lower case m. These conventions are my own and I do not expect you to adopt them, though you may if you wish. I have just found after years of programming that these conventions help me in my organization. With that out of the way… • Open the demo database. • Open clsFrm • Add the following code to clsFrm directly underneath mInit(): Property Get cCtlCbo(strCboName As String) As clsCtlCbo Set cCtlCbo = mcolCtls(strCboName) End Property Property Get cCtlTxt(strCboName As String) As clsCtlTxt Set cCtlTxt = mcolCtls(strCboName) End Property • Save the class. These two properties expose combo and text box classes that are held in mcolCtls. We pass in the text name of the control to the property. Remember that the control scanner placed the clsCtlXXX instances into colCtls keyed on the control name so using the name we can index into the collection and grab the pointer to the correct class, and return that class pointer. Notice that I defined the properties to return clsCtlCbo and clsCtlTxt. Doing so allows the developer to directly use the methods and properties of the corresponding class without having to dimension a variable to store the class up in the form class. In other words we can directly program the clsCtlXXX simply by calling the correct property, passing in the correct name of a control and then manipulating the properties of the returned class. I will demonstrate that next. • Open frmDemoCtls in design view and immediately save as frmDemoCtls2. • In the form class delete the existing code and insert the following code: Public fclsFrm As clsFrm Private Sub Form_Open(Cancel As Integer) Set fclsFrm = New clsFrm fclsFrm.mInit Me With fclsFrm End With End Sub Notice that we now have a clean form with just fclsFrm to manipulate. Underneath fclsFrm.mInit we have inserted a With block to allow us to directly manipulate fclsFrm. Inside of the With block type in the following code. Please do the typing yourself as I want you to experience the Intellisense response. .cCtlCbo("Combo11"). Notice that as soon as you type in the trailing . intellisense presents you with properties of a specific instance of clsCtlCbo – the instance for combo11. Type in the following code: .cCtlCbo("Combo11").Ctl. Again notice that as soon as you type in the trailing . intellisense presents you with the properties of the wrapped control for that specific clsCtlCbo. Type in the following code: .cCtlCbo("Combo11).ctl.Name You have just asked for the name of the control contained in the clsCtlCbo instance for Combo11. Hit debug / compile. You should get a compile error because you are not doing anything valid with the name property that you requested. Add debug.print in front of this line of code like this: Debug.Print .cCtlCbo("Combo11").ctl.Name Now… until you actually run this code you cannot be sure that you will get a correct result, simply because you requested stuff for combo11. If combo11 does not exist you will get a run time error when cCtlCbo tries to index into the collection. Compile / save and open the form. Walk through the same process using the following code: Debug.Print .cCtlCbo("Combo11").cCtlLbl.ctl.Name Notice that you have full intellisense as you type in the code – the properties for cCtlCbo, then the properties for cCtlLbl, then the properties for the label control. We have full intellisense because we declared the property as returning the specific class or object. For example in clsFrm: Property Get cCtlCbo(strCboName As String) As clsCtlCbo Set cCtlCbo = mcolCtls(strCboName) End Property Returns a clsCtlCbo, and thus we can use intellisense to browse all of the properties of clsCtlCbo. In the future as we add new control classes to the system, we will come into clsFrm and build a new property to expose each additional control wrapper class by its type. This will ensure easy and efficient programming back in the parent form class. In this lecture we have designed a method of exposing the classes that the scanner in clsFrm created and placed into mcolCtls. We created a property for each type of clsCtlXXX such that the property returns that specific class type. We then built code back in the frmDemo to demonstrate the ease of programming that intellisense will provide because we have a property for each class type. Because there are a fixed number of controls provided by Access, there will always be a limited number of control wrappers and thus having a system to explicitly expose each class type makes good sense. -- John W. Colby www.ColbyConsulting.com