And Parent and Container are different. The Parent will always be some form, whereas the Container will not necessarily be a form.
If you want the hWnd of the Container, you can do: ctrl.Container.hWnd
Enjoy,
Elroy
EDIT1: I seriously doubt this is what you're after, but, to cover Krool's suggested situation, if you're in a custom-user-control, and you want the main form's handle, the following will do it pretty much anywhere within the user-control: Extender.Parent.hWnd or either Extender.Container.hWnd (for a container on the main form).
Last edited by Elroy; Apr 11th, 2017 at 02:02 PM.
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
Also, Ordinary Guy, if you're actually designing a custom User-Control, here's a link to a pastebin document that may help you. It's just something I put together a few years back.
Enjoy,
Elroy
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
Not always.
As Krool already mentioned, the usercontrol may be inside another usercontrol, and in that case the Parent is that usercontrol, not the form.
And if the parent Usercontrol does not have hWnd property implemented, Parent.hWnd on the child usercontrol raises an error.
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
'=============================================================================================
'The following source code comes from LaVolpe's AlphaImage Control - sptRemoteRender
'http://www.vbforums.com/showthread.php?630193-vb6-Alpha-Image-Control-v2-Final-Update-(15-Jan-2012)
'Reorganized by Dreammanor on April 12, 2017
'=============================================================================================
Private Function GetParentHandle(Optional ByVal bGetContainerHandle As Boolean = False) As Long
Dim hHandle As Long
Dim sControlName As String
Dim meControl As Control, myForm As Object
Dim X As Long, Index As Integer
On Error Resume Next
Set myForm = ParentControls(0) ' set instance of form/mdi
X = myForm.Controls.Count
If Err Then
Err.Clear
Exit Function
End If
sControlName = Ambient.DisplayName ' get our control's name
X = InStr(sControlName, "(") ' indexed?
If X Then ' if so, get the index
Index = Val(Mid$(sControlName, X + 1)) ' adjust control name & assign
sControlName = Left$(sControlName, X - 1)
Set meControl = myForm.Controls(sControlName)(Index)
Else ' assign
Set meControl = myForm.Controls(sControlName)
End If
If Err Then Exit Function
Set myForm = Nothing ' done with this
If bGetContainerHandle = True Then
hHandle = meControl.Container.hWnd
Else
hHandle = meControl.Parent.hWnd
End If
If Err Then Exit Function
GetParentHandle = hHandle
End Function
Last edited by dreammanor; Apr 12th, 2017 at 06:52 AM.
Krool, agree with you, ContainerHwnd property should be more direct and faster. But meControl is the standard form control, which has no ContainerHwnd property, so we can only use UserControl.ContainerHwnd. In addition, meControl also has some other properties that UserControl doesn't have, such as:
nScaleMode = meControl.Container.ScaleMode
Last edited by dreammanor; Apr 12th, 2017 at 06:42 AM.
There is an API that will return the form's hWnd, redardeless the usercontrol sits on the form or in another usercontrol:
Code:
Private Declare Function GetAncestor Lib "user32.dll" (ByVal hwnd As Long, ByVal gaFlags As Long) As Long
Private Const GA_ROOT = 2
Private Sub UserControl_Show()
Debug.Print "Parent form hWnd: " & GetAncestor(UserControl.hwnd, GA_ROOT)
End Sub
But it won't work at the time of the ReadProperties event.
I tried this above and it just doesn't work if the container is not a form.
Code:
Private Function GetParentHandle(Optional ByVal bGetContainerHandle As Boolean = False) As Long
Dim hHandle As Long
Dim sControlName As String
Dim meControl As Control, myForm As Object
Dim X As Long, Index As Integer
On Error Resume Next
Set myForm = ParentControls(0) ' set instance of form/mdi
X = myForm.Controls.Count
If Err Then
Err.Clear
Exit Function
End If
sControlName = Ambient.DisplayName ' get our control's name
X = InStr(sControlName, "(") ' indexed?
If X Then ' if so, get the index
Index = Val(Mid$(sControlName, X + 1)) ' adjust control name & assign
sControlName = Left$(sControlName, X - 1)
Set meControl = myForm.Controls(sControlName)(Index)
Else ' assign
Set meControl = myForm.Controls(sControlName)
End If
If Err Then Exit Function
Set myForm = Nothing ' done with this
If bGetContainerHandle = True Then
hHandle = meControl.Container.hWnd
Else
hHandle = meControl.Parent.hWnd
End If
If Err Then Exit Function
GetParentHandle = hHandle
End Function
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (ByRef Destination As Any, ByRef Source As Any, ByVal Length As Long)
Public Function GetTopUserControl(ByVal UserControl As Object) As VB.UserControl
If UserControl Is Nothing Then Exit Function
Dim TopUserControl As VB.UserControl, TempUserControl As VB.UserControl
CopyMemory TempUserControl, ObjPtr(UserControl), 4
Set TopUserControl = TempUserControl
CopyMemory TempUserControl, 0&, 4
With TopUserControl
If .ParentControls.Count > 0 Then
Dim OldParentControlsType As VBRUN.ParentControlsType
OldParentControlsType = .ParentControls.ParentControlsType
.ParentControls.ParentControlsType = vbExtender
If TypeOf .ParentControls(0) Is VB.VBControlExtender Then
.ParentControls.ParentControlsType = vbNoExtender
CopyMemory TempUserControl, ObjPtr(.ParentControls(0)), 4
Set TopUserControl = TempUserControl
CopyMemory TempUserControl, 0&, 4
Dim TempParentControlsType As VBRUN.ParentControlsType
Do
With TopUserControl
If .ParentControls.Count = 0 Then Exit Do
TempParentControlsType = .ParentControls.ParentControlsType
.ParentControls.ParentControlsType = vbExtender
If TypeOf .ParentControls(0) Is VB.VBControlExtender Then
.ParentControls.ParentControlsType = vbNoExtender
CopyMemory TempUserControl, ObjPtr(.ParentControls(0)), 4
Set TopUserControl = TempUserControl
CopyMemory TempUserControl, 0&, 4
.ParentControls.ParentControlsType = TempParentControlsType
Else
.ParentControls.ParentControlsType = TempParentControlsType
Exit Do
End If
End With
Loop
End If
.ParentControls.ParentControlsType = OldParentControlsType
End If
End With
Set GetTopUserControl = TopUserControl
End Function
Hello Krool, how are you? Thanks for the feedback.
My problem is as follows: I am using a subclassing and in it I used WM_SIZING for when parent.hwnd receives the sizing call it responds to a certain event.
The problem is that if the usercontrol is in the form everything is fine, but if it's in a container then WM_SIZING just doesn't work.
@Episcopal. You should explain why it doesn't work. Is a valid hWnd being returned or is it zero. Where are you calling your function to get the parent hWnd? From which usercontrol event. It is possible the problem is when it is being called.
Insomnia is just a byproduct of, "It can't be done"
Hello, this is another option that can work with nested UserControls:
Code:
Private Declare Function GetParent Lib "user32" (ByVal hWnd As Long) As Long
Private Function GetFormHwnd() As Long
Dim lPar As Long
Dim iHwnd As Long
iHwnd = UserControl.hWnd
lPar = GetParent(iHwnd)
While lPar <> 0
iHwnd = lPar
lPar = GetParent(lPar)
Wend
GetFormHwnd = iHwnd
End Function
Well, just reviewing the code (not running it), Button.ctl & Example.ctl subclass the control, not the host/parent.
Teste.ctl subclasses the control and also attempts to subclass the control's container. You said this works when the container is the form but doesn't when the container is not the form. I'm thinking it does work, but you may not be trapping the right message.
If the container is a picturebox for example, then it is highly unlikely that it will receive WM_SIZING messages when the picturebox is resized. It should receive WM_SIZE message after it resizes. It's really important to read documentation for the messages you are subclassing so that you know when they occur and how to respond to them.
Insomnia is just a byproduct of, "It can't be done"
For the Resize-Handling itself, I wouldn't bother with SubClassing...
Instead - what such a "BaseContainer-Control" could support, is a mechanism -
which will result in a CallBack (within the ContainerControl itself), when the Parent is resized.
In the RC5-WidgetEngine I've implemented something similar - and named the internal CallBack-Method:
Container_Resize()
This works (without storing Parent-References within the Controls permanently - and is thus Circle-Ref-safe),
by just enumerating all the Child-Controls within the UserControl_Resize()-Handler - and calling this method.
Here is an example, what I mean:
- in an empty Form-Project, add a Private UserControl and name it ucContainer
- set its "ControlContainer"-Property to True
- now paste the following "Anchor-Test-Code" into it:
Code:
Option Explicit
Private AL As Boolean, AT As Boolean, AR As Boolean, AB As Boolean
'*** UserControl-EventHandlers ***
Private Sub UserControl_Paint()
DrawWidth = 2: Cls
Line (15, 15)-(ScaleWidth - 30, ScaleHeight - 30), vbBlue, B
End Sub
Private Sub UserControl_Resize()
ResizeNotifyChildrenOf UserControl
End Sub
'*** Public Methods ***
Public Sub SetAchors(L, T, R, B)
AL = L: AT = T: AR = R: AB = B
End Sub
Public Sub ResizeNotifyChildrenOf(ByVal Ctl)
On Error Resume Next
For Each Ctl In Ctl.Controls
Ctl.Container_Resize
Next
On Error GoTo 0
End Sub
Public Sub Container_Resize()
With Extender
Dim WP!: WP = .Parent.ScaleWidth
Dim HP!: HP = .Parent.ScaleHeight
Static dL!: If dL = 0 Then dL = WP - .Left
Static dT!: If dT = 0 Then dT = HP - .Top
Static dW!: If dW = 0 Then dW = WP - .Width
Static dH!: If dH = 0 Then dH = HP - .Height
Dim W!: W = .Width: If AL And AR Then W = WP - dW
Dim H!: H = .Height: If AT And AB Then H = HP - dH
Dim L!: L = .Left: If AR And Not AL Then L = WP - dL
Dim T!: T = .Top: If AB And Not AT Then T = HP - dT
.Move L, T, IIf(W > 0, W, 1), IIf(H > 0, H, 1)
End With
UserControl.Refresh
End Sub
Now place two instances of ucContainer on the Form (as ucContainer1 and ucContainer2)
Make sure, that ucContainer2 is placed "within" ucContainer1 as a Child-Control...
Form-Test-Code is then:
Code:
Option Explicit
Private Sub Form_Load()
ucContainer1.SetAchors 1, 1, 1, 1
ucContainer2.SetAchors 1, 1, 1, 1 'this was placed as a Child-Control on ucContainer1
End Sub
Private Sub Form_Resize()
'only one such call, from *any* of the ucContainer-Controls on this form has to be made
ucContainer2.ResizeNotifyChildrenOf Me
End Sub
Private Sub Form_Resize()
'only one such call, from *any* of the ucContainer-Controls on this form has to be made
ucContainer2.ResizeNotifyChildrenOf Me
End Sub
Great master .... in your case you are using the form resize .... in my case I implemented a property in usercontrol ... By the way ... you are very creative.