Click here to Skip to main content
15,890,845 members
Articles / Programming Languages / Visual Basic
Article

Allow the User to Resize Controls at Runtime

Rate me:
Please Sign up or sign in to vote.
4.85/5 (44 votes)
2 Oct 2007CPOL3 min read 177K   8.3K   51   35
Enable resizing of controls on a form at runtime with this class and two lines of code.

Introduction

After searching the Web (including this site) for some simple code that would allow end users to resize controls at runtime, I finally concluded that there was not anything really effective yet really simple that I could even use as a base. I was perfectly willing to modify something to do what I wanted it to do, but most implementations were either buggy, or over-complicated, or just plain bad. I didn't find a single implementation that actually worked well.

Eventually, I decided to write my own. I did, and it was relatively simple (took about 2 hours to write), and it is very easy to use.

Background

Basically, all we are trying to do here is create something that will allow the user to resize and move controls around at runtime, similar to how we developers are able to do at design-time. This code gives you (the developer) a fairly solid baseline from which you can create any custom behavior you want. Compared to other implementations I've found, this is the simplest and most effective I've seen.

Using the Code

It basically consists of a class that can be included anywhere in your project, and then a couple of lines of code to "hook" the class to the control that you want to enable for runtime resizing.

To start with, let's just look at how you "hook" the control (in other words, how the class is used to make any control resizable at runtime):

VB.NET
Public Class Form1

    Dim rc As ResizeableControl

    Private Sub Form1_Load(ByVal sender As System.Object, 
    ByVal e As System.EventArgs) Handles MyBase.Load

        rc = New ResizeableControl(pbDemo)

    End Sub

End Class

That's it! Simple enough, right?

You'll want to keep the rc variable at the form level so that it doesn't drop out of scope. When the form loads, just create an instance of ResizeableControl, pass it the control that you want to be resizable (a picturebox named pbDemo in this example, but it could be any control), and assign the new instance to the form level rc variable. If you wanted multiple controls to be resizable, you might create an array or collection of ResizeableControl objects at the form level.

Now let's take a look at the class itself:

VB.NET
Public Class ResizeableControl

    Private WithEvents mControl As Control
    Private mMouseDown As Boolean = False
    Private mEdge As EdgeEnum = EdgeEnum.None
    Private mWidth As Integer = 4
    Private mOutlineDrawn As Boolean = False

    Private Enum EdgeEnum
        None
        Right
        Left
        Top
        Bottom
        TopLeft
    End Enum

    Public Sub New(ByVal Control As Control)
        mControl = Control
    End Sub

    Private Sub mControl_MouseDown(ByVal sender As Object, _
        ByVal e As System.Windows.Forms.MouseEventArgs) _
        Handles mControl.MouseDown

        If e.Button = Windows.Forms.MouseButtons.Left Then
            mMouseDown = True
        End If
    End Sub

    Private Sub mControl_MouseUp(ByVal sender As Object, _
        ByVal e As System.Windows.Forms.MouseEventArgs) _
        Handles mControl.MouseUp

        mMouseDown = False
    End Sub

    Private Sub mControl_MouseMove(ByVal sender As Object, _
    ByVal e As System.Windows.Forms.MouseEventArgs) _
    Handles mControl.MouseMove

        Dim c As Control = CType(sender, Control)
        Dim g As Graphics = c.CreateGraphics
        Select Case mEdge
            Case EdgeEnum.TopLeft
                g.FillRectangle(Brushes.Fuchsia, _
            0, 0, mWidth * 4, mWidth * 4)
                mOutlineDrawn = True
            Case EdgeEnum.Left
                g.FillRectangle(Brushes.Fuchsia, _
            0, 0, mWidth, c.Height)
                mOutlineDrawn = True
            Case EdgeEnum.Right
                g.FillRectangle(Brushes.Fuchsia, _
            c.Width - mWidth, 0, c.Width, c.Height)
                mOutlineDrawn = True
            Case EdgeEnum.Top
                g.FillRectangle(Brushes.Fuchsia, _
            0, 0, c.Width, mWidth)
                mOutlineDrawn = True
            Case EdgeEnum.Bottom
                g.FillRectangle(Brushes.Fuchsia, _
            0, c.Height - mWidth, c.Width, mWidth)
                mOutlineDrawn = True
            Case EdgeEnum.None
                If mOutlineDrawn Then
                    c.Refresh()
                    mOutlineDrawn = False
                End If
        End Select

        If mMouseDown And mEdge <> EdgeEnum.None Then
            c.SuspendLayout()
            Select Case mEdge
                Case EdgeEnum.TopLeft
                    c.SetBounds(c.Left + e.X, c.Top + e.Y, _
            c.Width, c.Height)
                Case EdgeEnum.Left
                    c.SetBounds(c.Left + e.X, c.Top, _
            c.Width - e.X, c.Height)
                Case EdgeEnum.Right
                    c.SetBounds(c.Left, c.Top, _
            c.Width - (c.Width - e.X), c.Height)
                Case EdgeEnum.Top
                    c.SetBounds(c.Left, c.Top + e.Y, _
            c.Width, c.Height - e.Y)
                Case EdgeEnum.Bottom
                    c.SetBounds(c.Left, c.Top, _
            c.Width, c.Height - (c.Height - e.Y))
            End Select
            c.ResumeLayout()
        Else
            Select Case True
                Case e.X <= (mWidth * 4) And _
            e.Y <= (mWidth * 4) 'top left corner
                    c.Cursor = Cursors.SizeAll
                    mEdge = EdgeEnum.TopLeft
                Case e.X <= mWidth 'left edge
                    c.Cursor = Cursors.VSplit
                    mEdge = EdgeEnum.Left
                Case e.X > c.Width - (mWidth + 1) 'right edge
                    c.Cursor = Cursors.VSplit
                    mEdge = EdgeEnum.Right
                Case e.Y <= mWidth 'top edge
                    c.Cursor = Cursors.HSplit
                    mEdge = EdgeEnum.Top
                Case e.Y > c.Height - (mWidth + 1) 'bottom edge
                    c.Cursor = Cursors.HSplit
                    mEdge = EdgeEnum.Bottom
                Case Else 'no edge
                    c.Cursor = Cursors.Default
                    mEdge = EdgeEnum.None
            End Select
        End If
    End Sub

    Private Sub mControl_MouseLeave(ByVal sender As Object, _
        ByVal e As System.EventArgs) _
        Handles mControl.MouseLeave

        Dim c As Control = CType(sender, Control)
        mEdge = EdgeEnum.None
        c.Refresh()
    End Sub

End Class

That is all there is to it. You can download the example and give it a try, or just cut and paste the code from this article.

Points of Interest

As you can see, not a whole lot of code, but enough to make it work. I would suggest you use this as a starting point, and then customize it to suit your needs.

For example, the Fuchsia borders could be replaced with sizing handles and a dashed line around the control (like the .NET forms designer). You could also add the ability to grab a corner and stretch the control vertically and horizontally at the same time (again, like the .NET forms designer).

There is really no limit to what you could do, but at least this gives you a place to start, and it works pretty good "right out of the box".

Enjoy!

History

  • 2nd October, 2007: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
CEO
United States United States
Jesse Chunn was the Founder and CEO of Standard I-O, Inc. for 10 years until it was acquired in 2005. Since then he has spent most of his time providing strategic management to companies of all sizes. Most recently, he was the CIO of the Merchant Customer Exchange (MCX) and the Head of Merchant Strategy at CU Wallet. Jesse gets his thrills by coming up with elegant solutions to complicated problems, whether they be technology problems, business problems, or both (his favorite).

Comments and Discussions

 
PraiseSuper! Pin
Member 1448091826-Aug-19 5:11
Member 1448091826-Aug-19 5:11 
PraiseJust excellent! Pin
Member 1091169510-May-17 3:20
Member 1091169510-May-17 3:20 
Questionhow do I apply this to a user control which has an OnPaint event? Pin
Dr_HH23-Oct-16 9:06
Dr_HH23-Oct-16 9:06 
AnswerRe: how do I apply this to a user control which has an OnPaint event? Pin
Dr_HH24-Oct-16 11:52
Dr_HH24-Oct-16 11:52 
QuestionGrt work, Help with implementing this for 1column/2row TableLayoutPane Pin
Member 1124690920-Nov-15 5:24
Member 1124690920-Nov-15 5:24 
NewsC# Code Pin
Member 1169616816-May-15 14:51
Member 1169616816-May-15 14:51 
GeneralRe: C# Code Pin
Jesse Chunn23-May-15 17:42
Jesse Chunn23-May-15 17:42 
QuestionHOW DO I CHANGE THE FUCHSIA BORDERS WITH SIZING HANDLES? Pin
Member 113501914-Jan-15 7:13
Member 113501914-Jan-15 7:13 
GeneralSome Updated Code Pin
Mellofax26-Jun-14 2:30
Mellofax26-Jun-14 2:30 
Hi folks,

I tried the code here and was very impressed - having just started out on switching to VB.Net using Studio 2013, I found this a great learning experience. Below I have modified the code SLIGHTLY - I modified it because on Studio 2013, the bottom and right resize wasn't working - I believe they have changed things slightly? Either way this code is optimised for STUDIO 2013 - I haven't tested it on any other version. I did try to use the previous reply`s code but it references undeclared objects (Quiz?) and this one does not

The changes

The biggest change is that I changed the names of most of the variables to be descriptive - one of the things I always insisted on as a head programmer was to make everything maximum legible so anyone else can adjust it to suit their needs. I believe with the extra comments and renamed variables I have achieved this

I removed a few unnecessary items in some of the calculations where the math was taking a variable away from the same variable - this was unneccesary because of course the result would always be zero.

I changed the logic ever so slightly so when resizing the edge highlight disappears - its unnecessary because the mouse has already changed to reflect the resize and the user is resizing!

Anyway, I hope this helps someone in some way, the way the original code helped me. Thanks a million to Jesse! You rule


VB
Public Class clsMoveRersize
    Private WithEvents ControlToMove As Control
    Private IsLeftButtonHeld As Boolean = False
    Private ObjectEdge As EdgeEnum = EdgeEnum.None
    Private HighlightWidth As Integer = 4
    Private IsHighlightDrawn As Boolean = False
    Private Enum EdgeEnum
        None
        Right
        Left
        Top
        Bottom
        TopLeft
        BottomRight
    End Enum
    Public Sub New(ByVal SourceControl As Control)
        ControlToMove = SourceControl
    End Sub

    Private Sub ControlToMove_MouseDown(ByVal sender As Object, _
        ByVal MouseStatus As System.Windows.Forms.MouseEventArgs) _
        Handles ControlToMove.MouseDown
        If MouseStatus.Button = Windows.Forms.MouseButtons.Left Then
            IsLeftButtonHeld = True
            sender.Refresh()
            IsHighlightDrawn = False
        End If
    End Sub
    Private Sub ControlToMove_MouseUp(ByVal sender As Object, _
        ByVal MouseStatus As System.Windows.Forms.MouseEventArgs) _
        Handles ControlToMove.MouseUp
        IsLeftButtonHeld = False
    End Sub
    Private Sub ControlToMove_MouseMove(ByVal sender As Object, _
    ByVal MouseStatus As System.Windows.Forms.MouseEventArgs) _
    Handles ControlToMove.MouseMove
        Dim SourceControl As Control = CType(sender, Control)
        Dim GraphicsBuffer As Graphics = SourceControl.CreateGraphics
        If Not IsLeftButtonHeld Then
            Select Case ObjectEdge
                Case EdgeEnum.TopLeft
                    GraphicsBuffer.FillRectangle(Brushes.Fuchsia, 0, 0, HighlightWidth * 4,_
                    HighlightWidth * 4)
                    IsHighlightDrawn = True
                    'Display top left corner pink box to show user the object can be moved
                Case EdgeEnum.Left
                    GraphicsBuffer.FillRectangle(Brushes.Fuchsia, 0, 0, HighlightWidth,_
                    SourceControl.Height)
                    IsHighlightDrawn = True
                    'Display left edge pink strip to show user the object can be Resized LEFT
                Case EdgeEnum.Right
                    GraphicsBuffer.FillRectangle(Brushes.Fuchsia, SourceControl.Width _
                     - HighlightWidth - 5, 0, _
                    SourceControl.Width, SourceControl.Height)
                    IsHighlightDrawn = True
                    'Display Right edge pink strip to show user the object can be Resized RIGHT
                Case EdgeEnum.Top
                    GraphicsBuffer.FillRectangle(Brushes.Fuchsia, 0, 0, SourceControl.Width,_
                    HighlightWidth)
                    IsHighlightDrawn = True
                Case EdgeEnum.Bottom
                    GraphicsBuffer.FillRectangle(Brushes.Fuchsia, 0, SourceControl.Height - _
                    HighlightWidth - 5, SourceControl.Width, HighlightWidth)
                    IsHighlightDrawn = True
                Case EdgeEnum.None
                    'Erase the temporary Highlight by simply refreshing the control
                    If IsHighlightDrawn Then
                        SourceControl.Refresh()
                        IsHighlightDrawn = False
                    End If
            End Select
        End If
        If IsLeftButtonHeld And ObjectEdge <> EdgeEnum.None Then 
            'Control is being manipulated - Do this
            SourceControl.SuspendLayout()
            Select Case ObjectEdge
                Case EdgeEnum.TopLeft
                    'Control is being moved
                    SourceControl.SetBounds(SourceControl.Left + MouseStatus.X, SourceControl.Top _
                    + MouseStatus.Y, SourceControl.Width, SourceControl.Height)
                Case EdgeEnum.Left
                    'Control is being Widened TO THE LEFT
                    SourceControl.SetBounds(SourceControl.Left + MouseStatus.X, SourceControl.Top, _
                    SourceControl.Width - MouseStatus.X, SourceControl.Height)
                Case EdgeEnum.Right
                    'Control is being Widened TO THE RIGHT
                    SourceControl.SetBounds(SourceControl.Left, SourceControl.Top, MouseStatus.X,_
                    SourceControl.Height)
                Case EdgeEnum.Top
                    'Control is being Widened TO THE TOP
                    SourceControl.SetBounds(SourceControl.Left, SourceControl.Top + MouseStatus.Y, _
                    SourceControl.Width, SourceControl.Height - MouseStatus.Y)
                Case EdgeEnum.Bottom
                    'Control is being Widened TO THE BOTTOM
                    SourceControl.SetBounds(SourceControl.Left, SourceControl.Top, _
                    SourceControl.Width, MouseStatus.Y + 8)
            End Select
            SourceControl.ResumeLayout()
        Else
            Select Case True
                Case MouseStatus.X <= (HighlightWidth * 4) And MouseStatus.Y <= (HighlightWidth * 4)
                    'top left corner
                    SourceControl.Cursor = Cursors.SizeAll
                    ObjectEdge = EdgeEnum.TopLeft
                Case MouseStatus.X <= HighlightWidth 'left edge
                    SourceControl.Cursor = Cursors.VSplit
                    ObjectEdge = EdgeEnum.Left
                Case MouseStatus.X > SourceControl.Width - (HighlightWidth + 5) 'right edge
                    SourceControl.Cursor = Cursors.VSplit
                    ObjectEdge = EdgeEnum.Right
                Case MouseStatus.Y <= HighlightWidth 'top edge
                    SourceControl.Cursor = Cursors.HSplit
                    ObjectEdge = EdgeEnum.Top
                Case MouseStatus.Y > SourceControl.Height - (HighlightWidth + 5) 'bottom edge
                    SourceControl.Cursor = Cursors.HSplit
                    ObjectEdge = EdgeEnum.Bottom
                Case Else 'no edge
                    SourceControl.Cursor = Cursors.Default
                    ObjectEdge = EdgeEnum.None
            End Select
        End If
    End Sub
    Private Sub ControlToMove_MouseLeave(ByVal sender As Object, ByVal e As System.EventArgs) _
    Handles ControlToMove.MouseLeave

        Dim SourceControl As Control = CType(sender, Control)
        ObjectEdge = EdgeEnum.None
        SourceControl.Refresh()
    End Sub

End Class


modified 26-Jun-14 8:45am.

SuggestionI have modified this quite heavily and made it much better...(Don't mean to be rude:D) Pin
Josh-Mason113-Sep-13 19:45
professionalJosh-Mason113-Sep-13 19:45 
GeneralRe: I have modified this quite heavily and made it much better...(Don't mean to be rude:D) Pin
Jesse Chunn4-Sep-13 10:51
Jesse Chunn4-Sep-13 10:51 
GeneralRe: I have modified this quite heavily and made it much better...(Don't mean to be rude:D) Pin
Josh-Mason115-Sep-13 6:42
professionalJosh-Mason115-Sep-13 6:42 
GeneralRe: I have modified this quite heavily and made it much better...(Don't mean to be rude:D) Pin
Jesse Chunn20-Apr-14 10:26
Jesse Chunn20-Apr-14 10:26 
QuestionPlease help adding drag to this great solution Pin
DutchLeo6-Apr-13 6:09
DutchLeo6-Apr-13 6:09 
AnswerRe: Please help adding drag to this great solution Pin
Josh-Mason114-Sep-13 9:30
professionalJosh-Mason114-Sep-13 9:30 
QuestionExtremely Helpful Pin
MathGeekQ25-Feb-13 9:29
MathGeekQ25-Feb-13 9:29 
QuestionHow to resize a veritcal scroll bar? Pin
Member 769168111-Oct-12 10:55
Member 769168111-Oct-12 10:55 
QuestionHelp to create a resizable button! Pin
Raha2823-Jun-12 23:15
Raha2823-Jun-12 23:15 
GeneralMy vote of 5 Pin
LanFanNinja24-Nov-11 18:35
LanFanNinja24-Nov-11 18:35 
GeneralMy vote of 5 Pin
Armando de la Torre18-Oct-11 9:04
Armando de la Torre18-Oct-11 9:04 
GeneralExcellent Article - Dragging Made Easy ! Pin
kasbaba6-Mar-10 23:36
kasbaba6-Mar-10 23:36 
AnswerFinal Version With Some Enhancements by Braian87b Pin
Member 378650823-Nov-09 7:31
Member 378650823-Nov-09 7:31 
GeneralNice Job. Pin
bollwerkj28-May-09 7:45
bollwerkj28-May-09 7:45 
QuestionSmall problem? Pin
PSU Steve27-Feb-09 3:11
professionalPSU Steve27-Feb-09 3:11 
GeneralAwesome! I tweaked it a bit... Pin
PSU Steve13-Feb-09 4:59
professionalPSU Steve13-Feb-09 4:59 

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.