Get current subform control (container) name (1 Viewer)

I Thanked Mark's example code mostly because it succinctly demonstrates the basics of some important design principles. Iterating through the form's Control collection, using a Private Collection to manage them efficiently then presenting the information as a Property.
 
I think, in part, the OP just wants to see if it can be done. But one reason it might be useful for real is to change out a subform based on user interaction with that subform. And in the case that there are multiple instances of that SAME subform on the main form, you would have to solve this.

And actually, I could put this to use in a calendar form. Say you make a form for a week with seven subforms, one for each day, so you have 7 instances of the same form on the main form, each showing data for that corresponding day. Currently, I would also put seven hidden textboxes and link my subforms via LinkMaster and LinkChildFields properties to date values I would control--in the textboxes--programmatically. Does that still make sense? But if the subform could determine it's host control by name, and the names contain a numeric value (0-6) indicating their offset from some fixed Sunday, then each subform could actually figure out it's own recordsource completely programmatically, which is to say, without using the LinkMasterFields and LinkChildFields properties. This would make that form simpler and potentially faster.

Hi MarkK,

you've hit the nail on the head here. This is basically what I was trying to achieve, plus as you say, I was sure it would be possible in some way. After moving Frothingslosh's solution into the OnLoad event, I discovered it won't work there, then read the comment about this after my attempt. :cool:

Your code will obviously solve this, so thanks for the solution, I was hoping there would be some simple reference to the current container name, but I guess it's not that simple. Your calendar comment is a perfect example of how simple the ability to reference the container would be.
Although placing a hidden textbox on the parent form then linking is another solution, in my opinion, this is only another workaround.

I'll say again, If you have the container name, you can then place that subform anywhere and it'll work without any further ado (:D)...

Ivan
 
I Thanked Mark's example code mostly because it succinctly demonstrates the basics of some important design principles. Iterating through the form's Control collection, using a Private Collection to manage them efficiently then presenting the information as a Property.
Yes, it's excellent! A good example that I aspire to.

It would be interesting to see other ways of doing this. In my experience there are usually 3 ways.

It hadn't occurred to me that "me" would return the object, so I learned something new.

I'm not comfortable with collections yet, something else I need to use more frequently.

Sent from my SM-G925F using Tapatalk
 
Last edited:
Taking MarkK's code we can eliminate the collection and just reference the container relevant to the subform:

Code:
Private subCont As Control

Private Sub Form_Load()
    Dim ctrl As Control
    For Each ctrl In Me.Parent.Controls
        'find the subform, and reference it
        If ctrl.ControlType = acSubform Then
            If ctrl.Form Is Me Then
                  Set subCont = ctrl
                  Exit For
            End If
        End If
    Next
End Sub
 
... then presenting the information as a Property.

IMHO, a property accessor method (Get/Let(set)) is used when the data for the property is intended to be exposed outside the class object (the principle of encapsulation). So the block beginning "Property Get Subforms() As VBA.Collection" exposes Subforms() outside of the subform object. I don't think that is actually intended or required here - it would be an odd thing to expose. For example, from the main form you can currently use Me!sfm1.Form.Subforms.Count.

So we would think, ok lets just make it private... "Private Property Get..." (it's Public by default). But under OOP the whole purpose of accessor methods Get/Let(Set) are to provided public access to properties. There's no such notion as a private property made available via an accessor method in OOP afaik (VBA breaks the rule). You already have direct access to the private variable m_sfms in any case. So applying an accessor doesn't achieve what an accessor is intended to do (encapsulate). In my view, it would be appropriate to use a private function in this case. Alternatively, create a class to encapsulate m_sfms.

I am basing my view on the wider usage of OOP and accept that VBA bends the OOP rules.
 
Very nice sum up Chris. I'm going to put a link to this from the evolving OOP thread started a few months ago. Private Properties and the calendar suggested by Markk (so you have 7 instances of the same form on the main form) -- very "classy".
 
I would expose the Public Property Get Subforms As VBA.Collection from the parent form so it is only ever built once. Putting it on the subform was just to prove it could work that way, but it belongs on the main form as a specialized controls collection. Doing that work on every subform is duplication of effort, and therefore a waste.

One HUGE advantage in VBA of using properties is that they are evaluated during debugging in the locals window, so if you expose objects as properties, private or not, and break into code, you can minutely dissect and drill down into your class structures, which I find very handy. If I write a custom class, most of its members, particularly objects, I'll expose as properties, which makes looking into that class very easy.

By way of example, you can do this with a bound form, break into code on a button click or something, open the locals window, expand the Me node, find the recordset, find the fields collection, find a field, find that field's Value property, and in doing this notice how you can drill down into the current state of the object tree. If you use classes or write classes in VBA, your properties will be visible and evaluated in this view.
 
Thank you all for this question and great replies! I have a subform used 5 times on a parent form. The subform show last month's forecast. If the forecaster wants to keep last month's forecast this month, a simple double click on the forecast number copies it to this month's forecast. Therefore, I needed to know the actual location of my subform. Is it Labor, Material, Contracts, Equipment, or Other. Once know, the subform then calculates which subform holds this month's forecast information, therefore knows where to write the forecast from last month.
Works like a charm and I really do appreciate this "leg up!"
 
Thanks for the solution MarkK it was very useful for me to read and useful code to know about.

However in this case I believe there is an easier way!

If a subform is setup on a main form, you simply create a public variable named "mode" in the subform's module. In the main form's form_open event you set the value of the variable in the subform and all the other subforms to tell it "what it is!" and how it needs to behave.

eg

me.MySubFormControlName.form.Mode = 1

Best to sue a enum instead of the "1" though.

I think this helps the subform to "encapsulate" the code better.

Here's a line form a main form that I wrote recently, which instead of using a public variable uses a sub that initialises the form and sets it up to behave the way it needs to:

Me.SFMyFormThatCanBehaveInDifferetnWays.Form.subInitialise FormMode:=MyEnum.UseQuery1


This works fine until the code crashes and variables are reset. So to make it a bit more resilient I DO NOT refer the the public variable in the other subform code, instead I use a function to get the value of the public variable.

In this way if the public variable is not set you can raise and error and ABORT!
 
The simple solution is :


Screen.ActiveForm.ActiveControl.Name

it took me three hours but...
As far as I understand the subform container is a control of the main form, meanwhile the underlying subform object is an object of the subfromcontainer.
May be I'm wrong but the expression is working like charm :-)


with best regrads :)
 
To save having to click on the subform, this code will cycle through all the controls in a form and print the names of each subform container & the actual subform object names

Code:
Dim ctrl As Control
For Each ctrl In Me.Controls
        If ctrl.ControlType = acSubform Then
            Debug.Print "Subform container name = " & ctrl.Name
            Debug.Print "Subform object name = " & ctrl.Form.Name
        End If
Next

For example:
Subform container name = subControlTypes
Subform object name = subfrmControlTypes1
 
I've had this function from Leigh on my website for a while. Not sure if it's any better than what's been offered here so far but I just thought including it here would be good to see more options. Cheers!
 

Users who are viewing this thread

Back
Top Bottom