Class Module to Handle Opening Forms Hierarchically

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • NeoPa
    Recognized Expert Moderator MVP
    • Oct 2006
    • 32633

    Class Module to Handle Opening Forms Hierarchically

    Overview

    This article shows how to set up and use a Class Module, as well as, more specifically, how to open forms such that they appear as a hierachical menu structure. Form A opens Form B and, while Form B is still open, remains hidden from view. As and when Form B terminates, Form A comes back into view and reassumes the focus.

    The code for classForm below includes examples of setting class properties as well as various methods. Of particular interest among the methods is the encapsulated event handler frmCalled_Close (), which is able to be triggered due to the WithEvents keyword used in line #19 (See code below).


    Module Code

    Code:
    Option Compare Database
    Option Explicit
    
    '21/1/2004  Added Private Set & Public Get code for frmTo.
    '21/9/2004  Removed ResumeTo functionality. _
                Now handled by the OnTimer() subroutine in the calling form _
                checking for (Visible) which indicates the called form is finished.
    '24/2/2005  Added function Uninitialised to show if instance of this object _
                has yet been initialised with the callers info. _
                It also checks this before it tries to open a new form.
    '31/3/2008  Added varOpenArgs as optional parameter to ShowForm.  Simply to be _
                passed directly to the opened form using DoCmd.OpenForm(). _
                Also set .OpenForm() to treat Cancel of the open as NOT an error.
    
    Private Const conUnInitMsg As String = _
                      "Object uninitialised - unable to show form."
    
    Private frmParent As Form
    Private WithEvents frmCalled As Form
    
    Public Property Set frmFrom(frmValue As Form)
        Set frmParent = frmValue
    End Property
    
    Private Property Get frmFrom() As Form
        Set frmFrom = frmParent
    End Property
    
    Private Property Set frmTo(frmValue As Form)
        Set frmCalled = frmValue
    End Property
    
    Public Property Get frmTo() As Form
        Set frmTo = frmCalled
    End Property
    
    'Uninitialised returns True if frmFrom not yet initialised.
    Public Function Uninitialised() As Boolean
        Uninitialised = (frmParent Is Nothing)
    End Function
    
    'ShowForm opens form strTo and hides the calling form.  Returns True on success.
    Public Function ShowForm(strTo As String, _
                             Optional strFilter As String = "", _
                             Optional varOpenArgs As Variant = Null) As Boolean
        ShowForm = True
        'Don't even try if caller hasn't initialised Form object yet
        If Uninitialised() Then
            ShowForm = False
            Call ShowMsg(strMsg:=conUnInitMsg, strTitle:="classForm.ShowForm")
            Exit Function
        End If
        Call DoCmd.Restore
        'Handle error on OpenForm() only.
        On Error GoTo ErrorSF
        Call DoCmd.OpenForm(FormName:=strTo, _
                            WhereCondition:=strFilter, _
                            OpenArgs:=varOpenArgs)
        On Error GoTo 0
        Set frmTo = Forms(strTo)
        frmFrom.Visible = False
        Exit Function
    
    ErrorSF:
        ShowForm = False
        ' If open is cancelled (either by user or code) then simply exit
        If Err.Number <> 2501 Then _
            Call ErrorHandler(strName:=strTo, _
                              strFrom:=frmFrom.Name & ".ShowForm", _
                              lngErrNo:=Err.Number, _
                              strDesc:=Err.Description)
    End Function
    
    '************************* Contained Object Method(s) *************************
    'For these subroutines to be activated the contained object must have the
    ''On Close' property set to a valid subroutine within the contained object.
    Private Sub frmCalled_Close()
        frmFrom.Visible = True
        Call DoCmd.Restore
        Set frmTo = Nothing
    End Sub
    '******************************************************************************

    Calling and Using Code

    Code:
    Option Compare Database
    Option Explicit
    
    Private clsTo As New classForm
    
    Private Sub Form_Open(Cancel As Integer)
        Set clsTo.frmFrom = Me
    End Sub
    
    Private Sub cmdWhatever_Click()
        Call clsTo.ShowForm(strTo:="frmWhatever")
    End Sub
    
    Private Sub cmdExit_Click()
        Call DoCmd.Close
    End Sub
    
    Private Sub Form_Close()
        'Method must exist in order for container to handle event.
    End Sub
    Line #4 indicates the declaration for the classForm object.
    Line #7 indicates the basic setting up of the object. This tells the class which form is the caller for returning to after any called form terminates.
    Line #11 indicates calling another form. At this point in the code the class takes over and hides the calling form as well as showing the called form.
    Lines #18 through #20 indicate a stub of an event handler that must exist if the class is to be able to capture the called form terminating. This is necessary for the class to re-show the caller form.
    Last edited by NeoPa; Sep 27 '11, 02:52 PM.
  • jimatqsi
    Moderator Top Contributor
    • Oct 2006
    • 1288

    #2
    That looks like a very useful class. Thanks for the post. :)

    Comment

    • Darren Bartrup
      New Member
      • May 2018
      • 3

      #3
      Shouldn't a couple of the properties in the class module be Public rather than Private?
      Code:
      Private Property Get frmFrom() As Form
      will stop you being able to use
      Code:
      Set clsTo.frmFrom = Me
      Last edited by Darren Bartrup; May 1 '18, 09:35 AM. Reason: Code formatting.

      Comment

      • NeoPa
        Recognized Expert Moderator MVP
        • Oct 2006
        • 32633

        #4
        Are you sure?

        I haven't got a copy to hand ATM but it works so I'm not sure what you say makes sense.

        If you can show it doesn't work then I'll happily spend the time looking into it, but if you're just confused and want me to do the checking for you then that doesn't work for me.

        Comment

        • Darren Bartrup
          New Member
          • May 2018
          • 3

          #5
          Sorry, I should have worded it differently - no, not confused as such just not checking how the code works properly.

          My confusion resulted from Private Property Get frmFrom() As Form being declared as Private which caused the intellisense not to suggest frmFrom when I added the Form_Open event.

          frmTo is only used within the class module though, so can be Private. This then raises the question of whether it is the better practice to use frmFrom(Property)or frmParent(Member Variable?) within the class - I've no idea, and may be beyond the scope of this question?

          Either way - the code works as intended.

          Hopefully that made sense.
          Last edited by Darren Bartrup; May 3 '18, 03:23 PM. Reason: Added that the code does work.

          Comment

          • NeoPa
            Recognized Expert Moderator MVP
            • Oct 2006
            • 32633

            #6
            Originally posted by Darren Bartrup
            Darren Bartrup:
            This then raises the question of whether it is the better practice to use frmFrom(Propert y)or frmParent(Membe r Variable?) within the class - I've no idea, and may be beyond the scope of this question?
            Not beyond the scope. I'm very happy to answer that.

            With OOP (Object Oriented Programming) it's very much about boundaries. Manipulating {Instance}.{Pro perty} is generally frowned upon, as this allows the using process uncontrolled access to the class instance. Generally speaking, in OOP, where a class designer wants a using process to have access to elements of the class instance they will (should) provide Property Gets and Property Sets to support that controlled access.

            Obviously, it's not always strictly necessary, but it's generally considered good practice, or at least so I understand. Particularly when there is any desire to restrict how the values can be set. I really can't claim to be the best expert at OOP mind you.

            Comment

            • Darren Bartrup
              New Member
              • May 2018
              • 3

              #7
              That makes perfect sense, thanks for the pointers.

              Comment

              • NeoPa
                Recognized Expert Moderator MVP
                • Oct 2006
                • 32633

                #8
                Originally posted by Darren Bartrup
                Darren Bartrup:
                Thanks for the pointers.
                No. They're Class Instances! (J/k).

                Glad I was able to help :-)

                Comment

                Working...