Creating Nested Control Arrays in Visual Basic

ID Number: Q79029

1.00

WINDOWS

Summary:

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 is not possible in Visual Basic without making use of the

Windows API functions SetParent and GetFocus.

Because Visual Basic does not support overlapping controls, creating

controls and then simply moving them into position within previously

created controls will not function correctly. Using the Windows API

functions in conjunction with the Load and Unload methods can

circumvent this problem and allow dynamic, flexible structures to be

created during execution.

This information applies to Microsoft Visual Basic programming system

version 1.0 for Windows.

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 proper parent/child relationships between

an array of parents (such as picture controls) and an array of

children (such as command buttons), where each child array is within

one element of the parent control.

The function GetFocus requires no parameters. The function SetParent

requires two 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 if

parentage was set at run time. If the control is dragged to another

control of the same type as the previous parent, when released the

control assumes the same relative position it had in the previous

parent. The program demonstrates that before unloading controls nested

using SetParent and moved during the run, parentage should be returned

to the original hierarchy. This is to avoid the possibility of

"Unrecoverable Application Error" (UAE) messages that could occur due

to conflicting messages to Windows.

Create the following controls:

Control Ctlname Property Setting

------- ------- ----------------

Form Form1

Picture Picture1() Index=1

Command button Command1() Index=1

Command button Loadall Taborder=0, Caption="Load All"

Command button Loadsome Taborder=1, Caption="Load Four"

Command button Changemode Taborder=2, Caption="DragMode=0"

Command button Unloadall Taborder=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.

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

Run the example and try all the buttons. Toggle the DragMode on and

off and drag the command buttons from one picture to another. Add the

following code to your program:

Global Module

-------------

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

Form1

-----

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 exit

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 UAEs

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 UAEs

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

Load picture1(I) ' can't load control created at design time

End If

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 design time

End If

xoffset = picture1(I).scalewidth \ 4 -

command1(N).width \ 2 +

((N - 1) Mod 2) * (picture1(I).scalewidth \ 2) ' **

If N > 2 Then

yoffset = picture1(I).scaleheight \ 2 +

(picture1(I).scaleheight \ 4 -

command1(N).height \ 2) ' **

'Note: All code statements (in all procedures) MUST be on one line!

Else 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

ret% = setparent(Handle2Child, Handle2Parent) 'Call API function

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

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 nested structure

If loadstate = 0 Then

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

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

ret% = setparent(Handle2Child, Handle2Parent) 'Call API function

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

dummy.Move (form1.width \ 4) * (num - 1) + parentsize \ 10,

theheight%, parentsize, childsize ' **

End Sub

** Note: These statements are shown on multiple lines, but must be on

a single line in Visual Basic's Code window.

Reference(s):

"Microsoft Windows Programmer's Reference Book and Online Resource,"

version 3.0, Visual Basic add-on #1-55615-413-5

Additional reference words: 1.00