Click here to Skip to main content
15,887,027 members
Articles / Programming Languages / C#
Article

Simple Runtime Control Sizing and Dragging Class

Rate me:
Please Sign up or sign in to vote.
4.90/5 (73 votes)
29 Sep 2003Public Domain2 min read 227.7K   5.6K   99   65
Sample and brief description of simple class that enables sizing and dragging of controls on a form

Image 1

Figure - Form showing selected Label control, following selection

Introduction

The PickBox class provides sizing handles that allow the positioning and sizing on simple controls on a containing form. This C# sample was adapted from an earlier version of the class written in VB (version 6). The sample was prepared using Borland's C# Builder IDE and exported to a VS project.

Using the code

The PickBox class exposes a "WireControl" method that attaches events to a passed control, implementing “pickbox” behavior. Clicking on a “wired” control displays eight sizing handles around the perimeter of the control and enables sizing and dragging of the control via mouse event handlers provided by the class instance (see commented code for details). The following snippet illustrates the use of the PickBox class and this function from within the Sample Form:

C#
//(Excerpt from Winform.cs)
//
// Create an instance of the PickBox class
//
private PickBox pb = new PickBox();
public WinForm() // Sample Form's constuctor
{
    InitializeComponent();
    //
    // Provide a Click event handler for each control
    // that attaches a pick box to the control when clicked
    //
    foreach (Control c in this.Controls) {
        pb.WireControl(c);
    }
}

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.

C#
//(Excerpt from PickBox.cs)
private void SelectControl(object sender, EventArgs e) {
    if (m_control is Control) {
        m_control.Cursor = oldCursor;
        // Remove event any event handlers appended to last control
        // by this class
        m_control.MouseDown -= new MouseEventHandler(this.ctl_MouseDown);
        m_control.MouseMove -= new MouseEventHandler(this.ctl_MouseMove);
        m_control.MouseUp -= new MouseEventHandler(this.ctl_MouseUp);
        m_control.Click -= new EventHandler(this.SelectControl);
        m_control = null;
    }
    m_control = (Control)sender;
    //Add event handlers for moving the selected control around
    m_control.MouseDown += new MouseEventHandler(this.ctl_MouseDown);
    m_control.MouseMove += new MouseEventHandler(this.ctl_MouseMove);
    m_control.MouseUp += new MouseEventHandler(this.ctl_MouseUp);
    //Add sizing handles to Control's container (Form or PictureBox)
    for (int i = 0; i<8; i++) {
        m_control.Parent.Controls.Add(lbl[i]);
        lbl[i].BringToFront();
    }
    //Position sizing handles around Control
    MoveHandles();
    //Display sizing handles
    ShowHandles();
    oldCursor = m_control.Cursor;
    m_control.Cursor = Cursors.SizeAll;
}

The sizing handles are Labels that are created, initialized and stored in an array of Label controls when the instance of the PickBox class is constructed. MouseDown, MouseMove, and MouseUp events service the array of Labels during control sizing operations.

Points of Interest

The class sample works well for simple applications, but may exhibit some interaction within applications employing more complicated, time-critical event handling. In it’s current form it provides for the selection of only one control at a time.

The PickBox sample is a simpler, and probably less versatile C# example of the functionality presented in the C++ sample “A Sizing/Moving widget” by Andrew JM Hall.

History

  • This is the initial submission of the sample.

License

This article, along with any associated source code and files, is licensed under A Public Domain dedication


Written By
Program Manager General Dynamics Mission Systems Canada
Canada Canada
Manager, Customer Training in Ottawa, ON, Canada
www.gdcanada.com

Comments and Discussions

 
AnswerRe: how about rotating ? Pin
JK Rajesh30-Jan-07 18:59
JK Rajesh30-Jan-07 18:59 
AnswerRe: how about rotating ? Pin
Bishoy Demian30-Jan-07 20:14
Bishoy Demian30-Jan-07 20:14 
GeneralVS 2005 Pin
NewbieDude7-Feb-06 0:59
NewbieDude7-Feb-06 0:59 
GeneralRe: VS 2005 Pin
programmingfish18-Aug-06 5:03
programmingfish18-Aug-06 5:03 
GeneralUserControls Pin
dchurch2424-Jan-06 0:04
dchurch2424-Jan-06 0:04 
GeneralAdditional code for handling child controls Pin
Ronit H31-Oct-05 4:01
Ronit H31-Oct-05 4:01 
GeneralRe: Additional code for handling child controls Pin
heckknow2-Jan-07 15:54
heckknow2-Jan-07 15:54 
GeneralVB.NET code for us non-C# folks :) PinPopular
mpemberton27-Oct-05 9:03
mpemberton27-Oct-05 9:03 
I don't take credit for all of this translation. The reasonably good conversion tool by Kamal Patel[^] did most of it, but I did the rest (and tested). Enjoy...

If anyone else has any additional tweaks to share, such as multi-select for block moving, deleting controls, etc, that would be lovely. I'm just now getting started with a new project and this code was EXACTLY what I was looking for! Thank you very much for sharing your expertise!

Public Class PickBox
    '/ summary
    '/ This class implements sizing and moving functions for
    '/	runtime editing of graphic controls
    '/ summary
    '////////////////////////////////////////////////////////////////
    ' PRIVATE CONSTANTS AND VARIABLES
    '////////////////////////////////////////////////////////////////

    Private Const BOX_SIZE As Integer = 8
    Private BOX_COLOR As Color = Color.White
    Private m_container As ContainerControl
    Private m_control As Control
    Private lbl(8) As Label
    Private startl As Integer
    Private startt As Integer
    Private startw As Integer
    Private starth As Integer
    Private startx As Integer
    Private starty As Integer
    Private dragging As Boolean
    Private arrArrow() As Cursor = New Cursor() {Cursors.SizeNWSE, Cursors.SizeNS, _
Cursors.SizeNESW, Cursors.SizeWE, Cursors.SizeNWSE, Cursors.SizeNS, _
Cursors.SizeNESW, Cursors.SizeWE}

    Private oldCursor As Cursor

    Private Const MIN_SIZE As Integer = 20

    '
    ' Constructor creates 8 sizing handles & wires mouse events
    ' to each that implement sizing functions
    '
    Public Sub New()
        Dim i As Integer
        For i = 0 To 8 - 1 Step i + 1
            lbl(i) = New Label
            lbl(i).TabIndex = i
            lbl(i).FlatStyle = 0
            lbl(i).BorderStyle = BorderStyle.FixedSingle
            lbl(i).BackColor = BOX_COLOR
            lbl(i).Cursor = arrArrow(i)
            lbl(i).Text = ""
            lbl(i).BringToFront()
            AddHandler lbl(i).MouseDown, AddressOf Me.lbl_MouseDown
            AddHandler lbl(i).MouseMove, AddressOf Me.lbl_MouseMove
            AddHandler lbl(i).MouseUp, AddressOf Me.lbl_MouseUp
        Next
    End Sub

    '////////////////////////////////////////////////////////////////
    ' PUBLIC METHODS
    '////////////////////////////////////////////////////////////////

    '
    ' Wires a Click event handler to the passed Control
    ' that attaches a pick box to the control when it is clicked
    '
    Public Sub WireControl(ByVal ctl As Control)
        AddHandler ctl.Click, AddressOf SelectControl
    End Sub


    '///////////////////////////////////////////////////////////////
    ' PRIVATE METHODS
    '///////////////////////////////////////////////////////////////

    '
    ' Attaches a pick box to the sender Control
    '
    Private Sub SelectControl(ByVal sender As Object, ByVal e As EventArgs)

        If TypeOf m_control Is Control Then
            m_control.Cursor = oldCursor

            'Remove event any pre-existing event handlers appended by this class
            RemoveHandler m_control.MouseDown, AddressOf Me.ctl_MouseDown
            RemoveHandler m_control.MouseMove, AddressOf Me.ctl_MouseMove
            RemoveHandler m_control.MouseUp, AddressOf Me.ctl_MouseUp

            m_control = Nothing
        End If

        m_control = CType(sender, Control)
        'Add event handlers for moving the selected control around
        AddHandler m_control.MouseDown, AddressOf Me.ctl_MouseDown
        AddHandler m_control.MouseMove, AddressOf Me.ctl_MouseMove
        AddHandler m_control.MouseUp, AddressOf Me.ctl_MouseUp
        AddHandler m_control.Parent.Click, AddressOf Me.Parent_Click

        'Add sizing handles to Control's container (Form or PictureBox)
        Dim i As Integer
        For i = 0 To 8 - 1 Step i + 1
            m_control.Parent.Controls.Add(lbl(i))
            lbl(i).BringToFront()
        Next

        'Position sizing handles around Control
        MoveHandles()

        'Display sizing handles
        ShowHandles()

        oldCursor = m_control.Cursor
        m_control.Cursor = Cursors.SizeAll

    End Sub

    Public Sub Remove()
        HideHandles()
        m_control.Cursor = oldCursor
    End Sub

    Private Sub ShowHandles()
        If Not m_control Is Nothing Then
            Dim i As Integer
            For i = 0 To 8 - 1 Step i + 1
                lbl(i).Visible = True
            Next
        End If
    End Sub

    Private Sub HideHandles()
        Dim i As Integer
        For i = 0 To 8 - 1 Step i + 1
            lbl(i).Visible = False
        Next
    End Sub

    Private Sub MoveHandles()
        Dim sX As Integer = m_control.Left - BOX_SIZE
        Dim sY As Integer = m_control.Top - BOX_SIZE
        Dim sW As Integer = m_control.Width + BOX_SIZE
        Dim sH As Integer = m_control.Height + BOX_SIZE
        Dim hB As Integer = BOX_SIZE / 2
        Dim arrPosX = New Integer() {sX + hB, sX + sW / 2, sX + sW - hB, _
            sX + sW - hB, sX + sW - hB, sX + sW / 2, sX + hB, sX + hB}

        Dim arrPosY = New Integer() {sY + hB, sY + hB, sY + hB, sY + sH / 2, _
            sY + sH - hB, sY + sH - hB, sY + sH - hB, sY + sH / 2}

        Dim i As Integer
        For i = 0 To 8 - 1 Step i + 1
            lbl(i).SetBounds(arrPosX(i), arrPosY(i), BOX_SIZE, BOX_SIZE)
        Next
    End Sub

    '///////////////////////////////////////////////////////////////
    ' MOUSE EVENTS THAT IMPLEMENT SIZING OF THE PICKED CONTROL
    '///////////////////////////////////////////////////////////////

    '
    ' Store control position and size when mouse button pushed over
    ' any sizing handle
    '
    Private Sub lbl_MouseDown(ByVal sender As Object, ByVal e As MouseEventArgs)
        dragging = True
        startl = m_control.Left
        startt = m_control.Top
        startw = m_control.Width
        starth = m_control.Height
        HideHandles()
    End Sub

    '
    ' Size the picked control in accordance with sizing handle being dragged
    '	0   1   2
    '   7       3
    '   6   5   4
    '
    Private Sub lbl_MouseMove(ByVal sender As Object, ByVal e As MouseEventArgs)
        Dim l As Integer = m_control.Left
        Dim w As Integer = m_control.Width
        Dim t As Integer = m_control.Top
        Dim h As Integer = m_control.Height
        If dragging Then
            Select Case (CType(sender, Label)).TabIndex
                Case 0
                    ' Dragging top-left sizing box
                    If startl + e.X < startl + startw - MIN_SIZE Then
                        l = startl + e.X
                    Else
                        l = startl + startw - MIN_SIZE
                    End If
                    If startt + e.Y < startt + starth - MIN_SIZE Then
                        t = startt + e.Y
                    Else
                        t = startt + starth - MIN_SIZE
                    End If

                    w = startl + startw - m_control.Left
                    h = startt + starth - m_control.Top
                    Exit Select
                Case 1
                    ' Dragging top-center sizing box
                    If startt + e.Y < startt + starth - MIN_SIZE Then
                        t = startt + e.Y
                    Else
                        t = startt + starth - MIN_SIZE
                    End If
                    h = startt + starth - m_control.Top
                    Exit Select
                Case 2
                    ' Dragging top-right sizing box
                    If startw + e.X > MIN_SIZE Then
                        w = startw + e.X
                    Else
                        w = MIN_SIZE
                    End If
                    If startt + e.Y < startt + starth - MIN_SIZE Then
                        t = startt + e.Y
                    Else
                        t = startt + starth - MIN_SIZE
                    End If
                    h = startt + starth - m_control.Top
                    Exit Select
                Case 3
                    ' Dragging right-middle sizing box
                    If startw + e.X > MIN_SIZE Then
                        w = startw + e.X
                    Else
                        w = MIN_SIZE
                    End If
                    Exit Select
                Case 4
                    ' Dragging right-bottom sizing box
                    If startw + e.X > MIN_SIZE Then
                        w = startw + e.X
                    Else
                        w = MIN_SIZE
                    End If
                    If starth + e.Y > MIN_SIZE Then
                        h = starth + e.Y
                    Else
                        h = MIN_SIZE
                    End If
                    Exit Select
                Case 5
                    ' Dragging center-bottom sizing box
                    If starth + e.Y > MIN_SIZE Then
                        h = starth + e.Y
                    Else
                        h = MIN_SIZE
                    End If
                    Exit Select
                Case 6
                    ' Dragging left-bottom sizing box
                    If startl + e.X < startl + startw - MIN_SIZE Then
                        l = startl + e.X
                    Else
                        l = startl + startw - MIN_SIZE
                    End If
                    w = startl + startw - m_control.Left
                    If starth + e.Y > MIN_SIZE Then
                        h = starth + e.Y
                    Else
                        h = MIN_SIZE
                    End If
                    Exit Select
                Case 7
                    ' Dragging left-middle sizing box
                    If startl + e.X < startl + startw - MIN_SIZE Then
                        l = startl + e.X
                    Else
                        l = startl + startw - MIN_SIZE
                    End If
                    w = startl + startw - m_control.Left
                    Exit Select
            End Select
            If l < 0 Then
                l = 0
            Else
                l = l
            End If
            If t < 0 Then
                t = 0
            Else
                t = t
            End If
            m_control.SetBounds(l, t, w, h)
        End If
    End Sub

    '
    ' Display sizing handles around picked control once sizing has completed
    '
    Private Sub lbl_MouseUp(ByVal sender As Object, ByVal e As MouseEventArgs)
        dragging = False
        MoveHandles()
        ShowHandles()
    End Sub

    '///////////////////////////////////////////////////////////////
    ' MOUSE EVENTS THAT MOVE THE PICKED CONTROL AROUND THE FORM
    '///////////////////////////////////////////////////////////////

    '
    ' Get mouse pointer starting position on mouse down and hide sizing handles
    '
    Private Sub ctl_MouseDown(ByVal sender As Object, ByVal e As MouseEventArgs)
        dragging = True
        startx = e.X
        starty = e.Y
        HideHandles()
    End Sub

    '
    ' Reposition the dragged control
    '
    Private Sub ctl_MouseMove(ByVal sender As Object, ByVal e As MouseEventArgs)
        If dragging Then
            Dim l As Integer = m_control.Left + e.X - startx
            Dim t As Integer = m_control.Top + e.Y - starty
            Dim w As Integer = m_control.Width
            Dim h As Integer = m_control.Height
            If l < 0 Then
                l = 0
            Else
                If l + w > m_control.Parent.ClientRectangle.Width Then
                    l = m_control.Parent.ClientRectangle.Width - w
                Else
                    l = l
                End If
            End If
            If t < 0 Then
                t = 0
            Else
                If t + h > m_control.Parent.ClientRectangle.Height Then
                    t = m_control.Parent.ClientRectangle.Height - h
                Else
                    t = t
                End If
            End If

            m_control.Left = l
            m_control.Top = t
        End If
    End Sub

    '
    ' Display sizing handles around picked control once dragging has completed
    '
    Private Sub ctl_MouseUp(ByVal sender As Object, ByVal e As MouseEventArgs)
        dragging = False
        MoveHandles()
        ShowHandles()
    End Sub

    Private Sub Parent_Click(ByVal sender As Object, ByVal e As EventArgs)
        Remove()
    End Sub
End Class


Mark
GeneralSaving Layout Pin
kumagKayo22-Aug-05 17:06
kumagKayo22-Aug-05 17:06 
GeneralSuggestion Pin
Carl Mercier11-Jul-05 10:35
Carl Mercier11-Jul-05 10:35 
GeneralWorks great with my Custom Control + How to Unwire in Run Time Pin
babuntu24-Jun-05 12:15
babuntu24-Jun-05 12:15 
GeneralNot able to Work with runtime Control and resizing Pin
VB 8.018-Sep-07 20:22
VB 8.018-Sep-07 20:22 
Questionlimited to certain controls? Pin
davidhart8-May-05 18:18
davidhart8-May-05 18:18 
AnswerRe: limited to certain controls? Pin
25-Jul-05 3:55
suss25-Jul-05 3:55 
GeneralRe: limited to certain controls? Pin
Member 214369825-Jul-05 3:59
Member 214369825-Jul-05 3:59 
GeneralIncreasing productivity Pin
IFreezy19-Apr-05 0:37
IFreezy19-Apr-05 0:37 
GeneralRe: Increasing productivity Pin
youssef29-May-05 12:23
youssef29-May-05 12:23 
GeneralRe: Increasing productivity Pin
youssef29-May-05 12:24
youssef29-May-05 12:24 
GeneralKilled 2 birds w/ one stone Pin
Keyser_WS67-Feb-05 11:25
Keyser_WS67-Feb-05 11:25 
GeneralRe: Killed 2 birds w/ one stone Pin
jkorovessis7-Feb-05 12:47
jkorovessis7-Feb-05 12:47 
GeneralGreat Control Pin
CCosgrove18-Jan-05 19:18
CCosgrove18-Jan-05 19:18 
GeneralRe: Great Control Pin
Anonymous21-Jan-05 13:22
Anonymous21-Jan-05 13:22 
GeneralRe: Great Control Pin
Kaieteur Potaro1-Nov-06 15:41
Kaieteur Potaro1-Nov-06 15:41 
GeneralIt's the thing that I've found for along time Pin
Tung Nguyen14-Nov-04 20:24
Tung Nguyen14-Nov-04 20:24 
GeneralRe: It's the thing that I've found for along time Pin
dgudat8-Jun-05 7:10
dgudat8-Jun-05 7:10 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.