Home > Software design >  MSAccess VBA Control.Parent Irregularities
MSAccess VBA Control.Parent Irregularities

Time:02-27

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).

enter image description here

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
  • Related