Is Form being Loaded as a SubForm

ted.martin

Registered User.
Local time
Today, 17:41
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
 
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
 
Or how about this? Replace MyFormName with your form name...!

Code:
If CurrentProject.AllForms("MyFormName").IsLoaded Then
      DoCmd.Close acForm, "MyFormName"
End If
 
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])
 
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
 
Did any of you see Markks solution? It was much simpler and tbh, imho more elegantly constructed...
 
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:
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
 
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
 
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
 
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?
 
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
 
That's so simple - and obvious when explained that way.
Just shows I know less than nothing about elegant coding. :o
 
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:
Excellent - just what I was hoping....:)

BTW that example is a relatively simple one.

At the other extreme, I have a highly complex form used for multiple purposes with around 500 controls.
It has around 20-25 groups of tagged controls which are made hidden/visible; locked/unlocked; enabled/disabled; highlighted or not ... all using tags.

Just so you know what I'll propose next ... :D
 
Tony, one thing you can do here,
Code:
   If Ctrl.Form.Hwnd = oForm.Hwnd Then
      blnIsA_Sub = True
   End If
...although it looks like Hwnd is working, is use "Is", like...
Code:
   If Ctrl.Form Is oForm Then
      blnIsA_Sub = True
   End If
...which, of course, can be further reduced to...
Code:
   blnIsA_Sub = Ctrl.Form Is oForm
Is compares instances, not types, so it is only true if the two operands point to the same instance of the object.

I do not get an error running your code from the load event of a subform.

Also, given how much effort this code goes thru to determine if the object is a subform, therefore I would rather do this test, event though it means intentionally raising an error...
Code:
Property Get MeIsASubform As Boolean
   On Error Resume Next
   MeIsASubform = Left(Typename(Me.Parent), 4) = "Form"
End Property
...and it also always makes more sense to me for an object to expose it's own properties that to hand the object over to an external routine to run a test on it from the outside. For encapsulation, I want that test to run INSIDE the object, and for the result to be exposed as a property or method.
Does this address the topic you raise, or have I missed the point?
Mark
 
Colin, I was going to say "no, I won't do it," because what starts to cloud the issue is the infrastructure or maybe the "meta-structure" required to demonstrate the issue.

At its simplest, here's what we are talking about: We might need to selectively enable or disable four buttons until all data is entered in a form, say. At that level, I can
  • .Tag those four buttons and run a big loop thru all controls, test for .Tag, and set the state, or I can
  • expose a four member array I pre-populated with said buttons, enumerate that array, and set the state.
Two simple choices for how to make that work.

But looking at your form, there are groups, and mixed groups, and labels that belong to other controls, and so on, and so suddenly there is the bigger problem, like a meta-problem, of managing all the different groups that get hidden while the other group gets disabled. And once all that overhead is taken into account, will the simple two point description, above, be significantly improved? Probably not. You still either gonna .Tag them, or put 'em in an array.

But as to your last post, if I wrote a demo like the one you did, I would write a class, maybe called cControlGroup, that has properties like .Visible and .Enabled. Also, there would be a .Load method that takes a ParamArray which would be the list of controls, untagged, that belong to that group. Maybe this goes to what you are talking about with your 500 control form.

Imagine a cControlGroup class that could be used like this...
Code:
Private m_ccgA as cControlGroup
Private m_ccgB as cControlGroup

Property Get GroupA As cControlGroup
   If m_ccgA Is Nothing then 
      Set m_ccgA = New cControlGroup
      m_ccgA.Load [COLOR="Blue"]Me.Text0, Me.Text2, Me.Text[/COLOR]
   End If
   Set GroupA = m_ccgA
End Property

Property Get GroupB As cControlGroup
   If m_ccgB Is Nothing then 
      Set m_ccgB = New cControlGroup
      m_ccgB.Load [COLOR="blue"]Me.cmd1, Me.cmd2, Me.cmd3[/COLOR]
   End If
   Set GroupB = m_ccgB
End Property

Private Sub Text0_AfterUpdate()
   Me.GroupB.Enabled = Me.GroupA.HasNoNullValue
End Sub
So if we write a class that manages a set of controls, and that set of controls is defined by simply passing them into that class in the constructor, that makes a lot of sense to me. See what's going on in that code? Constructors, essentially, for classes that manage groups of controls, and then the form's code just leverages those classes.

That's how I'd approach that problem. But I'm not going to because I don't have time right now, but I would comment on it, as I'm doing, like this, in this post...

Hope this helps, makes sense, etc...
Cheers,
Mark
 

Users who are viewing this thread

Back
Top Bottom