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):
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:
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)
c.Cursor = Cursors.SizeAll
mEdge = EdgeEnum.TopLeft
Case e.X <= mWidth
c.Cursor = Cursors.VSplit
mEdge = EdgeEnum.Left
Case e.X > c.Width - (mWidth + 1)
c.Cursor = Cursors.VSplit
mEdge = EdgeEnum.Right
Case e.Y <= mWidth
c.Cursor = Cursors.HSplit
mEdge = EdgeEnum.Top
Case e.Y > c.Height - (mWidth + 1)
c.Cursor = Cursors.HSplit
mEdge = EdgeEnum.Bottom
Case Else
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