The information in this article applies to:
- Microsoft Visual Basic programming system for Windows,
versions 1.0, 2.0, 3.0
SUMMARY
This article explains how to create an array of picture controls or frames
with an array of child controls (such as command buttons) within each
element of the parent array by using the Windows API functions SetParent
and GetFocus. This is not possible in Visual Basic for Windows without
using the Windows API functions because Visual Basic does not support
overlapping controls. In other words, in Visual Basic alone, you cannot
create controls and then simply move them into position within previously
created controls.
By using the Windows API functions in conjunction with the Load and Unload
methods, you can circumvent this problem and allow dynamic, flexible
structures to be created during execution.
MORE INFORMATION
This article explains how to get the following control structure:
Parent Control Child Controls on Parent
-------------- --------------------------------------------------
Picture1(1) Command1(1), Command1(2), Command1(3), Command1(4)
Picture1(2) Command1(5), Command1(6), Command1(7), Command1(8)
Picture1(3) Command1(9), Command1(10), Command1(11), Command1(12)
Picture1(4) Command1(13), Command1(14), Command1(15), Command1(16)
where Picture1 and Command1 are control arrays.
The following example uses the two API functions (GetFocus and SetParent)
to establish the correct parent/child relationships between an array of
parents (such as picture controls) and an array of children (such as
command buttons). Each child array is placed within one element of the
parent control.
The GetFocus function requires no parameters. The SetParent function
requires two parameters as follows:
Parameter Type and Description
-------------- -------------------------------------
hWndChild HWND Identifies the child window
hWndNewParent HWND Identifies the new parent window
The return value identifies the previous parent window.
This example also demonstrates how Windows handles a drag and drop when
parentage was set at run time. If you drag a control to another control
of the same type as the previous parent and drop it, the released control
assumes the same relative position it had in the previous parent.
The example program demonstrates that before unloading controls nested
using SetParent and moved during the run, parentage should be returned
to the original hierarchy. This avoids the possibility of general
protection (GP) faults or Unrecoverable Application Errors (UAEs) that
could occur due to conflicting messages to Windows.
Step-by-Step Example
- Start a new project in Visual Basic. Form1 is created by default.
- Add a picture box and five command buttons to Form1, giving them the
property settings shown here:
Control Control Name Property Setting
---------------------------------------------------------------
Form Form1
Picture Picture1() Index=1
Command button Command1() Index=1
Command button Loadall TabIndex=0, Caption="Load All"
Command button Loadsome TabIndex=1, Caption="Load Four"
Command button Changemode TabIndex=2, Caption="DragMode=0"
Command button Unloadall TabIndex=3, Caption="Unload All"
NOTE: The placement of the original picture box and command button is
important. The picture should be created first and the command button
drawn within the picture box. The placement of the other controls at
design time is not critical. They are resized and moved at run time.
- Add code to the examaple application. Here is a summary of each of
the Sub procedures you will create:
Code Purpose
---------------- --------------------------------------------------
Loadall_Click Loads all parent controls and all child controls
Loadsome_Click Loads all parent controls and four child controls
Unloadall _Click Unloads all controls. Must reset parentage to the
original first!
Changemode_Click Changes DragMode between auto and manual modes
Resetparent General procedure to reset parentage for Unload and
Close
Cplace General procedure to place the four command buttons
Form_Resize Code to maintain original size
Form_Load Sets initial size and properties of controls
Form_Unload Calls Unloadall_Click to avoid conflicting Windows
messages
Picture1_DragDrop Handles setting of parentage to user actions
Command1_Click Sets captions to reflect change in position and
state
- Add the following code to general declarations section of Form1:
Declare Function setparent% Lib "user" (ByVal h%, ByVal h%)
Declare Function getfocus% Lib "user" ()
Global Handle2Child As Integer
Global Handle2Parent As Integer
Global dragstate, loadstate, toggle, innernum As Integer
Global I, N, K As Integer
Global xoffset, yoffset, cmdnum, childsize, parentsize As Integer
Global Const maxouter = 4
Global Const maxinner = 4
Option Base 1
Global storecaption(16) As String ' array=maxinner*maxouter
- Add the following Sub procedures to the application in apprpriate events
in Form1. Each code statement must be entered as one, single line.
' Enter the following two lines as one, single line:
Sub Picture1_DragDrop (index As Integer, source As Control, X As Single,
Y As Single)
picture1(index).SetFocus
' Procedure for control array of parent Picture Boxes:
Handle2Parent = getfocus()
source.SetFocus
Handle2Child = getfocus()
ret% = setparent(Handle2Child, Handle2Parent)
source.caption = Mid$(source.caption, 1, 1) + "/" +
LTrim$(RTrim$(Str$(index)))
End Sub
Sub Form_Load ()
form1.width = screen.width - screen.width \ 8
form1.height = screen.height \ 2
form1.backcolor = &HFFFF00
form1.caption = "Nested Control Arrays"
picture1(1).visible = 0
command1(1).visible = 0
parentsize = CInt((form1.width \ maxouter) * .8)
childsize = CInt((2 * parentsize \ maxinner) * .6)
picture1(1).height = parentsize
picture1(1).width = parentsize
command1(1).height = childsize
command1(1).width = childsize
cplace loadall, 1
cplace loadsome, 2
cplace changemode, 3
cplace unloadall, 4
End Sub
Sub resetparent () ' Function to clean up parentage before unload.
picture1(1).SetFocus
Handle2Parent = getfocus()
For I = innernum To 1 Step -1
command1(I).SetFocus
Handle2Child = getfocus()
ret% = setparent(Handle2Child, Handle2Parent)
Next I
End Sub
Sub Command1_Click (index As Integer) ' Procedure for control array
' of Buttons.
If toggle Then
command1(index).caption = storecaption(index)
toggle = 0
Else
storecaption(index) = command1(index).caption ' Change caption to
' reflect state.
command1(index).caption = "ON"
toggle = -1
End If
End Sub
Sub Form_Unload (Cancel As Integer) ' Cleans up before program exits.
unloadall_click
End Sub
Sub changemode_Click () ' Toggles between automatic & manual dragmodes.
If loadstate Then
If Not dragstate Then
For I = 1 To innernum
command1(I).dragmode = 1 ' Automatic
dragstate = -1 ' Reset flag.
Next I
changemode.caption = "DragMode=1"
Else
For I = 1 To innernum
command1(I).dragmode = 0 ' Manual
dragstate = 0 ' Reset flag.
Next I
changemode.caption = "DragMode=0"
End If
End If
End Sub
Sub unloadall_click () ' Unloads all dynamically created controls only.
Select Case loadstate
Case 1
resetparent ' Must call prior to unload to avoid GP fault or UAE
For I = maxouter To 1 Step -1
For N = maxinner To 1 Step -1
cmdnum = ((I - 1) * 4) + N
If cmdnum <> 1 Then Unload command1(cmdnum)
Next N
If I <> 1 Then Unload picture1(I)
Next I
command1(1).visible = 0 ' Can't unload controls
picture1(1).visible = 0 ' created at design time so hide!
loadstate = 0 ' Reset flag for load routines.
changemode.enabled = 0
Case 2
resetparent ' Must call prior to unload to avoid GP fault or UAE.
For I = maxouter To 1 Step -1
If I = 1 Then
For N = maxinner To 2 Step -1
Unload command1(N)
Next N
End If
If I <> 1 Then Unload picture1(I)
Next I
command1(1).visible = 0 ' Can't unload controls
picture1(1).visible = 0 ' created at design time so hide!
loadstate = 0 ' Reset flag for load routines.
changemode.enabled = 0
End Select
End Sub
Sub loadsome_click () ' Loads all parents and one set of children
If loadstate = 0 Then ' to demonstrate drag and drop.
changemode.enabled = -1
command1(1).Move 0, 0
For I = 1 To maxouter
If I <> 1 Then ' Can't load control created at design time.
Load picture1(I)
End If
' Enter the following two lines as one, single line:
picture1(I).Move -picture1(1).width, -picture1(1).height,
parentsize, parentsize
picture1(I).visible = -1 ' Load off-screen /\.
Picture1(I).SetFocus
Handle2Parent = getfocus() ' Get handle by API call.
If I = 1 Then
For N = 1 To 4
If N <> 1 Then
Load command1(N) ' Can't load control created at
End If ' design time.
' Enter the following two lines as one, single line:
xoffset = picture1(I).scalewidth \ 4 - command1(N).width
\ 2 + ((N - 1) Mod 2) * (picture1(I).scalewidth \ 2)
If N > 2 Then
' Enter the following three lines as one, single line:
yoffset = picture1(I).scaleheight \ 2 +
(picture1(I).scaleheight \ 4 -
command1(N).height \ 2)
Else
' Enter the following two lines as one, single line:
yoffset = (picture1(I).scaleheight \ 4 -
command1(N).height\2)
End If
command1(N).Move xoffset, yoffset
command1(N).visible = -1
command1(N).SetFocus
Handle2Child = getfocus() ' Get handle by API call.
' Call API function.
ret% = setparent(Handle2Child, Handle2Parent)
' Enter the following two lines as one, single line:
command1(N).caption = LTrim$(RTrim$(Str$(N))) + "/" +
LTrim$(RTrim$(Str$(I)))
Next N
End If
Next I
xoffset = ((form1.scalewidth \ maxouter) - picture1(1).width) \ 2
picture1(1).Move xoffset, 0
For I = 2 To maxouter
' Enter the following line as one, single line:
picture1(I).Move (I - 1) * (form1.scalewidth \ maxouter) +
xoffset, picture1(I - 1).top ' **
Next I
innernum = 4 ' Set global loop maximum.
loadstate = 2
End If
End Sub
Sub loadall_click () ' Loads all parents and children in
If loadstate = 0 Then ' nested structure.
changemode.enabled = -1
command1(1).Move 0, 0
For I = 1 To maxouter ' size command button proportionally.
If I <> 1 Then Load picture1(I) ' Can't load control created at
' design time.
' Load off-screen:
picture1(I).Move -picture1(1).width, -picture1(1).height
picture1(I).visible = -1
picture1(I).SetFocus
Handle2Parent = getfocus() ' Get handle by API call.
For N = 1 To maxinner
cmdnum = ((I - 1) * 4) + N
If cmdnum <> 1 Then
Load command1(cmdnum) ' Can't load control created at
' design time.
End If
xoffset=((N-1) Mod 2)*(picture1(I).scalewidth\(maxinner\2))
If N > 2 Then
yoffset = picture1(I).scaleheight \ 2
Else yoffset = picture1(I).scaletop
End If
' Enter the following command as one, single line:
command1(cmdnum).Move picture1(I).scalewidth \ 8 +
xoffset, picture1(I).scaleheight \ 8 + yoffset
command1(cmdnum).visible = -1
command1(cmdnum).SetFocus
Handle2Child = getfocus() ' Get handle by API call.
' Call API function.
ret% = setparent(Handle2Child, Handle2Parent)
Next N
Next I ' Caption the control array buttons.
For K = 1 To (maxinner * maxouter)
command1(K).caption = LTrim$(RTrim$(Str$(K)))
Next K
xoffset = ((form1.scalewidth \ maxouter) - picture1(1).width) \ 2
picture1(1).Move xoffset, 0
For I = 2 To maxouter
picture1(I).Move (I - 1) * (form1.scalewidth \ maxouter) +
xoffset, picture1(I - 1).top ' **
Next I
innernum = 16 ' Set global loop maximum.
loadstate = 1
End If
End Sub
Sub Form_Resize ()
form1.width = screen.width - screen.width \ 8
form1.height = screen.height \ 2
End Sub
Sub cplace (dummy As Control, num As Integer) ' Size static controls.
theheight% = parentsize + childsize * 2
' Enter the following two lines as one, single line:
dummy.Move (form1.width \ 4) * (num - 1) + parentsize \ 10,
theheight%, parentsize, childsize ' **
End Sub
- Run the example and try all the buttons. Toggle the DragMode on and off
and drag the command buttons from one picture to another.
|