Home
    Shop
    Advertise
    Write For Us
    Affiliate
    Newsletter
    Contact

Runtime Control Sizing and Dragging Class

This article extends the Simple Runtime Control Sizing and Dragging Class by Jim Korovessis, as explained in his article at Code Project. The PickBox class provides sizing handles that allow the positioning and sizing on simple controls on a containing form.

 

Jim used C# to implement this class; however, I am using a VB.Net version of that class in my implementation. You can download sample VB.NET project, used in this tutorial.

The major functionality that I have written is identifying Panels and GroupBoxes as Containers and adding/removing controls to them by dragging in/out controls.

The ControlPickBox class exposes a "WireControl" method that attaches events to a passed control, implementing "pickbox" behavior. A control that is wired, when clicked, displays sizing handles around its edges enabling sizing and dragging of the control. This class is limited to allow dragging and resizing of one control at a time.

To use this class, one needs to only create an object of the class and call WireControl method for the controls to wire.

Private CP As New ControlPickBox
 
Private Sub Form1_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
For Each TempControl As Control In Me.Controls
            CP.WireControl(TempControl)
      Next
End Sub

The code above wires all the controls on our form enabling us to resize and move them at run-time. One can use this technique to allow dynamic positioning of the controls as well as complete dynamic layout design of the form.

The "WireControl" method attaches a Click event handler to each passed control. When called the event handler then attaches the "pickbox", made up of eight Label controls that act as sizing handles, to the clicked control. In addition, mouse event handlers are attached to the control allowing for dragging of the control on its parent form. In order to use the class allowing panels and group-boxes, Jim's functionality needed a slight bit of adjustment.

Change;

AddHandler m_control.Parent.Click, AddressOf Me.Parent_Click

To;

AddHandler m_control.FindForm.Click, AddressOf Me.Parent_Click

The reason for this is that a controls Parent can be a Panel or a GroupBox. The Parent_Click method clears selection - i.e. when the user clicks the form, we clears any sizing handles around a selected control, and this we don't want to do when clicking a GroupBox or a Panel parent of a control, in which case we just want to shift the sizing handles to the new control pressed.

Let us now get down to identifying the parent control and adding out selected control to that control when dragged into it.

Private Sub ctl_MouseUp(ByVal sender As Object, ByVal e As MouseEventArgs)
        dragging = False
        AfterMove_IdentifyParent(m_control.FindForm)
        Dim i As Integer
        For i = 0 To 8 - 1 Step i + 1
            m_control.Parent.Controls.Add(lbl(i))
            lbl(i).BringToFront()
        Next
        MoveHandles()
        ShowHandles()
End Sub

Ctl_MouseUp method is executed when the mouse button is released, i.e. the control is released. At this point we set our control at the new position, so this is the place that we need to identify our Parent and add the control accordingly.

When checking if a control is over another control, we use a loop on the Controls collection and check if a control comes on top of another. If so, then we add it to that control, and then again recall the method checking for the controls inside that container control. In other words, when a control is dragged, it can end up inside a panel, or inside a panel that is inside another panel that may be inside a group-box.

Private Sub AfterMove_IdentifyParent(ByRef searchIn As Object)
        Dim repeat As Boolean = False
        Dim tempContainer As Control = Nothing
 
        For Each tempControl As Control In searchIn.Controls
            If tempControl.Equals(m_control) Then
            Else
                If tempControl.GetType.ToString() _
= "System.Windows.Forms.Panel" Then
 
                    Dim topInt As Integer = tempControl.Bounds.Top
                    Dim bottomInt As Integer = tempControl.Bounds.Bottom
                    Dim leftInt As Integer = tempControl.Bounds.Left
                    Dim rightInt As Integer = tempControl.Bounds.Right
 
                    If m_control.Location.X >= leftInt Then
                        If m_control.Location.X <= rightInt Then
                            If m_control.Location.Y >= topInt Then
                                If m_control.Location.Y <= bottomInt Then
                                    Dim myControl As New Control
                                    myControl = m_control
 
                                  myControl.Parent.Controls.Remove(m_control)
                                  myControl.Location = New _
                                   Point(myControl.Location.X – leftInt _
, myControl.Location.Y - topInt)
                                  tempControl.Controls.Add(myControl)
                                  m_control = myControl
                                  If tempControl.Controls.Count > 1 Then
                                      repeat = True
                                      tempContainer = tempControl
                                      Exit For
                                  Else
                                      repeat = False
                                      Exit For
                                  End If
                                End If
                            End If
                        End If
                    End If
                ElseIf tempControl.GetType.ToString() _
= "System.Windows.Forms.GroupBox" Then
                    Dim topInt As Integer = tempControl.Bounds.Top
                    Dim bottomInt As Integer = tempControl.Bounds.Bottom
                    Dim leftInt As Integer = tempControl.Bounds.Left
                    Dim rightInt As Integer = tempControl.Bounds.Right
 
                    If m_control.Location.X >= leftInt Then
                        If m_control.Location.X <= rightInt Then
                            If m_control.Location.Y >= topInt Then
                                If m_control.Location.Y <= bottomInt Then
                                    Dim myControl As New Control
                                    myControl = m_control
 
                                  myControl.Parent.Controls.Remove(m_control)
                                  myControl.Location = _
New Point(myControl.Location.X – _
leftInt , myControl.Location.Y - topInt)
                                  tempControl.Controls.Add(myControl)
                                  m_control = myControl
                                   If tempControl.Controls.Count > 1 Then
                                        repeat = True
                                        tempContainer = tempControl
                                        Exit For
                                  Else
                                        repeat = False
                                        Exit For
                                  End If
                                End If
                            End If
                        End If
                    End If
                End If
            End If
        Next
 
        If repeat = True Then
            AfterMove_IdentifyParent(tempContainer)
        Else
        End If
End Sub

The code above searches through the control collection for Panels and GroupBoxes and checks if the control is dropped onto either of them. If so, then it adds the control to that container, and recalls the same method for that container. Otherwise it exits the method.

In the sample attached with this project, you'll see a GroupBox and a Panel object on the form. Drag the group-box into the Panel, which'll move inside the panel, and then drag-in a text-box and drop it over the group-box. The application will add that text-box into the Group-Box successfully.

Now for the next part; to bring out the control from its container. When the user selects the control, we don't want it to be just moveable inside the container, but also be drag-able outside the container. To do this we need to remove the control from the container and add it to the form.

To accomplish this, I have modified the MouseDown event handler;

Private Sub ctl_MouseDown(ByVal sender As Object, ByVal e As MouseEventArgs)
        dragging = True
        If m_control.Parent.GetType.ToString <> _
"System.Windows.Forms.Form" Then
 
            Dim frmParent As Form = m_control.FindForm
            Dim tempControl As Control = m_control
            tempControl.Location = ctl_FindLocation()
            tempControl.Parent.Controls.Remove(m_control)
            frmParent.Controls.Add(tempControl)
            tempControl.BringToFront()
 
        End If
        start_X = e.X
        start_Y = e.Y
        HideHandles()
End Sub

As you can see, when the mouse button is pressed I am checking if the parent of the control is not a Form. If it isn't, I get a new location for the control, remove it from the container, and add it to the form.

Why do we need to get a new location? Because the location property is defined according to the container. Suppose a button is located at X=5 and Y = 10 in a Panel Control. If we were to move it to the form, then the location would need to be justified according to the form which would be X=5 + Panel.X and Y = 10 + Panel.Y.

That is simple for a single container, but since we are allowing container within container, we need to be cautious about it.

Private Function ctl_FindLocation() As Point
        Dim m_location As Point
 
        If m_control.Parent.GetType.ToString = _
"System.Windows.Forms.Panel" Then
 
            Dim tempParentControl As Control = m_control.Parent
            Dim ParentForm As System.Windows.Forms.Form
ParentForm = m_control.FindForm
            Dim tempX As Integer = m_control.Location.X
tempX = tempX + tempParentControl.Location.X
 
            Dim tempY As Integer = m_control.Location.Y
tempY = tempY + tempParentControl.Location.Y
 
            tempParentControl = tempParentControl.Parent
            While Not tempParentControl.Equals(ParentForm)
                tempX = tempX + tempParentControl.Location.X
                tempY = tempY + tempParentControl.Location.Y
                tempParentControl = tempParentControl.Parent
            End While
            m_location = New Point(tempX, tempY)
 
        ElseIf m_control.Parent.GetType.ToString = _
"System.Windows.Forms.GroupBox" Then
 
            Dim tempParentControl As Control = m_control.Parent
            Dim ParentForm As System.Windows.Forms.Form
ParentForm = m_control.FindForm
 
            Dim tempX As Integer = m_control.Location.X
tempX = tempX + tempParentControl.Location.X
 
            Dim tempY As Integer = m_control.Location.Y
tempY = tempY + tempParentControl.Location.Y
 
            tempParentControl = tempParentControl.Parent
            While Not tempParentControl.Equals(ParentForm)
                tempX = tempX + tempParentControl.Location.X
                tempY = tempY + tempParentControl.Location.Y
                tempParentControl = tempParentControl.Parent
            End While
            m_location = New Point(tempX, tempY)
        Else
            m_location = m_control.Location
        End If
 
        Return m_location
End Function

The method above calculates the new location for our control. What it does is;

  1. Control.X = Control.X + ControlParent.X

  2. Control.Y = Control.Y + ControlParent.Y

  3. Remove Control from ControlParent

  4. Add Control to ControlParent.Parent

  5. If ControlParent.Parent Is Not a Form, then repeat.

That's all there is to it. Try out the sample application attached with this tutorial. You can reach me at msalmank@gmail.com. Feel free to send me questions or feedback. I would like to express a special thanks to Jim Korovessis for his wonderful class that made this article possible.


Tutorial toolbar:  Tell A Friend  |  Add to favorites  |  Feedback  |   


comments powered by Disqus