I discovered Control.Parent
may refer to either a Form
OR it might also be a Page
if it's in a TabControl
. In the past, I've simply snagged the parent, and it was a form, even for controls on tabs. What's confounding me more, is subform
controls appear to have the Form
as .Parent
, but no other controls do (note, this may not always be true as indicated by @FunThomas); but it is the case in my version of Access).
Control Type | Control.Parent Returns |
Parent Type | Expected Parent |
---|---|---|---|
TabCtl1 | TopForm | Form | TopForm |
TabPage1 | TabCtl1 | TabControl | TabCtl1 |
TabPage2 | TabCtl1 | TabControl | TabCtl1 |
SubFormCtl | TopForm | Form | TabPage2 |
ListBoxCtl | TabPage1 | Tab Page | Tab Page 1 |
TextBoxCtl | TabPage1 | Tab Page | Tab Page 1 |
And now, the question: What am I doing wrong? Do I need to loop through all the parents until I get the Control's parent form?
CodePudding user response:
Controls "live" inside other controls. You can get the control they live in via the property Parent
.
If a control has one or several controls "inside", you can get a list via the property Controls
, but note that not all control types can have children, eg a label can't have child controls and has no Controls
-property.
This parent-child relationship can be cascading. You can have a Tab Control with pages, and a page can contain an group of option button. You would have something like
Form
TabControl
Page 1
Frame 2
Label 3
Check 4
Are you sure that your subform is on a Page control? If yes, it should have the Page control as parent, not the Main form - at least that is the case in an example I did to test my code.
Labels that are linked to a control, eg the label next to an input field have that control as parent. As a rule of thumb: If you move a control and other controls are moved with it, then those controls are childs and the control you are moving is the parent.
The following function will return the top-most parent, that should be the form.
Function getForm(ctrl As Variant) As Variant
Dim Parent As Variant
On Error Resume Next
Set parent = ctrl.parent
On Error GoTo 0
If IsEmpty(parent) Then
Set getForm = ctrl
Else
Set getForm = getForm(parent)
End If
End Function
CodePudding user response:
After a lot of testing and head scratching, I found the solution to be rather more simple than expected. I used @Nathan_Sav's and @FunThomas's and ended up melding them to ensure I could do what I needed.
I am after the parent form as suggested by @AlbertD.Kallal (so I could select the parent Form
and the child Control
and revert the action if needed, or simply log the change). I wanted the parent form because it's easiest and most consistent to simply use FrmInUse.ControlToActOn.WhateverINeedToDo
instead of going down the tree. This method is used in the other answer I posted recently: "Access VBA - How to get the properties of a parent subform, or, get the user-given name for a subform (not the object reference name)". This question was trying to figure out why the .Parent
was not consistently returned.
It would be possible, I suppose, to activate all the controls down to the one I wanted (it would ensure tabs are open to the desired location), however, users indicated that did not do what they expected AND that also fires events (even though Microsoft's TabControl.Change Documentation says VBA won't trigger the event, I found in practice that is patently not true in my version of Access).
To get the Parent
of a Control
in a manner consistently parsed by a loop (Form.SfrmControl.Sfrm.CtrlIWant
or Form.CtrlIWant
) and given that the Control
or Form
has a parent object (checked elsewhere), this gets what I was looking for.
If TypeOf ControlTarget Is Form Then
Set ParentForm = ControlTarget.Parent
Else
Set ParentForm = ControlTarget.Properties.Parent.Form
End If
The code is then used like this: (pseudo code while we polish it up and test for edge cases, I plan on posting the fully built class and components later).
Public Sub GetAddress(ByRef FormOrControlTarget As Object) As String
Dim ParentForm As Access.Form
If TypeOf FormOrControlTarget Is Form Then
Set ParentForm = FormOrControlTarget.Parent
Else
Set ParentForm = FormOrControlTarget.Properties.Parent.Form
End If
If ParentForm Is Nothing Then
GetAddress = FormOrControlTarget.Name & FormOrControlTarget.Hwnd
ElseIf FormOrControlTarget Is Form Then
' Note: the below line won't work as-is, you'll need to search the parent form
' for it; I'll update this and the relevant code when it's ready to go.
GetAddress = ParentForm.Name & ParentForm.SfrmControl.Name & FormOrControlTarget.Name & FormOrControlTarget.Hwnd
Else
GetAddress = ParentForm.Name & ParentForm.Hwnd & FormOrControlTarget.Name
End if
End Sub