Is Form being Loaded as a SubForm (1 Viewer)

ted.martin

Registered User.
Local time
Today, 10:11
Joined
Sep 24, 2004
Messages
743
I have a form that I use both as a continuous Form and a continuous SubForm. There is a Close button on this form except that I hide this button when the form is being loaded as a Sub-Form.

The way I do this is to use the cmdClose.Visible code is put in the Load event of the form a check to see if the Parent Form is Loaded.

However this code is becoming cumbersome as the form could be loaded in 3 or 4 separate instances as a SubForm.

I was wondering whether there was a simple way to check whether a form was being loaded as a subform or not.

I have thought about using some code based around Me.Parent.Name but that gives me a 2427 error. I can work with this but again wonder if there is a more elegant way?

Many thanks
 

ted.martin

Registered User.
Local time
Today, 10:11
Joined
Sep 24, 2004
Messages
743
Very helpful. This is what I have used.

Code:
Private Sub Form_Load()

On Error GoTo Err_Parent

Dim str As String
str = Me.Parent.Name   ' It will error at this point if No Parent exists
cmdClose.Visible = False


Exit_Parent:
Exit Sub

Err_Parent:

If Err.Number = 2427 Then
    cmdClose.Visible = True
    Resume Exit_Parent
End If

End Sub
 

isladogs

MVP / VIP
Local time
Today, 11:11
Joined
Jan 14, 2017
Messages
18,213
Or how about this? Replace MyFormName with your form name...!

Code:
If CurrentProject.AllForms("MyFormName").IsLoaded Then
      DoCmd.Close acForm, "MyFormName"
End If
 

arnelgp

..forever waiting... waiting for jellybean!
Local time
Today, 18:11
Joined
May 7, 2009
Messages
19,230
make a function out of it:


Code:
Public Function IsSubForm(ByVal obj As Form, Optional Ctl As Object) As Boolean
    On Error GoTo Err_Parent
    
    Dim str As String
    str = obj.Parent.name   ' It will error at this point if No Parent exists
    If Not Ctl Is Nothing Then _
        obj(Ctl.name).Visible = False
    IsSubForm = True

Exit_Parent:
    Exit Function

Err_Parent:
    If Not Ctl Is Nothing Then _
        obj(Ctl.name).Visible = True
    IsSubForm = False
    Resume Exit_Parent
End Function


now on the Load Event of the form/subform (Property->Event), just put:


= IsSubForm([Form], [cmdClose])
 

Gasman

Enthusiastic Amateur
Local time
Today, 11:11
Joined
Sep 21, 2011
Messages
14,261
Colin,
How does that detect it is loaded as a subform?

Or how about this? Replace MyFormName with your form name...!

Code:
If CurrentProject.AllForms("MyFormName").IsLoaded Then
      DoCmd.Close acForm, "MyFormName"
End If
 

Minty

AWF VIP
Local time
Today, 11:11
Joined
Jul 26, 2013
Messages
10,371
Did any of you see Markks solution? It was much simpler and tbh, imho more elegantly constructed...
 

isladogs

MVP / VIP
Local time
Today, 11:11
Joined
Jan 14, 2017
Messages
18,213
I understood it was being run from a subform which could be opened from several different parent forms...?
Anyway, I also like Mark's solution ....
 
Last edited:

Gasman

Enthusiastic Amateur
Local time
Today, 11:11
Joined
Sep 21, 2011
Messages
14,261
Where would the code be placed?
Code:
Private Property Get HasParent as boolean
On error goto handler
  HasParent = Typename(Me.Parent.Name) = "String"
  Exit Property
handler:
End Property
 

Uncle Gizmo

Nifty Access Guy
Staff member
Local time
Today, 11:11
Joined
Jul 9, 2003
Messages
16,280
check whether a form was being loaded as a subform or not.

I wouldn’t do it in each instance of the subform, I’d do it from the Main Form….

Also, I'm uncomfortable when an Error is triggered deliberately to make something happen. However, it can't be avoided all of the time and is sometimes a necessary evil.

In this particular case I can see a way which would avoid such an abuse of MS Access!

I would look at the problem from a different perspective.

There's no such thing as a “Sub-Form”! What you have on your “Main Form” is an ordinary form housed within a subform/subreport control, which I refer to as a subform window. The term “Subform” is a useful way of explaining what you see, however the term does tend to take your eye off the ball so to speak, because the form (Subform) isn’t actually on the Main Form it actually resides within the subform/subreport control.

There is a natural tendency to think about Controls, Forms, Objects as individual entities, however most things in MS Access are members of a collection. There's no exception in this case, your command button is one of the controls in the controls collection of the form it’s on. The form itself is housed within the subform window. It’s not necessary to refer to this form by its full name, you just refer to it as an object, as the object contained within that subform window a “Form”

Once you think about writing your code to take advantage of these Collections of Controls, then there are loads of fascinating and interesting things you can do. In this case, I would approach the problem from the “Main Form” perspective, not the subform perspective.

I would write code in the main form that would traverse the Main Form’s Controls Collection when the main form loads.

in your main form's “Load Event” (the form housing all of the identical subforms), run the code below. The code will seek out all of your subforms it will look in each form and see if it has a command button named “btnToHide” and set it's visible property to false...

Code:
Private Sub Form_Load()
Dim Ctrl As Control

    For Each Ctrl In Me.Controls
    
        Select Case Ctrl.ControlType
            Case acSubform
                Ctrl.Form.btnToHide.Visible = False
        
        End Select
    
    Next Ctrl

End Sub      'Form_Load
 
Last edited:

MarkK

bit cruncher
Local time
Today, 03:11
Joined
Mar 17, 2004
Messages
8,180
Gasman, that property goes on the form that might be stand-alone or might be a subform...
Code:
Property Get HasParent As Boolean
[COLOR="Green"]'  This property defaults to false if there is an error[/COLOR]
On Error Resume Next
   HasParent = Not Me.Parent Is Nothing  [COLOR="green"]'this causes an error in a stand-alone form[/COLOR]
End Property

Private Sub Form_Open(Cancel As Integer)
   Me.cmdClose.Visible = Not Me.HasParent  [COLOR="Green"]'not visible if Me.HasParent, else visible[/COLOR]
End Sub

I agree with Uncle Gizmo that it is not good practice to wait for an error to happen as a routine part of solving a problem, but to me it is crazy that referencing the Parent property of a form should ever return an error at all. A stand-alone form's Parent property should return Nothing.

IMO,
Mark
 

Uncle Gizmo

Nifty Access Guy
Staff member
Local time
Today, 11:11
Joined
Jul 9, 2003
Messages
16,280
Update!!!

The previous code I provided is dependent on each subform containing a command button named “btnToHide” In other words if you add a new, different subform, one without this command button on it, then you will get an error.

One solution would be to add a command button named “btnToHide” to all of the subforms that you are going to use on the main form.

However it doesn't seem right that you should have to add an unnecessary control to a form to ensure that your code works correctly in one particular situation. Now think about the above statement, it is obvious that adding an extra control for this purpose goes against the grain, but look, you are quite happy adding code to the onload event of the form which is very specific to one particular situation in your database. In other words adding code seems right, but adding a control seems wrong.

The point I am making is that you should keep your forms clean! Don't have any extra controls, controls that are there to operate under certain conditions. Don't have any code which operates under certain conditions, not if it's possible to achieve the same effect by manipulating the form with VBA.

As I mentioned before about utilising an error to make your code work correctly, it should be avoided if at all possible. Again the same here, if you have the opportunity to keep your form clean, free from unnecessary code, free from unnecessary controls then you should do that.

I think what I have described is written down somewhere as a basic principal of writing code, and it's got a name, but I can't recall what it is… something like coupling and/or decoupling, although I'm sure those terms are more to do with Class Modules…

I'm not criticizing anybody for using the error event or adding code or controls to a form in this way. I do it myself! My goal is to pique your interest in a different way of looking at your coding techniques which I'm sure once your grasp will make things so much easier!

Now for the update...
The new version of the code below looks in each form (subform) interrogates the controls collection of the form looks for a command button named “btnToHide” if it finds the button it renders it invisible, if it cannot find the button it does nothing. In other words it does not fail like the first code example. You will only need this updated code if you add forms (subforms) which do not have the command button named “btnToHide”....

Code:
Private Sub Form_Load()

    Dim Ctrl As Control
    For Each Ctrl In Me.Controls
    
        Select Case Ctrl.ControlType
            Case acSubform
            
                Dim frmCtrl As Control
                For Each frmCtrl In Ctrl.Form.Controls
            
                    If frmCtrl.Name = "btnToHide" Then
                        frmCtrl.Visible = False
                    End If
            
                Next frmCtrl
        End Select
    
    Next Ctrl

'Warning!!! This code is not tested, and besides that warning, I also have some
'suspicions that what I think it's doing and what it is actually doing are two
'different things! In other words if you use it make sure you know what it's
'doing!


End Sub
 
Last edited:

isladogs

MVP / VIP
Local time
Today, 11:11
Joined
Jan 14, 2017
Messages
18,213
I agree completely with both mark and Tony.
I don't like using code that relies on an error being triggered
If you do so, unexpected behaviour will probably occur at a later date and bite you.
There is (almost) always a better way.
For example compare the different solutions given to code which checks if a function or sub exists. See this thread
https://www.access-programmers.co.uk/forums/showthread.php?t=241933
 

Gasman

Enthusiastic Amateur
Local time
Today, 11:11
Joined
Sep 21, 2011
Messages
14,261
The O/P is specifically asking about a button, but what if there were a bunch of controls involved?
I've seen lots of examples here where controls are disabled/hidden if a value is in the Tag property.
Why not use the tags? then it does not matter if the control exists or not?
 

MarkK

bit cruncher
Local time
Today, 03:11
Joined
Mar 17, 2004
Messages
8,180
What I don't like about using the .Tag property of a subset of controls is that it is hidden. You have to go digging thru the controls in design view to see how the thing works. What I do if I have a subset of controls I am concerned with is I re-expose that subset as a variant array explicitly constructed in the code, like...
Code:
private m_vControls[COLOR="Green"]  'global to the form, for the lifetime of the form[/COLOR]

Public Property Get MyControlSet
[COLOR="Green"]'  this property lazy-loads the array if a consumer happens to need it[/COLOR]
   if isempty(m_vControls) then m_vControls = Array([COLOR="Blue"]Me.Text0, Me.Text2, Me.Text4[/COLOR])
   MyControlSet = m_vControls
End Property
  • no hidden .Tag property values
  • very short list of controls to enumerate, better than For Each ctl in Me.Controls, and then check EVERYTHING for a .Tag
  • clear-as-day list of controls that are in the set
...and enumerating the set is as simple as...
Code:
   Dim var
   For Each var in Me.MyControlSet
      var.ForeColor = vbRed
   Next
...and MyControlSet is public, so consumers outside the form itself can access it too if they want.
Mark
 

Uncle Gizmo

Nifty Access Guy
Staff member
Local time
Today, 11:11
Joined
Jul 9, 2003
Messages
16,280
What I do if I have a subset of controls I am concerned with is I re-expose that subset as a variant array explicitly constructed in the code, like...
Code:
private m_vControls[COLOR="Green"]  'global to the form, for the lifetime of the form[/COLOR]

Public Property Get MyControlSet
[COLOR="Green"]'  this property lazy-loads the array if a consumer happens to need it[/COLOR]
   if isempty(m_vControls) then m_vControls = Array([COLOR="Blue"]Me.Text0, Me.Text2, Me.Text4[/COLOR])
   MyControlSet = m_vControls
End Property
  • no hidden .Tag property values
  • very short list of controls to enumerate, better than For Each ctl in Me.Controls, and then check EVERYTHING for a .Tag
  • clear-as-day list of controls that are in the set
...and enumerating the set is as simple as...
Code:
   Dim var
   For Each var in Me.MyControlSet
      var.ForeColor = vbRed
   Next
...and MyControlSet is public, so consumers outside the form itself can access it too if they want.
Mark

I shall add that to my armory, and give it a try! Thks Mark!
 

Minty

AWF VIP
Local time
Today, 11:11
Joined
Jul 26, 2013
Messages
10,371
That's so simple - and obvious when explained that way.
Just shows I know less than nothing about elegant coding. :eek:
 

isladogs

MVP / VIP
Local time
Today, 11:11
Joined
Jan 14, 2017
Messages
18,213
Hi mark

As someone who regularly uses the tag property to manage groups of controls, I fully accept your comments about digging around in design view to determine which controls have a particular tag.

I also think your code is elegant but I'm not convinced its as concise in practice as using tags to do the same thing.
Also do you risk getting confused by having several control sets on your form to manage several control groups?
Or have I misunderstood that bit?

So I have a challenge for you which I hope you will be happy to take on.
Some months ago I created a sample database using the tag property to change the following properties of groups of controls - visible / enabled / locked

You can find it here https://www.access-programmers.co.uk/forums/showthread.php?t=293439

My challenge is for you to use the same form and controls and do exactly the same as I did but using your approach.
There are 4 groups of controls on the form, currently tagged A, B, C, D
I'd be interested in seeing the code required using your approach.

Are you up for that?
 
Last edited:

Uncle Gizmo

Nifty Access Guy
Staff member
Local time
Today, 11:11
Joined
Jul 9, 2003
Messages
16,280
it is crazy that referencing the Parent property of a form should ever return an error at all. A stand-alone form's Parent property should return Nothing.

IMO,
Mark

Hi Mark..
I have suffered many hours nay, many days nay, many weeks struggling with the stupid, unnecessary issue of what happens when an object does not exist in an object variable/form variable... I've tried using "is nothing" (that's the only one I can recall at the moment) there's loads of different ones I've tried, but nothing works! So I understand your pain!!!

In view of your observation about referencing the Parent property of a form I wrote The following routine. Unfortunately will not work from the load event of the subform because at that stage the subform is not instantiated, even though you are calling the damn code from the very self same subform!

I spent a few hours on this and I am tired! So I haven't vetted it for stupid errors. I would appreciate someone else pointing anything out to me that I have missed...

First thing is it needs a better name!

It will return true if the form you pass into it is a subform, and will return false if it's a stand-alone form.

The first problem I solved was that you might have the same subform loaded more than once so you can't use the name of subform because if you find 3 how do you know which one is the right one? Then I remembered that you could get a forms unique identifier, the windows handle "Hwnd" so I use that to identify the particular form...

The only thing is, seeing as you can't use it from the form's load event, I can't see that there's really anything else you can use it for?

Code:
Function fBlnIsSubform(oForm As Form) As Boolean
'Code Found Here:-
'https://msdn.microsoft.com/en-us/vba/access-vba/articles/allforms-object-access

'This looks interesting!
'https://social.msdn.microsoft.com/Forums/office/en-US/cbe20928-a121-4709-a7c3-d8952973242f/runtime-error-2455-you-entered-an-expression-that-has-an-invalid-reference-to-the-property?forum=accessdev


'''Call Like this:-
'''Private Sub btnToHide_Click()
'''    MsgBox "Am I a Subform? >>> " & fBlnIsSubform(Me)
'''End Sub

'WILLNOT work from Load event, Well, it sorta works but throws errors....


Dim blnIsA_Sub As Boolean

    Dim obj As AccessObject
    Dim dbs As Object
    Set dbs = Application.CurrentProject
    
    ' Search for open AccessObject objects in AllForms collection.
    For Each obj In dbs.AllForms
        If obj.IsLoaded = True Then
        Dim strFrmName As String
        Dim frmX As Form
        strFrmName = obj.Name
            
            'Find each loaded form in turn and set them to a form variable
            Set frmX = Forms(strFrmName)
            Dim Ctrl As Control
                'For each control in the found, "Loaded" form
                For Each Ctrl In frmX.Controls
                    Select Case Ctrl.ControlType
                        Case acSubform  'check to see if it is a subform
                        
                            If Ctrl.Form.Hwnd = oForm.Hwnd Then     'Test to see if the form you are in (the form you passed in)
                                blnIsA_Sub = True                   'and the form you have found, have the same Windows handle.
                            End If      
                    End Select
                Next Ctrl
        End If
    Next obj
    
    fBlnIsSubform = blnIsA_Sub
    
End Sub
 
Last edited:

Users who are viewing this thread

Top Bottom