Option Explicit DefInt A-Z Const BUG = False 'The Relate component module is used like a stretch control to maintain 'the relationship between controls on a form as the user resizes it. 'Except, the Relate component is much better. ' Call AddChild(ChildControl, Move&Size) ' Call AddContents(ContentsControl, Move&Size, ContainerControl) ' Call AddPane(PaneControl, Move&Size, SashControl) Global Const RE_LEFT = 1 Global Const RE_WIDTH = 2 Global Const RE_TOP = 4 Global Const RE_HEIGHT = 8 ' Call EnforceForm(Me) ' Call EnforceContainer(ContainerControl) ' Call EnforceSash(SashControl) ' Call RemoveForm(Me) ' Call RemoveControl(Text1) Global CorrectionX As Long Global CorrectionY As Long Global RepositionMessage As String 'HOW TO USE '1 - Define the relationships of objects '--------------------------------------- 'This is done by calling the AddChild, AddContents, 'and AddPane methods from the Form_Load event. 'In each relationship, the movement of one object 'is expected to effect the size and position of the other. 'This is referred to as a parent/child relationship. 'Examples are provided below. 'Step 2 shows how to enforce these relationships. 'AddChild ' Adds to the list a control and its ' relationship to the form that can be enforced. ' Syntax ' Call AddChild(ChildControl, Move&Size) ' ChildControl - The control whose relationship to the form can be enforced. ' This control must have Left, Top, Width, and Height properties. ' Move&Size - Integer representing the size and ' position properties of ChildControl that ' should move when the relationship is enforced. ' See the Move&Size constants listed below. ' NOTES: This relationship is enforced using the EnforceForm method. ' The form is gotten from the ChildControl.Parent property. ' Size and position constants for the same direction ' should not be combined in the Move&Size argument. 'AddContents ' Adds to the list the ContentsControl contained ' in the ContainerControl and its relationship to ' the ContainerControl that can be enforced. ' Syntax ' Call AddContents(ContentsControl, Move&Size, ContainerControl) ' ContentsControl - The control whose relationship ' to the ContainerControl can be enforced. ' This control must have Left, Top, Width, and Height properties. ' Move&Size - Integer representing the size and ' position properties of ContentsControl that ' should move when the relationship is enforced. ' See the Move&Size constants listed below. ' ContainerControl - Is often a Frame or Panel control. ' This control must have Width, and Height properties ' with at least read access at run-time. ' NOTES: This relationship is enforced using the EnforceContainer method. ' ContentsControl should be drawn inside ContainerControl. ' Size and position constants for the same direction ' should not be combined in the Move&Size argument. 'Move&Size Argument Constants for AddChild and AddContents '--------------------------------------------------------------- ' RE_LEFT = 1 ' Both the left and right edges of the child ' move with the width of the parent. ' RE_WIDTH = 2 ' The width of the child moves ' with the width of the parent. ' RE_TOP = 4 ' Both the top and bottom of the child ' move with the height of the parent. ' RE_HEIGHT = 8 ' The height of the child moves ' with the height of the parent. 'You can add vertical constants to horizontal constants. 'EXAMPLE: This example defines the relationships of 'a bitmap editor that might contain a PictureBox 'with ScrollBars along the right side and bottom. ' Call AddChild(VScroll1, RE_LEFT + RE_HEIGHT) ' Call AddChild(HScroll1, RE_WIDTH + RE_TOP) ' Call AddChild(Picture1, RE_WIDTH + RE_HEIGHT) 'AddPane ' Adds to the list the PaneControl and its relationship ' to the ContainerControl that can be enforced. ' Syntax ' Call AddPane(PaneControl, Move&Size, SashControl) ' PaneControl - The control whose relationship ' to the SashControl can be enforced. ' This control must have Left, Top, Width, and Height properties. ' Move&Size - Integer representing the size and ' position properties of PaneControl that ' should move when the relationship is enforced. ' RE_LEFT = 1 ' Both the left and right edges of PaneControl ' move with the left edge of SashControl. ' RE_WIDTH = 2 ' The width of PaneControl ' moves with the left edge of SashControl. ' RE_TOP = 4 ' both the top and bottom of PaneControl ' move with the top of SashControl. ' RE_HEIGHT = 8 ' the height of PaneControl ' moves with the top of SashControl. ' SashControl - Is often used as a separator ' bar between two sides of a form. ' This control must have Left, Top properties ' with at least read access at run-time. ' NOTES: This relationship is enforced using the EnforceSash method. ' Both controls must have Left, Top, Width, and Height properties. ' PaneControl and SashControl should have the same parent or container. ' In other words, they should both be drawn ' on the same form or container control. ' Size and position constants for the same direction ' can be combined to produce special conditions. ' Such a PaneControl is usually to the right or below SashControl. ' Take, for example, one ListBox control above another ' with a SashControl between them. ' Both the Top and Height properties of the lower ' PaneControl must change when the SashControl is enforced. ' Particular caution is taken by this component to make sure that the ' bottom edge of the lower PaneControl does not snap out of place. '2 - Maintain the relationships. '------------------------------- 'This is done by calling the EnforceForm, 'EnforceContainer, and EnforceSash methods. 'EnforceForm is usually called from the Form_Resize event. 'EnforceContainer is usually called right after ContainerControl 'is moved which is usually caused by EnforceForm or EnforceSash. 'The order in this case is important. ' Call EnforceForm(Me) ' Call EnforceContainer(ContainerControl) 'EnforceSash is usually called right after SashControl.Move method is called. 'This is usually in a procedure called from various DragDrop events. ' Call EnforceSash(SashControl) '3 - Clean Up '------------ 'Be sure to call the RemoveControl method 'whenever a parent or child control is unloaded. 'Call the RemoveForm method from the 'Form_Unload event of parent forms. 'There's no need to call RemoveControl 'in the Form_Unload event. ' Call RemoveForm(Me) ' Call RemoveControl(Text1) '4 - Movement Correction (optional) '---------------------------------- 'The user might resize a form or move a Parent 'in such a way as to make children 'distorted or unusable. It might be obvious to the user 'to move whatever back a bit. Or you might prefer to 'prevent the user from doing such a thing in the 'first place. Such movements can be corrected in two 'basic ways. In code, you can define limitations that 'you can check before calling EnforceForm, EnforceContainer, and EnforceSash. 'If the limits are exceeded, then you can programatically 'move the form or control back within the limitations. 'The second method is to use the CorrectionX and 'CorrectionY global variables. 'For example: 'Private Sub CommonDrop(Source As Control, ByVal X As Single, ByVal Y As Single) ' Do ' CorrectionX = 0 ' Source.Move X ' Call EnforceSash(Source) ' X = X + CorrectionX ' Loop Until CorrectionX = 0 'End Sub ' Sub Form_Resize() ' If Me.WindowState = MINIMIZED Then Exit Sub ' Call EnforceForm(Me) ' If CorrectionX > 0 Or CorrectionY > 0 Then ' 'Move causes a cascading event. ' Me.Move Me.Left, Me.Top, Me.Width + CorrectionX, Me.Height + CorrectionY ' End If ' End Sub 'Watch out for cascading events. If you move a form 'in the From_Resize event, the event will start a second 'time before completing the first. Exit the event right 'after calling the Move method. Note that CorrectionX and 'CorrectionY are in the ScaleMode of the parent's size 'and position properties. So, no conversion is needed. 'CorrectionX and CorrectionY can be negative 'for Splitter Bar type SashControls. 'CorrectionX and CorrectionY are set to zero by EnforceForm. 'The sign of CorrectionX and CorrectionY indicate the direction. 'For example, if CorrectionX is positive after EnforceForm, 'then widen the form more. 'NOTES '----- 'The ScaleMode of every form and ContainerControl must be Twips or Pixels. 'If a ContainerControl does not have a ScaleMode property, 'then it's assumed to be Twips. 'If a SashControl is a lightweight/graphic control, 'then the ScaleMode of the parent form is used. 'If this component finds a situation it cannot handle, 'it puts a message in RepositionMessage. 'A error is not generated since many of these problems 'are not so bothersome to the user as to require such a measure. 'These messages are usually generated while 'testing or debugging an application. 'So, RepositionMessage can be very useful in these cases. 'If a VB error occurs in this component module, 'it can be trapped by the host/client application. 'The child controls (which are ChildControl, ContentsControl, and 'PaneControl) must have write access at run-time to Left and Top properties. 'If RE_HEIGHT option is used, then write access to 'Width and Height properties is needed at run-time. 'If RE_WIDTH option is used, then write access 'to the Width property is needed at run-time. '********************************************* 'The rest is private! '********************************************* '********************************************* '********************************************* 'THE SCALE DILEMMA '---------------- 'The user of this component might change the ScaleMode 'of the form, container, or parent. therefore, 'this component must keep track of these ScaleModes 'each time this component is used. 'In the case of Frame methods, not all container 'controls have a ScaleMode property. 'If one does not, the ScaleMode of the size and 'position properties of controls in the container 'are usually (and assumed to be) in TWIPS. 'However, The size and position properties of 'the container itself are not expressed according to the 'container's ScaleMode. Instead, they are based on 'the ScaleMode of the container's parent (which might 'not be the form). Since all containers have an hWnd 'property, the true parent can be found using the 'GetParent API call (not the Parent property). 'If MasterControls are allowed to be lightweight/graphic 'controls, then a MasterControl might not have an 'hWnd property. In this case, GetParent can't be used. 'In addition, there is no way to restrict the user from 'changing the ScaleMode of the true parent. Therefore, 'the user must be warned about this situation. 'ANCHOR DILEMMA '-------------- 'CORRECTION DIRECTION DILEMMA '---------------------------- 'For parent forms and containers, 'a positive correction amount means the parent should be enlarged. 'But if a sash is moved and there is a correction to be performed, 'can the sign of the correction amount indicate the direction? 'EnforceSash could determine if the pane that 'needs more room is above or below the sash. 'If the pane is below, make the correction negative. 'But what if the pane is a container that is also enforced? 'EnforceContainer does not know what made it smaller. 'Therefore, it does not know whenter to 'make the correction negative or positive. 'For this reasion, another tactic is needed. 'It is assumed that the panes that the sash 'was moved away from, have enough room. 'Therefore, a positive correction amount 'means that the sash should be moved back. 'This means that the client side correction code needs 'to figure out which way the sash was moved. 'DOUBLE CORRECTION DILEMMA '------------------------- 'Why is the client side correction code in a loop? 'After all the relationships have been inforced, 'the correction performed, 'and then the relationships enforced again, 'there may still be a correction to perform. 'This occures when the child in a 'relationship is the parent in another. 'In this case there is a heirarchy of controls. 'If there is no heirarchy, 'then the loop is not needed. 'An alternitive technique considers that heirarchy. 'First, the parents that are not children 'in other relationships are enforced. 'A correction is performed. 'And then its children that are parents to 'other controls are enforced and so on. 'This technique might be quicker; but, 'the loop is more portible. 'Sometimes, the enforcements are never satisfied 'and the loop technique becomes endless. Const PARENT_FORM = 1 Const PARENT_CONTAINER = 2 Const PARENT_SASH = 3 Type ReChildStruct Sides As Integer Horizontal As Long Vertical As Long ParentType As Integer ParentIndex As Integer End Type Dim mChildCount As Integer Dim mChildData() As ReChildStruct Dim mChildControl() As Control 'Parent arrays. Dim mFormCount As Integer Dim mForm() As Form Dim mControlCount As Integer Dim mControl() As Control '********************************************* 'Declarations for Scale routines. It's private! '********************************************* Const TWIPS = 1 Const PIXELS = 3 Dim mTpux As Integer 'Twips Per Unit X Dim mTpuy As Integer 'Twips Per Unit Y 'GetParent may return a frame where control.Parent always returns a form. Declare Function ReGetParentAPI Lib "User" Alias "GetParent" (ByVal hWnd As Integer) As Integer Dim d As ReChildStruct Dim c As Control Dim Sides As String Sub AddChild (ctlChild As Control, ByVal Sides As Integer) Dim frmParent As Form Dim ChildData As ReChildStruct Set frmParent = ctlChild.Parent 'The external scale mode of a form is always TWIPS. 'Forms always have the ScaleMode property. If Not zValidateScaleMode(frmParent.ScaleMode) Then Exit Sub ChildData.ParentIndex = zMakeParentFormIndex(frmParent) ChildData.ParentType = PARENT_FORM Call AddContentsOrChildSub(Sides, ChildData, ctlChild, frmParent.Width, frmParent.Height) End Sub Sub AddContents (ctlChild As Control, ByVal Sides As Integer, ctlContainer As Control) Dim ChildData As ReChildStruct Dim Wide As Long Dim High As Long If Not zValidateScaleMode(zExternalScaleModeVB3(ctlContainer)) Then Exit Sub Wide = ctlContainer.Width * mTpux High = ctlContainer.Height * mTpuy If Not zValidateScaleMode(zInternalScaleMode(ctlContainer)) Then Exit Sub ChildData.ParentIndex = zMakeParentControlIndex(ctlContainer) ChildData.ParentType = PARENT_CONTAINER Call AddContentsOrChildSub(Sides, ChildData, ctlChild, Wide, High) End Sub Private Sub AddContentsOrChildSub (ByVal Sides As Integer, ChildData As ReChildStruct, ctlChild As Control, ByVal ParentWidth As Long, ByVal ParentHeight As Long) ChildData.Sides = Sides ChildData.Horizontal = ParentWidth - (ctlChild.Left + ctlChild.Width) * mTpux ChildData.Vertical = ParentHeight - (ctlChild.Top + ctlChild.Height) * mTpuy Call zAppendChild(ChildData, ctlChild) End Sub Sub AddPane (ctlChild As Control, ByVal Sides As Integer, ctlSash As Control) Dim ChildData As ReChildStruct Dim SashLeft As Long Dim SashTop As Long If Not zValidateScaleMode(zExternalScaleModeVB3(ctlSash)) Then Exit Sub SashLeft = ctlSash.Left * mTpux SashTop = ctlSash.Top * mTpuy ChildData.ParentIndex = zMakeParentControlIndex(ctlSash) ChildData.ParentType = PARENT_SASH '---------------------- ChildData.Sides = Sides ChildData.Horizontal = SashLeft - ctlChild.Left * mTpux 'If Sides has LEFT+WIDTH then we must leave it. If Not (Sides And RE_LEFT + RE_WIDTH) = RE_LEFT + RE_WIDTH Then ChildData.Horizontal = ChildData.Horizontal - ctlChild.Width * mTpux End If ChildData.Vertical = SashTop - ctlChild.Top * mTpuy 'If Sides has TOP+HEIGHT then we must leave it. If Not (Sides And RE_TOP + RE_HEIGHT) = RE_TOP + RE_HEIGHT Then ChildData.Vertical = ChildData.Vertical - ctlChild.Height * mTpuy End If Call zAppendChild(ChildData, ctlChild) End Sub Sub EnforceContainer (ctlContainer As Control) Dim ChildIndex As Integer Dim ParentIndexSought As Integer Dim ParentWidth As Long Dim ParentHeight As Long For ChildIndex = 0 To mControlCount - 1 If mControl(ChildIndex) Is ctlContainer Then Exit For Next 'ChildIndex If ChildIndex >= mControlCount Then RepositionMessage = "AddContents was not called for the container control passed to EnforceContainer." Exit Sub End If ParentIndexSought = ChildIndex 'Not all container controls have a ScaleMode property. 'And not all their (true) parents do either. If Not zValidateScaleMode(zExternalScaleModeVB3(ctlContainer)) Then Exit Sub ParentWidth = ctlContainer.Width * mTpux ParentHeight = ctlContainer.Height * mTpuy If Not zValidateScaleMode(zInternalScaleMode(ctlContainer)) Then Exit Sub ParentWidth = ParentWidth / mTpux ParentHeight = ParentHeight / mTpuy 'We found the parent. Now let's find its children. For ChildIndex = 0 To mChildCount - 1 If mChildData(ChildIndex).ParentType = PARENT_CONTAINER And mChildData(ChildIndex).ParentIndex = ParentIndexSought Then Call EnforceParentSub(ChildIndex, ParentWidth, ParentHeight) End If Next 'ChildIndex End Sub Sub EnforceForm (frmParent As Form) Dim i As Integer Dim ParentIndexSought As Integer Dim ParentWidth As Long Dim ParentHeight As Long For i = 0 To mFormCount - 1 If mForm(i) Is frmParent Then Exit For Next 'i If i >= mFormCount Then RepositionMessage = "ReFormLoad was not called for the form passed to EnforceForm." Exit Sub End If ParentIndexSought = i 'The external scale mode of a form is always TWIPS. 'Forms always have the ScaleMode property. If Not zValidateScaleMode(frmParent.ScaleMode) Then Exit Sub CorrectionX = 0 CorrectionY = 0 ParentWidth = frmParent.Width / mTpux ParentHeight = frmParent.Height / mTpuy 'We found the parent. Now let's find its children. For i = 0 To mChildCount - 1 If mChildData(i).ParentType = PARENT_FORM And mChildData(i).ParentIndex = ParentIndexSought Then Call EnforceParentSub(i, ParentWidth, ParentHeight) End If Next 'i End Sub Private Sub EnforceParentSub (ByVal ChildIndex As Integer, ByVal ParentWidth As Long, ByVal ParentHeight As Long) Dim ChildData As ReChildStruct Dim ctlChild As Control Dim FixX As Long Dim FixY As Long Dim cLeft As Long Dim cTop As Long Dim cWidth As Long Dim cHeight As Long ChildData = mChildData(ChildIndex) Set ctlChild = mChildControl(ChildIndex) '**************************************************** 'Make child dimensions. 'If a dimention is negative, save error in Fix and make it 0. '**************************************************** cLeft = ctlChild.Left cWidth = ctlChild.Width 'Horizontal = ParentWidth - (ChildLeft + ChildWidth). If (ChildData.Sides And RE_LEFT) = RE_LEFT Then 'ChildLeft = ParentWidth - ChildWidth - Horizontal. cLeft = zSluffNeg2Fix(ParentWidth - cWidth - ChildData.Horizontal / mTpux, FixX) ElseIf (ChildData.Sides And RE_WIDTH) = RE_WIDTH Then 'ChildWidth = ParentWidth - ChildLeft - Horizontal. cWidth = zSluffNeg2Fix(ParentWidth - cLeft - ChildData.Horizontal / mTpux, FixX) End If cTop = ctlChild.Top cHeight = ctlChild.Height 'Vertical = ParentWidth - (ChildTop + ChildHeight). If (ChildData.Sides And RE_TOP) = RE_TOP Then 'ChildTop = ParentHeight - ChildHeight - Vertical. cTop = zSluffNeg2Fix(ParentHeight - cHeight - ChildData.Vertical / mTpuy, FixY) ElseIf (ChildData.Sides And RE_HEIGHT) = RE_HEIGHT Then 'ChildHeight = ParentHeight - ChildTop - Vertical. cHeight = zSluffNeg2Fix(ParentHeight - cTop - ChildData.Vertical / mTpuy, FixY) End If '**************************************************** 'Move child. '**************************************************** If (ChildData.Sides And RE_HEIGHT) = RE_HEIGHT Then ctlChild.Move cLeft, cTop, cWidth, cHeight ElseIf (ChildData.Sides And RE_WIDTH) = RE_WIDTH Then ctlChild.Move cLeft, cTop, cWidth Else ctlChild.Move cLeft, cTop End If '**************************************************** 'It might have snapped back a bit if squished too much. '**************************************************** If (ChildData.Sides And RE_WIDTH) = RE_WIDTH Then FixX = FixX + ctlChild.Width - cWidth End If If (ChildData.Sides And RE_HEIGHT) = RE_HEIGHT Then FixY = FixY + ctlChild.Height - cHeight End If '**************************************************** 'Is this correction larger? '**************************************************** 'Debug.Print "EnforceParentSub " & ChildIndex & " FixY(<>0)=" & FixY & "; CorrectionY=" & CorrectionY If Abs(CorrectionX) < Abs(FixX) Then CorrectionX = FixX End If If Abs(CorrectionY) < Abs(FixY) Then CorrectionY = FixY End If End Sub Sub EnforceSash (ctlSash As Control) Dim i As Integer Dim ParentIndexSought As Integer Dim SashLeft As Long Dim SashTop As Long For i = 0 To mControlCount - 1 If mControl(i) Is ctlSash Then Exit For Next 'i If i >= mControlCount Then RepositionMessage = "RePosLoad was not called for the sash control passed to EnforceSash." Exit Sub End If ParentIndexSought = i 'If ctlSash or its (true) parent are a graphic control, 'then ScaleMode is not available. So fake it. If Not zValidateScaleMode(zExternalScaleModeVB3(ctlSash)) Then Exit Sub SashLeft = ctlSash.Left SashTop = ctlSash.Top 'We found the parent. Now let's find its children. For i = 0 To mChildCount - 1 If mChildData(i).ParentType = PARENT_SASH And mChildData(i).ParentIndex = ParentIndexSought Then Call EnforceSashSub(i, SashLeft, SashTop) End If Next 'i End Sub Private Sub EnforceSashSub (ByVal ChildIndex As Integer, ByVal SashLeft As Long, ByVal SashTop As Long) Dim ChildData As ReChildStruct Dim ctlChild As Control Dim FixX As Long Dim FixY As Long Dim cLeft As Long Dim cWidth As Long Dim cTop As Long Dim cHeight As Long 'An anchor is used when both the size and dimention are changed. 'A negitive AnchorCorrection means the sash was moved too far to the right or bottom. Dim RightAnchor As Long Dim BottomAnchor As Long Dim AnchorCorrection As Long Dim IsMoveAgain As Integer ChildData = mChildData(ChildIndex) Set ctlChild = mChildControl(ChildIndex) '**************************************************** 'Make child dimensions. 'If a dimention is negative, save error in Fix and make it 0. '**************************************************** cLeft = ctlChild.Left cWidth = ctlChild.Width If (ChildData.Sides And RE_LEFT + RE_WIDTH) = RE_LEFT + RE_WIDTH Then 'Horizontal = ParentLeft - ChildLeft 'RightAnchor = ChildLeft + ChildWidth 'ChildLeft = ParentLeft - Horizontal 'ChildWidth = RightAnchor - ChildLeft RightAnchor = cLeft + cWidth 'If cLeft is negative, add it to FixX. cLeft = zSluffNeg2Fix(SashLeft - ChildData.Horizontal / mTpux, FixX) 'If cWidth is negative, add it to FixX. cWidth = RightAnchor - cLeft If cWidth < 0 Then 'Debug.Print "EnforceSashSub " & ChildIndex & " cWidth(<0)=" & cWidth cLeft = cLeft + cWidth FixX = FixX + cWidth cWidth = 0 End If 'Else Horizontal = ParentLeft - ChildLeft - ChildWidth ElseIf (ChildData.Sides And RE_LEFT) = RE_LEFT Then 'ChildLeft = ParentLeft - ChildWidth - Horizontal cLeft = zSluffNeg2Fix(SashLeft - cWidth - ChildData.Horizontal / mTpux, FixX) ElseIf (ChildData.Sides And RE_WIDTH) = RE_WIDTH Then 'ChildWidth = ParentLeft - ChildLeft - Horizontal cWidth = zSluffNeg2Fix(SashLeft - cLeft - ChildData.Horizontal / mTpux, FixX) End If 'It might be nither. cTop = ctlChild.Top cHeight = ctlChild.Height If (ChildData.Sides And RE_TOP + RE_HEIGHT) = RE_TOP + RE_HEIGHT Then BottomAnchor = cTop + cHeight 'If cTop is negative, add it to FixY. cTop = zSluffNeg2Fix(SashTop - ChildData.Vertical / mTpuy, FixY) 'If cHeight is negative, add it to FixY. cHeight = BottomAnchor - cTop If cHeight < 0 Then 'Debug.Print "EnforceSashSub " & ChildIndex & " cHeight(<0)=" & cHeight cTop = cTop + cHeight FixY = FixY + cHeight cHeight = 0 End If ElseIf (ChildData.Sides And RE_TOP) = RE_TOP Then cTop = zSluffNeg2Fix(SashTop - cHeight - ChildData.Vertical / mTpuy, FixY) ElseIf (ChildData.Sides And RE_HEIGHT) = RE_HEIGHT Then cHeight = zSluffNeg2Fix(SashTop - cTop - ChildData.Vertical / mTpuy, FixY) End If 'It might be nither. '**************************************************** 'Move child. '**************************************************** If (ChildData.Sides And RE_HEIGHT) = RE_HEIGHT Then ctlChild.Move cLeft, cTop, cWidth, cHeight ElseIf (ChildData.Sides And RE_WIDTH) = RE_WIDTH Then ctlChild.Move cLeft, cTop, cWidth Else ctlChild.Move cLeft, cTop End If '**************************************************** 'The size might have snapped back a bit if squished too much. 'If the right or bottom should be anchored 'then we might have to move it again. '**************************************************** If (ChildData.Sides And RE_LEFT + RE_WIDTH) = RE_LEFT + RE_WIDTH Then AnchorCorrection = RightAnchor - (ctlChild.Left + ctlChild.Width) If AnchorCorrection <> 0 Then 'Debug.Print "EnforceSashSub " & ChildIndex & " Right AnchorCorrection(<>0)=" & AnchorCorrection IsMoveAgain = True cLeft = cLeft + AnchorCorrection FixX = FixX + AnchorCorrection End If ElseIf (ChildData.Sides And RE_WIDTH) = RE_WIDTH Then FixX = FixX + ctlChild.Width - cWidth End If If (ChildData.Sides And RE_TOP + RE_HEIGHT) = RE_TOP + RE_HEIGHT Then AnchorCorrection = BottomAnchor - (ctlChild.Top + ctlChild.Height) If AnchorCorrection <> 0 Then 'Debug.Print "EnforceSashSub " & ChildIndex & " Bottom AnchorCorrection(<>0)=" & AnchorCorrection IsMoveAgain = True cTop = cTop + AnchorCorrection FixY = FixY + AnchorCorrection End If ElseIf (ChildData.Sides And RE_HEIGHT) = RE_HEIGHT Then FixY = FixY + ctlChild.Height - cHeight End If '**************************************************** 'Move child again. '**************************************************** If IsMoveAgain Then ctlChild.Move cLeft, cTop End If '**************************************************** 'Is this correction bigger? '**************************************************** 'Debug.Print "EnforceSashSub " & ChildIndex & " FixY(<>0)=" & FixY & "; CorrectionY=" & CorrectionY If Abs(CorrectionX) < Abs(FixX) Then CorrectionX = FixX End If If Abs(CorrectionY) < Abs(FixY) Then CorrectionY = FixY End If End Sub Sub RemoveControl (ctl As Control) Dim i As Integer Dim j As Integer Dim k As Integer Dim DeleteCount As Integer '-------------------------------------------- 'Check the parent control array. For i = 0 To mControlCount - 1 If mControl(i) Is ctl Then Set mControl(i) = Nothing DeleteCount = 0 For j = 0 To mChildCount - 1 If mChildData(j).ParentIndex = i Then DeleteCount = DeleteCount + 1 ElseIf DeleteCount > 0 Then Set mChildControl(j - DeleteCount) = mChildControl(j) mChildData(j - DeleteCount) = mChildData(j) End If Next 'j mChildCount = mChildCount - DeleteCount End If Next 'i '-------------------------------------------- 'Check the child control array. DeleteCount = 0 For k = 0 To mChildCount - 1 If mChildControl(k) Is ctl Then DeleteCount = DeleteCount + 1 ElseIf DeleteCount > 0 Then Set mChildControl(k - DeleteCount) = mChildControl(k) mChildData(k - DeleteCount) = mChildData(k) End If Next 'k mChildCount = mChildCount - DeleteCount End Sub Sub RemoveForm (frm As Form) Const PROC = "RemoveForm" Dim i As Integer Dim DeleteCount As Integer '-------------------------------------------- 'Remove parent controls. For i = 0 To mControlCount - 1 If mControl(i) Is Nothing Then 'Skip it. ElseIf mControl(i).Parent Is frm Then Set mControl(i) = Nothing End If Next 'i '-------------------------------------------- 'Remove child controls. DeleteCount = 0 For i = 0 To mChildCount - 1 If mChildControl(i).Parent Is frm Then DeleteCount = DeleteCount + 1 ElseIf DeleteCount > 0 Then Set mChildControl(i - DeleteCount) = mChildControl(i) mChildData(i - DeleteCount) = mChildData(i) End If Next 'i mChildCount = mChildCount - DeleteCount '-------------------------------------------- 'Remove forms last. For i = 0 To mFormCount - 1 If mForm(i) Is frm Then Set mForm(i) = Nothing End If Next 'i End Sub Sub xBugItem (ByVal i As Integer) 'd = mChildData(i) 'Set c = mChildControl(i) 'Debug.Print "Child " & i & " (Sides; Horiz; Vert; ParentType; ParentIndex)" 'Debug.Print "Child Data: " & d.Sides & "; " & d.Horizontal & "; " & d.Vertical & "; " & d.ParentType & "; " & d.ParentIndex 'Debug.Print "Child Caption: " & c '.Caption 'If (d.Sides And RE_LEFT) = RE_LEFT Then ' Sides = Sides & "Left " 'End If 'If (d.Sides And RE_LEFT) = RE_LEFT Then ' Sides = Sides & "Left " 'End If 'If (d.Sides And RE_TOP) = RE_TOP Then ' Sides = Sides & "Top " 'End If 'If (d.Sides And RE_WIDTH) = RE_WIDTH Then ' Sides = Sides & "Width " 'End If 'If (d.Sides And RE_HEIGHT) = RE_HEIGHT Then ' Sides = Sides & "Height " 'End If 'Debug.Print "Child Sides: " & Sides 'Debug.Print "Child: Left=" & c.Left & " Top=" & c.Top & " Width=" & c.Width & " Height=" & c.Height 'i = d.ParentIndex 'Select Case d.ParentType 'Case PARENT_FORM ' Debug.Print "Parent Form Caption: " & mForm(i).Caption ' Debug.Print "Parent Form: Left=" & mForm(i).Left & " Top=" & mForm(i).Top & " Width=" & mForm(i).Width & " Height=" & mForm(i).Height 'Case Else ' Set c = mControl(i) ' Select Case d.ParentType ' Case PARENT_CONTAINER ' Debug.Print "Parent Container Caption: " & mControl(i) '.Caption ' Case PARENT_SASH ' Debug.Print "Parent Sash Caption: " & mControl(i) '.Caption ' End Select ' Debug.Print "Parent: Left=" & c.Left & " Top=" & c.Top & " Width=" & c.Width & " Height=" & c.Height 'End Select End Sub Private Sub xBugList () 'Dim i As Integer ' 'On Error Resume Next 'If mChildCount = 0 Then ' Debug.Print "****** There are no lists. ******" ' Exit Sub 'End If 'Debug.Print "****** Lists ******" ' 'If mFormCount = 0 Then ' Debug.Print "*** There are no parent forms." 'Else ' Debug.Print mFormCount & " PARENT FORMS" ' For i = 0 To mFormCount - 1 ' Debug.Print "Parent Form " & i & ": " & mForm(i).Caption ' Next 'i 'End If ' 'If mControlCount = 0 Then ' Debug.Print "*** There are no parent controls." 'Else ' Debug.Print mControlCount & " PARENT CONTROLS" ' For i = 0 To mControlCount - 1 ' Debug.Print "Parent Control " & i & ": " & mControl(i) ' Next 'i 'End If ' 'Debug.Print mChildCount & " CHILD CONTROLS (Sides; Horiz; Vert; ParentType; ParentIndex)" 'For i = 0 To mChildCount - 1 ' Debug.Print "Child " & i & ": " & mChildControl(i) '.Caption ' Debug.Print "Child " & i & ": " & mChildData(i).Sides & "; " & mChildData(i).Horizontal & "; " & mChildData(i).Vertical & "; " & mChildData(i).ParentType & "; " & mChildData(i).ParentIndex 'Next 'i End Sub Private Sub zAppendChild (ChildData As ReChildStruct, ctlChild As Control) If mChildCount = 0 Then ReDim mChildData(0 To 10) ReDim mChildControl(0 To 10) ElseIf mChildCount > UBound(mChildData) Then ReDim Preserve mChildData(0 To mChildCount + 10) ReDim Preserve mChildControl(0 To mChildCount + 10) End If mChildData(mChildCount) = ChildData Set mChildControl(mChildCount) = ctlChild mChildCount = mChildCount + 1 End Sub Private Function zAppendParentControl (ctlParent As Control) As Integer If mControlCount = 0 Then ReDim mControl(0 To 10) ElseIf mControlCount > UBound(mControl) Then ReDim Preserve mControl(0 To mControlCount + 10) End If Set mControl(mControlCount) = ctlParent zAppendParentControl = mControlCount mControlCount = mControlCount + 1 End Function Private Function zAppendParentForm (frmParent As Form) As Integer If mFormCount = 0 Then ReDim mForm(0 To 10) ElseIf mFormCount > UBound(mForm) Then ReDim Preserve mForm(0 To mFormCount + 10) End If Set mForm(mFormCount) = frmParent zAppendParentForm = mFormCount mFormCount = mFormCount + 1 End Function Private Function zExternalScaleModeVB3 (ctlFrame As Control) 'The Parent property is always a form. 'If the control is in a container -- which would 'be the true parent -- then the control's size 'and position are in the ScaleMode of the 'container (not the form). Dim hTrueParentWnd As Integer Dim ChildIndex As Integer Dim frmParent As Form Dim Count As Integer 'Expect errors since master controls can and might be graphic. On Error GoTo zExternalScaleModeTrap hTrueParentWnd = ReGetParentAPI(ctlFrame.hWnd) On Error GoTo 0 Set frmParent = ctlFrame.Parent If hTrueParentWnd = frmParent.hWnd Then 'The control is drawn right on the form. zExternalScaleModeVB3 = frmParent.ScaleMode Else 'We'll have to search for the true parent. Count = frmParent.Controls.Count For ChildIndex = 0 To Count - 1 'Not all controls have handles. If zhControlWnd(frmParent.Controls(ChildIndex)) = hTrueParentWnd Then Exit For End If Next ChildIndex 'We better have found it. If ChildIndex >= Count Then 'This is bad. Just use the form's ScaleMode. zExternalScaleModeVB3 = ctlFrame.Parent.ScaleMode Else 'Get the true parent/container's ScaleMode 'Not all container controls have ScaleModes. zExternalScaleModeVB3 = zInternalScaleMode(frmParent.Controls(ChildIndex)) End If End If Exit Function zExternalScaleModeTrap: 'This control is graphic, does not have a hWnd property, 'and therefore GetParent cannot be used. zExternalScaleModeVB3 = ctlFrame.Parent.ScaleMode Exit Function End Function Private Function zExternalScaleModeVB4 (ctlFrame As Control) 'VB4 has the Container property. 'So, this code gets simpler and better. '------------------------------------- 'The Parent property is always a form. 'If the control is in a container -- which would 'be the true parent -- then the control's size 'and position are in the ScaleMode of the 'container (not the form). ' If ctlFrame.Container Is ctlFrame.Parent Then ' 'The control is drawn right on the form. ' zExternalScaleModeVB4 = ctlFrame.Parent.ScaleMode ' Else ' zExternalScaleModeVB4 = zInternalScaleMode(ctlFrame.Container) ' End If End Function Private Function zhControlWnd (ctl As Control) As Integer 'Lightweight/graphic controls don't have Windows handles. 'So, an error is expected. 'Return 0 for graphic controls. On Error GoTo zhControlWndTrap zhControlWnd = ctl.hWnd zhControlWndTrap: Exit Function End Function Private Function zInternalScaleMode (frame As Control) As Integer 'Some container controls do not have 'a ScaleMode property. 'So, an error is expected. 'In this case the default is TWIPS. On Error GoTo zControlScaleModeTrap zInternalScaleMode = frame.ScaleMode Exit Function zControlScaleModeTrap: zInternalScaleMode = TWIPS Exit Function End Function Private Function zMakeParentControlIndex (ctlParent As Control) 'See if the frame is already stored. 'If it's not, then store it. Dim i As Integer Dim EmptySlotIndex As Integer 'While we're at it, find an empty slot. EmptySlotIndex = -1 For i = 0 To mControlCount - 1 If mControl(i) Is Nothing Then 'We found an empty slot. 'Some frame control must have been unloaded. EmptySlotIndex = i ElseIf mControl(i) Is ctlParent Then 'The control is already in the list. zMakeParentControlIndex = i Exit Function End If Next 'i 'Never found it If EmptySlotIndex < 0 Then 'No empty spots available. zMakeParentControlIndex = zAppendParentControl(ctlParent) Else 'Use the empty slot. Set mControl(EmptySlotIndex) = ctlParent zMakeParentControlIndex = EmptySlotIndex End If End Function Private Function zMakeParentFormIndex (frmParent As Form) 'See if the form is already stored. 'If it's not, then store it. Dim i As Integer Dim EmptySlotIndex As Integer 'While we're at it, try to find an empty slot. EmptySlotIndex = -1 For i = 0 To mFormCount - 1 If mForm(i) Is Nothing Then 'We found an empty slot. 'Some form must have been unloaded. EmptySlotIndex = i ElseIf mForm(i) Is frmParent Then 'The form is already in the list. zMakeParentFormIndex = i Exit Function End If Next 'i 'Never found it. If EmptySlotIndex < 0 Then 'No empty spots available. zMakeParentFormIndex = zAppendParentForm(frmParent) Else 'Use the empty slot. Set mForm(EmptySlotIndex) = frmParent zMakeParentFormIndex = EmptySlotIndex End If End Function Private Function zSluffNeg2Fix (ByVal Amount As Long, rFix As Long) As Long If Amount < 0 Then rFix = rFix - Amount Else : zSluffNeg2Fix = Amount End If End Function Private Function zValidateScaleMode (ByVal ScaleMode As Integer) 'The ScaleMode must be TWIPS or PIXELS. 'Don't hang around or generate an error if it isn't. 'The external scale mode of a form is always TWIPS. 'Ex: If zValidateScaleMode(TWIPS) then ... 'Forms always have the ScaleMode property. 'All mesurements are kept in Twips. ' Twips = Pixels * mTpu ' Pixels = Twips / mTpu zValidateScaleMode = True Select Case ScaleMode Case PIXELS mTpux = Screen.TwipsPerPixelX mTpuy = Screen.TwipsPerPixelY Case TWIPS mTpux = 1 mTpuy = 1 Case Else RepositionMessage = "Form and control ScaleMode must be Twip or Pixel." zValidateScaleMode = False End Select End Function