I’m building an Access database with classes e.g clsOrder, clsCustomer etc which manage the interface with tables. These classes create instances of forms when displaying their data. I found that once the execution of code was within one of these forms I couldn’t refer to the parent object that created it (so is there a better way of doing this? would be part of my question).
To deal with this I’m using a scripting dictionary to store instances of classes with a key using the ID of the class and a unique identifier for the class (e.g Order-3265). I then store a reference to the owner object in the form itself.
So when an object is created and its ID is known it puts a pointer to itself in the dictionary and gives that pointer to its form (hope that’s clear enough).
This then allows the form to interact with its owner class.
I’m using another class clsManager to do the adding of items to the Dictionary or retrieval or removal (with destruction).
Examples of classes - seriously cut down..
clsManager:
Public WorkingObjects As New Scripting.Dictionary
Public Function AddWorkingObject(key As String, ObjectType As Object) As Boolean
If Me.WorkingObjects.Exists(key) Then
Me.WorkingObjects.Remove key
Me.WorkingObjects.Add key, ObjectType
Else
Me.WorkingObjects.Add key, ObjectType
End If
End Function
Public Function GetWorkingObject(key As String) As Object
If Me.WorkingObjects.Exists(key) Then
Set GetWorkingObject = Me.WorkingObjects(key)
Else
Set GetWorkingObject = Nothing
End If
End Function
Public Function DestroyObject(obj As Object) As Boolean
Dim key As String
If Not obj Is Nothing Then
key = obj.DictionaryKey
If Me.WorkingObjects.Exists(key) Then
Me.WorkingObjects.Remove (key)
Set obj = Nothing
If obj Is Nothing Then
Debug.Print key & " destroyed"
Else
Debug.Print obj.DictionaryKey & " NOT destroyed"
End If
End If
Set obj = Nothing
End If
End Function
clsQuote:
Option Compare Database
Option Explicit
'use a form using an instance of this class to control manipulation of Quote records
'Loading and saving set default values if a null value is detected
Private Const scTABLE As String = "tblQuote"
Private intID As Long 'unique identifier
Private intCustomerID As Long
Private intSiteID As Long
Private rsQuoteTotalValues As DAO.Recordset
Private oCustomer As clsCustomer
Const ObjectType = "Quote-"
Private oEditForm As Form_frmQuote
Property Get EditForm() As Form_frmQuote
Set EditForm = oEditForm
End Property
Property Get ID() As Long
ID = intID
End Property
Property Let ID(QuoteID As Long)
intID = QuoteID
Me.EditForm.ID = QuoteID
End Property
Property Get Customer() As clsCustomer
Set Customer = oCustomer
End Property
Property Let CustomerID(ID As Long)
intCustomerID = ID
oCustomer.Load (ID)
EditForm.SiteID.RowSource = oCustomer.AddressSQL
EditForm.SiteID.Requery
EditForm.ContactID.RowSource = oCustomer.ContactsSQL
EditForm.ContactID.Requery
EditForm.CustomerID = ID
End Property
Property Get DictionaryKey() As String
DictionaryKey = ObjectType & CStr(Me.ID)
End Property
'END PROPERTIES//////////////////////////////////
Public Sub DisplayForm(Visibility As Boolean)
With Me.EditForm
.Visible = False
.subFrmQuoteSectionsSummary.SourceObject = ""
If Visibility = True Then
...some stuff...
.Visible = True
End If
End With
End Sub
Public Function Load(ID As Long) As Boolean
'On Error GoTo HandleError
Dim RS As DAO.Recordset
Dim sQry As String
Load = False
If Nz(ID, 0) <> 0 Then
sQry = "SELECT * FROM " & scTABLE & " WHERE ([ID]=" & ID & ");"
Set RS = Manager.DB().OpenRecordset(sQry, dbOpenForwardOnly)
With RS
If .RecordCount = 0 Then
MsgBox "Cannot find Quote with ID = " & ID, vbCritical
GoTo Done
End If
Me.ID = Nz(!ID, 0)
Me.CustomerID = Nz(!CustomerID, 0)
Manager.AddWorkingObject Me.DictionaryKey, Me
Me.EditForm.SetOwnerObject (Me.DictionaryKey)
.Close
End With
Set RS = Nothing
Load = True
End If
Done:
Exit Function
HandleError:
MsgBox "Error in Customer Load: " & vbCrLf & Err.Description, vbCritical
Resume Done
End Function
Private Sub Class_Initialize()
Debug.Print "Quote class initialized"
Set oCustomer = New clsCustomer
Set oEditForm = New Form_frmQuote
Me.ID = 0
Set oQuoteTidier = New clsClassTidier
Me.DisplayForm (False)
End Sub
Private Sub Class_Terminate()
Set oCustomer = Nothing
Set oEditForm = Nothing
Debug.Print "Quote class terminated"
End Sub
From the EditForm:
Option Compare Database
Option Explicit
'necessary for the object to have a reference to its owner in this manner to prevent circular reference
Private OwnerObject As clsQuote
Public Function SetOwnerObject(OwnerKey As String) As Boolean
SetOwnerObject = False
Set OwnerObject = Manager.GetWorkingObject(OwnerKey)
SetOwnerObject = True
End Function
Private Sub cmdClose_Click()
OwnerObject.EditForm.Visible = False
Manager.DestroyObject OwnerObject
DoCmd.Close acForm, Me.Name
End Sub
Each business object class (like ClsOrder) has an editForm instance which is loaded and hidden until required and a up to 3 DAO Recordsets that it keeps open.
I think all references to the business objects that are interrelated are pointers to the objects in the dictionary.
My problem is error 3035 exceeding system resources. I’ve checked objects are destroyed when not in use but repeatedly opening and closing objects gets me to error 3035.
So the question is- am I just asking Access to do stuff it can’t or would better programming fix it?
an editForm instance which is loaded and hidden until requiredis this required? why don't you make anewedit form if you need to edit/show data rather than keeping something hidden?Form_Unloadevent of the form, that fires when a user closes the form, but before the form object is actually destroyed. Keep the KISS principle in mind, I don't know the exacts of your project but you might be overcomplicating things.