Full Version: Expand/collapse Multiple Subforms
UtterAccess Forums > Microsoft® Access > Access Forms
Knuckles
Been looking for a solution for this for a while but can't seem to find it anywhere. Here's the situation I have a form where I placed a subform that lists all projects in the database. What I'd like to do is create a separate subform for the projects with each subform based on a different status such as "created", "In Progress", "completed" etc. Ideally what I would like to see would be the Statuses listed one on top of the other with a button next to it to expand or contract the data beneath it. I was able to create a version where I could hide or display the subform but I'm trying to get each section to be dynamic. Does that make sense? Any help would be appreciated. I'm good at dreaming this stuff up but I struggle with the code to make it happen.
pere_de_chipstick
Hi knuckles
It sounds as though you need something like a tree view control - Do a search on UA with 'treeview' as the criteria.
IMHO tree view controls are quite complex to implement, an alternative may be to use a single sub form with an additional control, eg a combo box or an option group, on the main form, where the selection in the additional control limits the records appearing the subform.
hth
Knuckles
Sorry I have not responded sooner. Thanks for the response. I think what I want to do is create several subforms (one for each status) size them as .5 high and postion them one above each other. What I'll need to do is somehow when I expand any subform (let's say I expand it to 3 inches) I'll need a method to move every control below that subform down 2.5" and increase the size of the form by the same amount. Does that sound like something that should be do-able?
pere_de_chipstick
Hi Knuckles
Well certainly your suggestion is possible, however whether you ought or should implement it like that is another matter!
First I take it that the status value of 'Created', 'In progress', 'Completed' etc, is defined in a single field in the table (if it is not then you are probably breaking normalisation rules and this needs to be addressed first)
If you are using the one field then you only need one sub form.
If the number of status values is fixed then you can use a series of check boxes to select the records with status values you want to display in the sub form. Note however that you will not be able to add any more status values in the future without a form design change.
If you do need to add more status values in the future you could perhaps use a multi-select list box.
Note that the status value stored should be numeric e.g. 1 = 'Created', 2 = 'In progress', 3 = 'Completed' etc.
Use code in the after update event of your check boxes or list box to write a filter statement to the sub form e.g.;
CODE
for i = 1 to 4
    If Me("Chkbox" & i) = -1 then strStatusIDs = strStatusIDs & i & ","
Next
if strStatusIDs <>"" then
    strStatusIDs = Left(strStatusIDs, Len(strStatusIDs)-1) 'remove last comma
    Me.SubFormName.Form.Filter = "StatusID IN (" & strStatusIDs  & ")"
Else
   Me.SubFormName.Form.Filter = ""
End if
Me.SubFormName.Form.Requery

(NOT TESTED)
Opersonally would not try to expand / contract the sub form, (and the controls placed under it) depending on whether a status has been selected or not as IMHO makes the form difficult to use. Just leave the sub form blank when no status is selected.
hth
Knuckles
PDC,
do have the statuses in their own table where each status is auto numbered and I am using the auto number as the primary key. I really like your suggestion about the multi select list box. I have not tried to pull that off yet. Is it easy?
Knuckles
pere_de_chipstick
Hi knuckles
o use a mutli select list box you have to first of all set the list box multi select property to either Simple or Extended, in your scenario you would need to set it to 'Extended'
Then in the list box event, in (probably) the on click event, you need to generate the filter string as follows (NOT TESTED):
CODE
Private Sub ListSubj_Click()
On Error GoTo Err_Proc
    
    Dim strFilter As String
    Dim VarItm As Variant, ctl As Control
    Set ctl = Me.ListSubj
    
    For Each VarItm In ctl.ItemsSelected
        strFilter = strFilter & ctl.ItemData(VarItm) & ","
    Next VarItm
    if strFilter <>"" then
         strFilter = Left(strFilter, Len(strFilter)-1)       'remove last comma
         Me.SubFormName.Form.Filter = "StatusID IN (" & strFilter  & ")"
    Else
       Me.SubFormName.Form.Filter = "StatusID=0"    'Hide subform records if nothing selected in list box
    End if
    Me.SubFormName.Form.Requery

Exit_Proc:
    Exit Sub

Err_Proc:
    MsgBox Err.Description
    Resume Exit_Proc
    
End Sub

Notes:
1. This will not automatically set the status for any new records you add to the sub form, as would a master/Child link between the main form and sub form.
2. If you sort on the status ID in the sub form's record source query, then this will list the records on the sub form by each status type and will be closest to the scenario you described in your earlier posts.
hth
bulsatar
Saw your initial post and I wanted to give a try for an example, just for fun. Open form2 in this db and then resize the form large enough so you can see what is going on. Some things that you could add to it are: resize form when collapsing subforms, resize window when resizing forms, add functionality to move controls to the right of the subforms as currently only controls below the subforms will be moved.
ight play with this a little more and get this into a resizing module for dynamically changing control location when resizing a form (kinda like a web page)
have fun with it!
Knuckles
Looking forward to giving it a try. If no status is selected will it return everything or nothing?
bulsatar
I didn't do anything with records on this one. This is just the manipulation of the size and placement of the subforms relative to the movement of the others. The records could easily be incorporated into the expand button by setting the subform recordsource to a query or sql statement
Knuckles
That's pretty mcuh exactly what I was looking for. Totally awesome! I'll need to monkey around with it a bit. My vision is 4 subforms, all the same in the sense they will display project fields such as name, customer, due date, etc. The only difference in the forms will be that they will display different statuses. so form 1 might be "created", form 2 - "In Progress", form 3 - "completed", etc.
ach subform will have a subform set as a datasheet. This way I can roll up some info into the header of the subform.
Thanks for taking the time to try it. Hope it was fun for you.
Knuckles
Knuckles
B,
ish I knew vba better than I do. I tried to make a few modifications but not very successful.
Ochanged the default size of the subs.
I was able to change the speed (very cool by the way)
What I couldn't do is prevent the width of form2 from getting wider which added the horizontal scroll bar.
Also noticed that form2 grows as needed and adds the vertical scroll bars but when you close the subs form2's height does not shrink.
What would the code look like without the speed function? Not that I wouldn't use it but I'm trying to get a handle on the vba.
Any chance you can take another pass at this and add comments so I understand what you did? If you teach a man to fish...
Knuckles
bulsatar
I am going to go ahead and add the code below with all of the comments. If you have any questions after reading through, please ask. I am going to go ahead and leave the changes you asked for for you to do, but I will give you plenty of hints (see after code).
!--c1-->
CODE
Private Sub MoveCtls(frm As Form, ctl As Control, speed As Integer)
'frm = form we are moving the controls in
'ctl = control that we are basing everything on
'speed = how fast the controls will be moved
Dim obj As Control
'cycle through all of the controls on the form
For Each obj In frm.Controls
    'if the control being looked at is below the control we are basing everything off of then
    If obj.Top > (ctl.Top + ctl.Height + 1) Then
        'we move the control by the amount passed to the function
        obj.Top = obj.Top + speed
    End If
Next
End Sub
Private Sub ResizeCtl(frm As Form, ctl As Control, speed As Integer)
'frm only used to pass the form to the MoveCtls function and the ResizeFrm function
'ctl = we need the control that will be changing in size
'speed = speed at which the control resizes
'establish some variables
Dim ctlX As Integer     'the control's x value (width)
Dim ctlY As Integer     'the control's y value (height)
Dim subfrmX As Integer  'the form's x value
Dim subfrmY As Integer  'the form's y value
Dim going As Boolean    'used to stop the growth in the loop
ctlX = ctl.Width                    'set the control's x value
ctlY = ctl.Height                   'set the control's y value
subfrmX = ctl.Form.Width            'set the subform's x value
subfrmY = ctl.Form.Detail.Height    'set the subform's y value
going = True                        'set the initial state of the yes/no variable
'is the control shrinking or growing?
If speed > 0 Then
    Do While going = True  'set the loop, it increments the size so
                           'we have to have a loop to keep it growing/shrinking
                           'until it is where we want it.
        If ctlX < subfrmX Then Call ResizeFrm(frm, speed, 0): ctl.Width = ctl.Width + speed: ctlX = ctlX + speed
        'if the control width is smaller than the subform, first resize the parent form to accomodate the new size
        'of the control and make sure we don't get an "out of space" error when moving the control
        'then increase the width of the control by the number of twips determined by the speed (more twips = faster)
        'then increase the variable by the same amount so that everything stays even.
        If ctlY < subfrmY Then Call ResizeFrm(frm, 0, speed): ctl.Height = ctl.Height + speed: ctlY = ctlY + speed: Call MoveCtls(frm, ctl, speed)
        'in addition to increasing/decreasing the size of the form, we also need to move any controls that we have below the one
        'we are resizing so that way they don't over lap.
        If ctlX >= subfrmX And ctlY >= subfrmY Then going = False 'check to see if it is time to stop growing/shrinking
    Loop
Else
    Do While going = True
        If ctlX > startX Then ctl.Width = ctl.Width + speed: ctlX = ctlX + speed
        If ctlY > startY Then ctl.Height = ctl.Height + speed: ctlY = ctlY + speed: Call MoveCtls(frm, ctl, speed)
        If ctlX <= startX And ctlY <= startY Then going = False
    Loop
End If
End Sub
Private Sub ResizeFrm(frm As Form, Xinc As Integer, Yinc As Integer)
'frm = need the form we are resizing
'Xinc = we need the amount we are going to increase the width by
'Yinc = we need the amount we are going to increase the height by
frm.Width = frm.Width + Xinc
frm.Detail.Height = frm.Detail.Height + Yinc
End Sub

So, when coming at a problem I usually follow this logic: What am I trying to accomplish? -> What attributes do I need to change to get it to do that? -> Can I figure out a way to do it through VBA? -> If not, then has someone else figured it out? -> If not, do I need to start looking at windows APIs.
Using this to approach your changes: I am trying to accomplish shrinking the form, to do that I will have to change the form's width and height, I might be able to figure it out, but someone else already figured out how to increase it so maybe all I have to do is send it the opposite sign to get it to shrink?, pretty sure I don't need any apis.
Let me know if this works for you
~~ I like to keep similar things in an If statement together so using a colon in a line will tell VBA that this is actually a "new line" (or next item) of code so instead of writing an if statement like this
CODE
If a = b Then
     i = 1
     c = 2
End If

I would write it like this:
If a = b Then i = 1: c = 2
(added clarification for how I write code)
bulsatar
Also something to think about, Form size is different than Window size. To change the size of the window (if it is not maximized but a popup or just smaller) then you would have to start using APIs. Well, Resize might work...haven't tried it before.
Knuckles
B,
uch obliged - copied your new code into the database. Now I just need to pick at it until it clicks for me. I am starting to realize that so much of what I try to build from scratch is out there somewhere. Hoping I can tap into UA's fantastic resources instead of re-inventing the wheel.
Thanks for the help.
Knuckles
Knuckles
B,
I have tried to use this in a DB that I am currently working on. After expanding and collapsing twice it consistently crashes when I try a third time. I think it's because each time it expands it becomes cumulative. Might there be a way to save the default detail height and width of the parent form and reset it on the false value of the toggle button?
Also, I am not grasping where you set the end of the loop to determine the expanded size of the subform. I see that you set widths for the control and the subform. I am a little confused because I thought the subform was the control.
I have attached a stripped down version of the database so you can see what's happening.
Knuckles
pullhair.gif
bulsatar
Correct, the crash is because it keeps expanding too big. To keep it from doing that, we will have to make the form contract when we close the subform. In order to accomplish that, all we have to do is add the Call ResizeFrm(frm,speed, 0): and Call ResizeFrm(frm, 0, speed): to the same spots in the second Do...Loop. This will add the code to shrink the form when the subform resizes because we are passing the negative value to the the function (shrinking it).
This is how forms, subforms/subreports work (and pretty much how Access lays out everything). Everything that you add to a form is a control. Those controls can then contain other items such as a subform, a subreport, a web page and much more. How this code works is it "goes into" the control and looks at the object of the subform and gets the size of the subform, which doesn't change even though the control on the form may be set to zero height and zero width. The code then takes that value and increments the control's size until the control is the same size as the subform. So I guess you could think of the control as a "window" that looks at another object on the other side. Even though the "window" may change size, the object on the other side does not.
bulsatar
I have attached another example db. This one includes moving controls to the right of the subform and shrinking the form each way. There are only small alterations to the code and it is still annotated so feel free to take a look.
It isn't perfect, for instance it is possible to mess up the arrangement of the items to the right and if the inital size set on the form open is too small, it will cause the program to error. Let me know if there are any specific questions after looking at the code.
Knuckles
I will take a look at this as soon as I get something off my desk. In the previous one I was able to get it to work multiple times when I increased the speed to 300. When I had the speed set to 50 this is where it crashed.
For Each obj In frm.Controls
If obj.Top > (ctl.Top + ctl.Height + 1) Then 'If obj.Top (which shows up as 780) > (60 + 410 + 1) so if 780 > 471 then
obj.Top = obj.Top + speed 'obj.Top = 780 + 50 (or 830)
End If
Next
What does the speed have to do with the size?
You also wrote "if the inital size set on the form open is too small, it will cause the program to error."
Initial size of the form or control? Sorry, where is this set again?
Thanks for your patience.
bulsatar
The initial size is part of what was changed in the functions. I moved the functions to a separate module so that way it can be accessed from any form. In order to do that, I had to add two variables for the initial size of the subforms so that way the function "knows" what size to shrink the subform controls to. If the initial size is too small, then the code keeps trying to move the controls ontop of eachother or off of the form.
For your other part, speed is the amount of twips that the subform control is resized and how far the other controls are moved each time the code runs. Making it faster should not have made a difference to that error...I don't know what happened there.
Knuckles
Just looked at the new one. Pretty slick. I am seeing that there is a relationship between speed and how close the controls are to the subforms. Haven't quite figured it out yet but I think I will.
Do you think I should use 500 and 300 as you have started with as my standard?
It looks like the slower the speed, the closer I can place other controls (buttons, etc.) to the subforms. That make sense to you?
Really appreciate your help on this. I must owe you several Mangos!
Can we talk next week?
Knuckles
bulsatar
I usually check the threads at least once a day so after you have had a chance to look at the code, feel free to ask any questions you need.
pere_de_chipstick
Hi Knuckles
Just thought I would post a sample showing the technique I suggested earlier:
Hold the CTRL key down to select items individually in the list box, or click and drag to select the adjacent status values in the list.
This is a lot simpler than trying to adjust the size of multiple-subforms, and IMHO, more flexible as you are not limited to a fixed number of status values that you can display at any one time.
Knuckles
Hi Bernie,
o I downloaded your sample but I have a question. I see that I can make multiple selections but I don't understand what it does. I was thinking that the project list on the right should requery based on the statuses selected but it does not. How would this need to work? Can there simply be a button added to requery the project list based on the criteria selected?
Knuckles
pere_de_chipstick
Hi Knuckles
When you select an item in the list box on the left then the list of projects meeting the selected status should appear in the sub form on the right,
ie select 'Created' in the list box and the following two items should appear in the sub form
Project 13 .... Created
Project 1 ......Created
select 'Design' as well as 'Created' in the list box and the sub form should show:
Project 13 ..... Created
Project 1 ...... Created
Project 16 .... Design
Project 5 ...... Design
etc. etc.
is this not happening?
If not, what version of Access are you using? (I used A2007 and saved it in A2003 format)
NB there are some status values where there is no project at that status.
Knuckles
I am still using Access 2003. Nothing at all happens when I make a selection. I stepped through the procedure and did not receive any bugs but nothing ran.
nuckles
pere_de_chipstick
Hi Knuckles
've checked the database in A2003, and you need to add the line
Me.SubfrmProjects.Form.FilterOn = True
Before the 'Exit_Proc:' statement in the Private Sub listSubj_Click()
hth
Knuckles
That did the trick! Very cool. Is there a way to have the project list by default show all projects? Clicking the clear button would then remove the filters and display all projects. Will I be able to use multiple lists to filter on multiple criteria?
Is it possible to do something similar but instead of using a multi-select list have a series of checkboxes? One for each status.
ou've got me rethinking the way I have approached this in the past. Thanks for the effort.
Knuckles
pere_de_chipstick
Hi Knuckles
Glad you liked it;
1. >>Is there a way to have the project list by default show all projects?<<
On the sub form set the Allow Filters property to 'No'
2. >>Clicking the clear button would then remove the filters and display all projects. <<
I've added a second button caption "Clear filter" with the On Click Event code:
CODE
    Me.listSubj.Requery     'Clears any selection in the list box
    Me.SubfrmProjects.Form.FilterOn = False   'removes the filter and displays all projects in the sub form

3. >>Will I be able to use multiple lists to filter on multiple criteria?<<
You can, but life gets a little more complex, your VBA code would need to be triggered when changing either (all) lists and would need to add the criteria from both (all) lists . You might be able to use e.g. "StatusID IN ( a,b,c,d,e) AND AnotherID IN (l,m,n,o,p)"
but I've not tried/tested this.
4. >>Is it possible to do something similar but instead of using a multi-select list have a series of checkboxes? One for each status. <<
Yes, but...
If your end user decides to add (or delete) a status then you would need to redesign your form and add or delete the relevant check boxes. In my experience, however many criteria / status you have provided the end user always wants something different. With the list box all you need do is add (or delete) the relevant record in the underlying table and the form takes care of itself.
hth
Knuckles
PDC,
xcellent, I'll change the settings and add the command button with the code.
The reason I asked about the multiple lists is that the group here may want to filter as such (for example)
Status = "in progress" and "on hold" for Customer "A", "L" and "W" where the project managers are Tony and Grace.
Oget your point about the check boxes but I like the concept which was a "team" request if you know what I mean.
I had worked with someone before that developed something similar. There was a limit (say 25 records) and he would have the checkboxes built but not visible if there wasn't a record.
If the "Status" table was set to max out at 15 records but there were only 11 statuses then only 11 checkboxes would be visible with the status name next to each one.
HAs a new record would be added it would then show the new status and check box.
Thanks for the help. I'll let you know after I make the updates.
Knuckles
Knuckles
B,
continue to fiddle around with it and how I want to use it in "real life". Attached is my idea for part of a dashboard scheme. Here's what I'm seeing that I would change if I could. If you expand the "notes" subform the main form grew in both directions even though there is no need to. If you expand either of the other subs the main forms grows cumulatively. In theory I would envision I could place a 4th sub on this form and have them all open without resizing the main.
Is this getting to complicated to pursue? I'm fascinated by what you have done so far but my skills are quite limited.
Knuckles
pere_de_chipstick
Hi Knuckles
If you are going to use check boxes, you will need to relate the name of the check box to the ID of the record it relates to, if you effectively code your check box names then you could identify them numerically say chkStatus1 to chkStatus15 then use:
CODE
'Check project status
for i = 1 to 15
   if Me("ChkStatus" & i) = -1 then strChkStat = strChkStat & i & ","
Next i
If strChkStat <> "" Then
    strChkStat = Left(strChkStat, Len(strChkStat) - 1)     'remove last comma
    strChkStat = "StatusID IN (" &  strChkStat & ") AND "
End if
'Check for  companies (in list box)
For Each VarItm In ctlComps.ItemsSelected
    strChkCo = strChkCo & ctlComps.ItemData(VarItm) & ","
Next VarItm
If strChkCo <> "" Then
    strChkCo = Left(strChkCo, Len(strChkCo) - 1)     'remove last comma
    strChkCo = "CompanyID IN (" &  strChkCo & ") AND "
End if
'Check project manager (in list box)
For Each VarItm In ctlMngrs.ItemsSelected
    strChkMngr = strChkMngr & ctlMngrs.ItemData(VarItm) & ","
Next VarItm
If strChkMngr <> "" Then
    strChkMngr = Left(strChkMngr, Len(strChkMngr) - 1)     'remove last comma
    strChkMngr = "ManagerID IN (" &  strChkMngr & ") AND "
End if
'Create Subform filter string
strFrmFilter = strChkStat & strChkCo & strChkMngr
IF strFrmFilter <>"" then
    strFrmFilter = Left(strFrmFilter , Len(strFrmFilter) - 5)     'remove last ' AND '
    Me.SubfrmProjects.Form.Filter = strFrmFilter
Else
    Me.SubfrmProjects.Form.Filter = "StatusID=0"
End If
Me.SubfrmProjects.Form.FilterOn = True

NOT TESTED!
You will have to ensure the click of each of your check boxes triggers the code.
I would seriously advise against using checkboxes where the companies and managers are concerned.
(I've assumed an 'AND' function between the Status, Company and Manager.)
hth
Knuckles
Hi PDC,
I have not yet gotten to try this out. What is your adversion to the checkboxes? Although the list box works great, having multiple listboxes for the various filtering options might take up a bit of real estate. Would it work with combo boxes, that is can I do multiple selects in a combo bow?
I'm trying to come up with an interface that is clean and easy to use.
Knuckles
pere_de_chipstick
Hi Knuckles
don't really have an aversion to check boxes, just that in some instances they can be limiting. Where you have a fixed number of status values that won't change, or will never be more than a fixed number of instances, then you could 'get away with it'; my concern though is that in virtually every database I have produced the number of 'fixed' values at the beginning of development is rather less than the number of 'fixed' values at the end of development, - you have to build in more than enough capacity to cope with these changes. Hence I don't use check boxes except where there is only one relevant option ie: for a life assurance company a question might be 'Deceased: Yes/No', though even this may not be sufficient as it could/should be 'Yes/No/Not Known'
Where it comes to other options e.g. 'companies' and 'managers' then these will inevitably change as a company takes on more projects and clients as it expands or takes over other companies; and managers taken on, retire, leave etc. Even where a manager takes over from a previous incumbent - a new manager may not be appointed for a completed project. in these cases simply changing the name in the managers list would lose any 'history' and make the project entry incorrect.
Combo boxes are not a solution as you cannot mult-select them, you could limit the size of a list box to show (say) three records in the list, and the vertical scroll bar will allow the rest of the list to be accessed/viewed.
In A2007, and later, you could use a combo box and multi-value field - but this opens up a whole new can of worms.
hth
Knuckles
That all makes sense. It's a journey. Thanks.
nuckles
pere_de_chipstick
Hi Knuckles.
ad you considered using cascading combo boxes rather than list boxes.
If you are able to group (e.g.) your companies by different categories, for instance project types, preffered suppliers, geographical area - in fact any categorisation that is relevant to your business, you could use the categorisation to limit the project list to the selected category. At the same time the selection can limit a second combo box to those companies also meeting the selected criteria, and selecting from that second list would then limit the project list to the one company.
You can approach the manager's list in a similar way.
Whilst cascading combo boxes may not give you the complete flexibilty of being able choose any set of individual criteria; by effectively predefining filters in this way you can limit your project list that makes your system easier for your users, rather than expecting them to select individual criteria separately.
hth
Knuckles
Hi Pere,
I use cascading combos a lot for data such as only showing the buyers based on the copany selected or the brand after a division is selected. In the past I have created a single form that I launch most of the reports from. On that form I place a combo box for pretty much all the fields I think it makes sense to filter on. Customer, sales rep, project manager, status, etc and a dat range for the due date based on 2 date fields. Works really well except for one thing. I use Like "*"&[form].[field]&"*" as my default criteria so if nothing is selected it will return everything. Problem is that since I am using the stroed number as opposed to the text I get more results than I should sometimes. Is there a better default to start with?
Knuckles
pere_de_chipstick
Hi Knuckes
You could try just using FieldName Like '" &[form].[field]&"*'"
to just have fields start with the name.
For allow the user to select e.g.
Starts with: (Like '" &[form].[field]&"*'" )
Includes: (Like '*" &[form].[field]&"*'" )
Ends with: ((Like '*" &[form].[field] & "'" )
Another option might be to add another table to your db and add all the categories to which the subject belongs to.
Your filter criteria you then just be on the PK of this table.
hth
Knuckles
The problem is that let's say that WalMart is customer ID 1. Tesco is 21 and ShopRite is 17. If I select WalMart (1) they will all show up in the set of records returned. The way I've gotten around it is that I start my numbering with 101 insetad of 1. Pretty sneaky huh?
Is there some way of using null or "" as part of the solution?
If nothing is selected then return all records else return what I've selected.
pere_de_chipstick
Hi Knuckles
I'm not sure if I have got a clear picture of your scenario in mind.
If you want to pick a specific customer say Tesco - 21 then your criteria should be "CustomerID=21'
"FieldName =" &[form].[field]
If you are filtering a PK field you should be very wary of using 'Like'. PKs are (or should be) assigned automatically by your database and should not normally be imbued with any other meaning. As you add more records it becomes inevitable that the 'like' parameter will produce results which are not relevant to your filter/search.
If you want to be able to show all records as a default then you can use:
'Fieldname > 0' or 'FieldName Is Not Null'
For simply not specify any filter (the filter string = "")
hth
bulsatar
I have been fiddling with the code some more, but I think this will be it for a bit. The only thing now is that a "home" position and "restore" position needs to be coded in...and it probably could be better built using classes...and maybe a little broader in the implementation so that it could be used for not just subforms, but moving controls within a form when resizing the form (eh, maybe leave that out separately.. < ), but all in all it still seems to move pretty fast and accurately. It gets a little wonky in the positioning when closing "layered" items....just fiddle with it and you will see what I mean. I put in a good amount of comments which should help walk you through the new code.
Good luck <
This is a "lo-fi" version of UA. To view the full version with more information, formatting and images, please click here.