How To Create Nested Control Arrays in Visual Basic

ID: Q79029


The information in this article applies to:


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

  1. Start a new project in Visual Basic. Form1 is created by default.


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


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


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


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


  6. Run the example and try all the buttons. Toggle the DragMode on and off and drag the command buttons from one picture to another.


Additional query words: 2.00 3.00


Keywords          : kbcode PrgCtrlsStd 
Version           : 1.00 2.00 3.00
Platform          : WINDOWS 
Issue type        : 

Last Reviewed: June 7, 1999