Advanced Class Concepts

MajP

You've got your good things, and you've got mine.
Local time
Today, 09:26
Joined
May 21, 2018
Messages
8,922
Advanced Class Concepts

I have posted several threads on using custom classes to extend the capabilities of controls and make pseudo "user controls".

This thread has additional concepts.
 

Attachments

  • Unbound.jpg
    Unbound.jpg
    36.1 KB · Views: 340
Last edited:
So we're skipping "Classes 101" and jumping into the Graduate courses?
I'm still trying to wrap my head around the last demo.
 
The class concepts in this are actually very straight forward. A lot less complicated than the common controls. The complicated part is generic dao code to figure out the all the information needed. Using DAO and you where the field is and what table it is from then you can use DAO to find out a lot. Then you store these in your custom properties. Building a custom collection is relatively straight forward. Once you see it once they are always basically the same.
 
Lesson: Composition.
Composition is one of the most important aspects of Object Oriented Programming. If not familar with the term you are familar with the concept. A Composite Class is a class that has some properties which are also class objects. Almost everything in Access that you usually use is a composite class. It is like producing any kind of machine, you would have parts that make an assembly, and that is a sub assembly of a higher part, which in turn is a sub assembly of an even higher part ... Composite classes are a hierrarchy of organization.

A car manufacturer has suppliers for engines, seats, radios, lights etc ....
--- The engine manufacturer has suppliers for pistons, cranks, blocks, sparkplugs, valves
------ Piston manufacturer may buy piston rings from someone
---The seat manufacturer may have suppliers for seatbelts, cushions, and electronic controls
------ The seat belt manufacturer may have a supplier of latches, springs..

In Access you have a Form Class

Form has Recordset, Controls, ...
--- Recordset is composed of Fields, Properties, ...
------ Field has Attributes, Properties ...
--------- Attribute (lowest Level)
---Controls have Controls as well, could have Pages (if a tab)
------- Page (lowest level)

If you understand composition it makes VBA notation more understandble
Forms("Form1").Recordset ' returns Form1's recordset object
Forms("Form1").Recordset.Fields("firstName") ' returns a field object of the recordset
Forms("Form1").Recordset.Fields("firstName").Attributes("somevalue") ' returns an attribute value

In building a custom composite class I think from the top down and build the class from the bottom up, building the child classes first.
For the UnboundFormController I imagine it would be like a top level class "UnboundFormController" which would control a lot of the adding, deleting, and updating. It would need to have a reference to all the unbound controls in a custom collection "UnboundControls", and each UnboundControl object would keep information about the UnboundControl.
At the bottom of the hierarchy the UnboundControl needs to have a reference to the real control and knowledge of what field in what table it is related to. Working from the bottom I build the UnboundControl class and test it. Then build the UnboundControls class and test it by adding UnboundControls to an instance. Then build the final composite class UnboundFormController that has the UnboundControls as a property.
The first demo is the test putting together the lower two classes. This is testing the UnboundControls collection. The higher class UnboundFormController is not built.
 
Last edited:
Lesson: Custom Collection Classes, Psuedo Inheritance, Default Properties, and For Each Loop

Before showing the UnboundControl class and UnboundControls collection class I wanted to show how to do these with a very basic example.

First build a simple Pet class that has some simple properties Name, AnimalType, Age, Sound that they make. There are lets and gets for all of these. I also added another property called ToString. I do this in most classes as an easy way to print out information. The tostring property only has a Get. Then I added 1 method MakeSound. I almost always have a method called Initialize that lets you set all of the properties up front.



Code:
'------------------------- Class variables ----------------------

'Normally all class variables are private and you use Lets, Gets, and Sets accessors to retrieve and set.

'Common to start with m to mean 'member' of a class.

Private m_Name As String
Private m_AnimalType As String
Private m_AgeInYears As Integer
Private m_Sound As String

'---------------------------- My Initialize -------------------------------------------------

'Most languages have what is called a parameterized contructor for the class. Does not exist in vba
'This is my way of cheating and I do this on most classes, but not required.
'It is just a method to set a lot of properties



Public Sub Initialize(TheName As String, TheAgeInYears As Integer, TheAnimalType As String, TheSound As String)
  Me.Name = TheName
  Me.AgeInYears = TheAgeInYears
  Me.AnimalType = TheAnimalType
  Me.Sound = TheSound
End Sub

'--------------------------------Property Accessors: Let, Get, Set -------------------------------

Public Property Get AnimalType() As String
  AnimalType = m_AnimalType
End Property


Public Property Let AnimalType(ByVal TheType As String)
  m_AnimalType = TheType
End Property


Public Property Get Name() As String
   'Attribute Item.VB_UserMemId = 0
  Name = m_Name
End Property

Public Property Let Name(ByVal TheName As String)
  'Attribute Item.VB_UserMemId = 0
  m_Name = TheName
End Property

Public Property Get Sound() As String
  Sound = m_Sound
End Property


Public Property Let Sound(ByVal TheSound As String)
  m_Sound = TheSound
End Property

Public Property Get AgeInYears() As Integer
  AgeInYears = m_AgeInYears
End Property


Public Property Let AgeInYears(ByVal TheAgeInYears As Integer)
  m_AgeInYears = TheAgeInYears
End Property


'I add this in all classes simply for debuging. A property that returns as string of the other properties
'Notice there is not let procedure

Public Property Get ToString() As String
  ToString = "I am a:" & Me.AnimalType & ", My Name is:" & Me.Name & ", I am: " & Me.AgeInYears & ", The Sound I make: " & Me.Sound
End Property

'------------------------------- Class Methods ------------------------------------------

Public Sub MakeSound()
  MsgBox Me.Sound
End Sub

To test this method
Code:
Public Sub TestPet()
  Dim myPet As New Pet
  myPet.Initialize "Toto", 5, "Dog", "Bow Wow"
  myPet.MakeSound
  Debug.Print myPet.ToString
  Dim myPet2 As New Pet
  myPet2.Initialize "Flipper", 8, "Dolphin", "Squeak whistle"
  myPet2.MakeSound
  Debug.Print myPet2.ToString
  'Show default works do not need name
  myPet2 = "Tiger"
  Debug.Print myPet2
End Sub

Default Property:
Now here is a trick for making a default property, if that is something you would like. Most vba classes have defaults that you do not have to type. Controls it is Value, Forms it is Controls, a collection is Item
MyCollection.item(1) same as MyCollection(1)
MyControl.value same as MyControl

To do this in a custom class you have to set attributes for the compiler, but this can not be done in VBE. You have to export the class, edit it with a text editor, and then reimport. You will not see the attribute when you import.

I wanted to make the Name property the default. So I have to type this in the Let and Get. It has to be the first line
Attribute Item.VB_UserMemId = 0

Custom Collection Class:
Making a custom collection is often needed to manage your multiple instance of your collection. A custom collection has all the same properties of a normal collection (add, remove, count, item). I usually have 2 or more add methods giving some options. All custom classes look similar to this.

Code:
Private m_Pets As Collection

Public Function Add(TheName As String, TheAgeInYears As Integer, TheAnimalType As String, TheSound As String) As Pet
  'create a new Pet and add to collection
  Dim NewPet As New Pet
  NewPet.Initialize TheName, TheAgeInYears, TheAnimalType, TheSound
  m_Pets.Add NewPet, NewPet.Name
  Set Add = NewPet
End Function

Public Sub Add_Pet(ByVal ThePet As Pet)
  'I also add a second Add to allow you to build the object and then assign it
   m_Pets.Add ThePet, ThePet.Name
End Sub

Public Property Get Count() As Long
  Count = m_Pets.Count
End Property

Public Property Get NewEnum() As IUnknown
    'Attribute NewEnum.VB_UserMemId = -4
    'Attribute NewEnum.VB_MemberFlags = "40"
    'This is allows you to iterate the collection "For Each pet in pets"
   Set NewEnum = m_Pets.[_NewEnum]
End Property

Public Property Get Item(Name_Or_Index As Variant) As Pet
  'Attribute Item.VB_UserMemId = 0
  'Export the class and uncomment the below in a text editer to allow this to be the default property
  'Then reimport
  Set Item = m_Pets.Item(Name_Or_Index)
End Property

Sub Remove(Name_Or_Index As Variant)
  'remove this person from collection
  'The name is the key of the collection
  m_Pets.Remove Name_Or_Index
End Sub

Public Property Get ToString() As String
  Dim strOut As String
  Dim i As Integer
  For i = 1 To Me.Count
    strOut = strOut & Me.Item(i).ToString & vbCrLf
  Next i
  ToString = strOut
End Property


'----------------------------------------------- All Classes Have 2 Events Initialize and Terminate --------
Private Sub Class_Initialize()
'Happens when the class is instantiated not related to the fake Initialize method
'Do things here that you want to run on opening
  Set m_Pets = New Collection
End Sub

Private Sub Class_Terminate()
  'Should set the object class properties to nothing
  Set m_Pets = Nothing
End Sub

They will have one class property which is a collection. Private m_Pets As Collection
1. Add
The add function creates a new instance of Pet and then adds it to the collection. Notice it returns what was created so you could call it like:
set NewPet = myPets.add("Toto",5,"Dog","Bark")

2. Remove:
Pretty basic. It removes from the class collection.

3. Count.
Simple gives the count of items in the collection

4. Item.
Returns the item from the collection

5. Add_Pet
Sometimes you may create a pet object ahead of time and then want to add it to the collection, instead of passing in the values for the Add function.

6. ToString
Lets you print out all objects in a collection and returns that string

7. Get NewEnum
This only purpose is to allow you to do a for each. If you did not do it you would have to iterate like
dim i as integer
for i = 1 to myPets.count
....myPets.item(i)
next i
with this you can do
for each myPet in MyPets
...
next

Enabling For Each
Code:
Public Property Get NewEnum() As IUnknown
    'Attribute NewEnum.VB_UserMemId = -4
    'Attribute NewEnum.VB_MemberFlags = "40"
    'This is allows you to iterate the collection "For Each pet in pets"
   Set NewEnum = m_Pets.[_NewEnum]
End Property
Again you have to export this procedure and uncomment the attribute and reimport.
Noticed that I also made the Item property a default.
All classes have two events Initialize and Terminate. The event Initialize is not related to my fake constructor "Initialize". These happen when the object is created and destroyed. Use initialize to set start conditions. In terminate it is a good idea to set to nothing class objects and any cleanup.

Inheritance
Vba does not have inheritance. Inheritance lets you make a sub class and have it inherit the properties of the parent class. Imagine you wanted to create a dog class with more specialized properties and methods, but you wanted to use the current properties and methods of the Pet class. You would simply inherit instead of building all of those into your Dog Class. In VBA the work around is what I did here. Our custom collection wants to inherit from the collection class. To do this a collection is made a class property of the custom collection class. Similar to the other User Control classes were I take a listbox and add more properties and methods to make it into a find as you type listbox.

The test for the CustomClass
Code:
  Dim myPets As New Pets
  myPets.Add "Toto", 5, "Dog", "Bow wow"
  myPets.Add "Flipper", 8, "Dolphin", "Squeak whistle"
  'Using the other add method
  Dim myPet As New Pet
  myPet.Initialize "Dino", "100", "Dinosaur", "Roar"
  myPets.Add_Pet myPet
  Debug.Print "I have " & myPets.Count & "Pets"
  Debug.Print myPets.Item(1).Name
  'The default works on the collection
  Debug.Print myPets(2).Name
  'The default works on the Pet class and you can use key
  Debug.Print myPets("dino")
  For Each myPet In myPets
    myPet.MakeSound
  Next myPet
  Debug.Print myPets.ToString
  myPets.Remove ("toto")
  Debug.Print "I have " & myPets.Count & " pets"
  myPets(1) = "Tiger"
  Debug.Print myPets(1)
End Sub
 

Attachments

Users who are viewing this thread

Back
Top Bottom