Click here to Skip to main content
15,888,351 members
Articles / Programming Languages / C#
Tip/Trick

Move and Resize Controls on a Form at Runtime (With Mouse)

Rate me:
Please Sign up or sign in to vote.
4.98/5 (55 votes)
13 Jan 2014CPOL3 min read 217.1K   20.7K   61   50
Move and resize controls on a form at runtime (with mouse)
Image 1

Introduction

Sometimes, we want to move and resize controls in run time for example when we want to create some template for a form. There are some codes on the web for this but they cannot do both moving and resizing controls. Therefore, I write my own class on the basis of another CodeProject article.

Using this class, we can make resizeable and movable control with only one line of code:

C#
ControlMoverOrResizer.Init(button1);   

Really? Yes!! :)

Background

This class uses lambda expression in event handler assignment.

C#
internal static void Init(Control control, Control container)
{
    _moving = false;
    _resizing = false;
    _moveIsInterNal = false;
    _cursorStartPoint = Point.Empty;
    MouseIsInLeftEdge = false;
    MouseIsInLeftEdge = false;
    MouseIsInRightEdge = false;
    MouseIsInTopEdge = false;
    MouseIsInBottomEdge = false;
    WorkType = MoveOrResize.MoveAndResize;
    control.MouseDown += (sender, e) => StartMovingOrResizing(control, e);
    control.MouseUp += (sender, e) => StopDragOrResizing(control);
    control.MouseMove += (sender, e) => MoveControl(container, e);
}      

I write all fields, properties and methods static; therefore it is not needed to create an object of ControlMoverOrResizer class.

C#
internal class ControlMoverOrResizer
{
    private static bool _moving;
    private static Point _cursorStartPoint;
    private static bool _moveIsInterNal;
    private static bool _resizing;
    private static Size _currentControlStartSize;
    internal static bool MouseIsInLeftEdge { get; set; }
    internal static bool MouseIsInRightEdge { get; set; }
    internal static bool MouseIsInTopEdge { get; set; }
    internal static bool MouseIsInBottomEdge { get; set; }

    internal enum MoveOrResize ...

    internal static MoveOrResize WorkType { get; set; } 

    internal static void Init(Control control) ...

    internal static void Init(Control control, Control container) ...

    private static void UpdateMouseEdgeProperties(Control control, Point mouseLocationInControl) ...

    private static void UpdateMouseCursor(Control control) ...

    private static void StartMovingOrResizing(Control control, MouseEventArgs e) ...

    private static void MoveControl(Control control, MouseEventArgs e) ...

    private static void StopDragOrResizing(Control control) ...
}  

Alone not private method in class is init method. When sending a control (or two with its container) to init method, it will add related methods to 3 important control events.

C#
control.MouseDown += (sender, e) => StartMovingOrResizing(control, e);
control.MouseUp += (sender, e) => StopDragOrResizing(control);
control.MouseMove += (sender, e) => MoveControl(container, e); 

Now when user holds the mouse down on control, event calls the StartMovingOrResizing method, and this method will set movingMode or resizingMode of control:

C#
private static void StartMovingOrResizing(Control control, MouseEventArgs e)
{
    if (_moving || _resizing)
    {
        return;
    }
    if (WorkType!=MoveOrResize.Move &&
        (MouseIsInRightEdge || MouseIsInLeftEdge || MouseIsInTopEdge || MouseIsInBottomEdge))
    {
        _resizing = true;
        _currentControlStartSize = control.Size;
    }
    else if (WorkType!=MoveOrResize.Resize)
    {
        _moving = true;
        control.Cursor = Cursors.Hand;
    }
    _cursorStartPoint = new Point(e.X, e.Y);
    control.Capture = true;
}  

When user moves the mouse cursor on control MoveControl method will call, this method calls the UpdateMouseEdgeProperties and UpdateMouseCursor functions.

UpdateMouseEdgeProperties will check which cursor is on any edge of control and will set related properties:

C#
private static void UpdateMouseEdgeProperties(Control control, Point mouseLocationInControl)
{
	if (WorkType == MoveOrResize.Move)
	{
		return;
	}
	MouseIsInLeftEdge = Math.Abs(mouseLocationInControl.X) <= 2;
	MouseIsInRightEdge = Math.Abs(mouseLocationInControl.X - control.Width) <= 2;
	MouseIsInTopEdge = Math.Abs(mouseLocationInControl.Y ) <= 2;
	MouseIsInBottomEdge = Math.Abs(mouseLocationInControl.Y - control.Height) <= 2;
}

and UpdateMouseCursor functions will change mouse cursor if it is in the edge of control:

C#
private static void UpdateMouseCursor(Control control)
{
	if (WorkType == MoveOrResize.Move)
	{
		return;
	}
	if (MouseIsInLeftEdge )
	{
		if (MouseIsInTopEdge)
		{
			control.Cursor = Cursors.SizeNWSE;
		}
		else if (MouseIsInBottomEdge)
		{
			control.Cursor = Cursors.SizeNESW;
		}
		else
		{
			control.Cursor = Cursors.SizeWE;
		}
	}
	else if (MouseIsInRightEdge)
	{
		if (MouseIsInTopEdge)
		{
			control.Cursor = Cursors.SizeNESW;
		}
		else if (MouseIsInBottomEdge)
		{
			control.Cursor = Cursors.SizeNWSE;
		}
		else
		{
			control.Cursor = Cursors.SizeWE;
		}
	}
	else if (MouseIsInTopEdge || MouseIsInBottomEdge)
	{
		control.Cursor = Cursors.SizeNS;
	}
	else
	{
		control.Cursor = Cursors.Default;
	}
}

Now program will callback to MoveControl function and continue in this method. It will check _resizing field (this field will set when using pressDown mouse on age of control and keep it down).

If control is in resizing mode, that edge of control where cursor is on it just moves with cursor:

C#
if (_resizing)
{
	if (MouseIsInLeftEdge)
	{
		if (MouseIsInTopEdge)
		{
			control.Width -= (e.X - _cursorStartPoint.X);
			control.Left += (e.X - _cursorStartPoint.X); 
			control.Height -= (e.Y - _cursorStartPoint.Y);
			control.Top += (e.Y - _cursorStartPoint.Y);
		}
		else if (MouseIsInBottomEdge)
		{
			control.Width -= (e.X - _cursorStartPoint.X);
			control.Left += (e.X - _cursorStartPoint.X);
			control.Height = (e.Y - _cursorStartPoint.Y)                   
		             + _currentControlStartSize.Height; 
		}
		else
		{
			control.Width -= (e.X - _cursorStartPoint.X);
			control.Left += (e.X - _cursorStartPoint.X) ;
		}
	}
	else if (MouseIsInRightEdge)
	{
		if (MouseIsInTopEdge)
		{
			control.Width = (e.X - _cursorStartPoint.X) 
                            + _currentControlStartSize.Width;
			control.Height -= (e.Y - _cursorStartPoint.Y);
			control.Top += (e.Y - _cursorStartPoint.Y); 
 
		}
		else if (MouseIsInBottomEdge)
		{
			control.Width = (e.X - _cursorStartPoint.X) 
                            + _currentControlStartSize.Width;
			control.Height = (e.Y - _cursorStartPoint.Y) 
                            + _currentControlStartSize.Height;                    
		}
		else
		{
			control.Width = (e.X - _cursorStartPoint.X) 
                           +_currentControlStartSize.Width;
		}
	}
	else if (MouseIsInTopEdge)
	{
		control.Height -= (e.Y - _cursorStartPoint.Y);
		control.Top += (e.Y - _cursorStartPoint.Y);
	}
	else if (MouseIsInBottomEdge)
	{
		control.Height = (e.Y - _cursorStartPoint.Y) 
                   + _currentControlStartSize.Height;                    
	}
	else
	{
		 StopDragOrResizing(control);
	}
}     

Else if control is in moving mode (control goes to moving mode when user presses mouse down inside of control and keeps it down), the position of control will move with cursor:

C#
else if (_moving)
{
	_moveIsInterNal = !_moveIsInterNal;
	if (!_moveIsInterNal)
	{
		int x = (e.X - _cursorStartPoint.X) + control.Left;
		int y = (e.Y - _cursorStartPoint.Y) + control.Top;
		control.Location = new Point(x, y);
	}
}

At last, when user releases the mouse, StopDragOrResizing method will be called and it will reset moving mode and resizing mode to false and update the cursor.

private static void StopDragOrResizing(Control control)
{
	_resizing = false;
	_moving = false;
	control.Capture = false;
	UpdateMouseCursor(control);
} 

Using the Code

For enable resizing and moving mode for a control, we just call init method in MoveAndResizeControls class in send control to it.

C#
MoveAndResizeControls.Init(button1); 

If we want to change container of control (for example, when control is filled in its container), we just send container as the second parameter.

C#
ControlMoverOrResizer.Init(button2,panel1); 

In some cases, we just want one of resizing or moving for controls in this case we just set the WorkType property in the ControlMoverOrResizer class with one of these values:

C#
internal enum MoveOrResize
{
	Move,
	Resize,
	MoveAndResize
}

In the demo example that is uploaded here, controls can be moved and resized by mouse. Also, a combobox is in demo form who will help you select worktype ("move", "resize" or "move and resize").

C#
using System;
using System.Windows.Forms;
using ControlManager;
namespace MoveAndResizeControls
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            ControlMoverOrResizer.Init(button1);
            ControlMoverOrResizer.Init(groupBox1);
            ControlMoverOrResizer.Init(textBox1);
            ControlMoverOrResizer.Init(button2,panel1);
            comboBox1.SelectedIndex = 0;
        }

        private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            switch (comboBox1.SelectedIndex)
            {
                case 0:
                    ControlMoverOrResizer.WorkType=ControlMoverOrResizer.MoveOrResize.MoveAndResize;
                    break;
                case 1:
                    ControlMoverOrResizer.WorkType = ControlMoverOrResizer.MoveOrResize.Move;
                    break;
                case 2:
                    ControlMoverOrResizer.WorkType = ControlMoverOrResizer.MoveOrResize.Resize;
                    break;
            }
        }
    }
}    

Points of Interest

If you want to save and load changes, you can use these methods:

  • GetSizeAndPositionOfControlsToString
  • SetSizeAndPositionOfControlsFromString

This is form after move and resize controls:

Image 2

My sincere thanks for your time and consideration.
Best wishes.

History

  • 2014/01/10: As of publication, version 1.0.0.0
  • 2014/02/09: As of update, version 1.1.0.0

License

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


Written By
Software Developer (Senior)
Iran (Islamic Republic of) Iran (Islamic Republic of)
I enjoy learning new things everyday.

"It’s the principle of reaping huge rewards from a series of small, smart choices. Success is earned in the moment to moment decisions that in themselves make no visible difference whatsoever, but the accumulated compounding effect is profound." Darren Hardy

Comments and Discussions

 
QuestionUsing this class with a user control Pin
AleMoon696930-Oct-14 7:32
AleMoon696930-Oct-14 7:32 
QuestionThat there is a bug when I try to change the size of ListBox or ComboBox Pin
Member 35223456-Oct-14 19:53
Member 35223456-Oct-14 19:53 
GeneralThanks Pin
Member 1099147130-Sep-14 4:47
Member 1099147130-Sep-14 4:47 
GeneralRe: Thanks Pin
seyyed hamed monem30-Sep-14 7:52
professionalseyyed hamed monem30-Sep-14 7:52 
QuestionCan you write the code for Vb? Pin
Member 1049181330-Jul-14 14:12
Member 1049181330-Jul-14 14:12 
AnswerRe: Can you write the code for Vb? Pin
seyyed hamed monem5-Aug-14 5:57
professionalseyyed hamed monem5-Aug-14 5:57 
GeneralRe: Can you write the code for Vb? Pin
doquigley16-Sep-14 14:33
doquigley16-Sep-14 14:33 
AnswerRe: Can you write the code for Vb? Pin
seyyed hamed monem17-Sep-14 7:42
professionalseyyed hamed monem17-Sep-14 7:42 
this is a auto converted code.(i am not test this code). code:
VB
Imports System.Collections.Generic
Imports System.Drawing
Imports System.Globalization
Imports System.Linq
Imports System.Windows.Forms

Public Class ControlMoverOrResizer

    Private Shared _moving As Boolean
    Private Shared _cursorStartPoint As Point
    Private Shared _moveIsInterNal As Boolean
    Private Shared _resizing As Boolean
    Private Shared _currentControlStartSize As Size
    Friend Shared Property MouseIsInLeftEdge() As Boolean
        Get
            Return m_MouseIsInLeftEdge
        End Get
        Set(value As Boolean)
            m_MouseIsInLeftEdge = value
        End Set
    End Property
    Private Shared m_MouseIsInLeftEdge As Boolean
    Friend Shared Property MouseIsInRightEdge() As Boolean
        Get
            Return m_MouseIsInRightEdge
        End Get
        Set(value As Boolean)
            m_MouseIsInRightEdge = value
        End Set
    End Property
    Private Shared m_MouseIsInRightEdge As Boolean
    Friend Shared Property MouseIsInTopEdge() As Boolean
        Get
            Return m_MouseIsInTopEdge
        End Get
        Set(value As Boolean)
            m_MouseIsInTopEdge = value
        End Set
    End Property
    Private Shared m_MouseIsInTopEdge As Boolean
    Friend Shared Property MouseIsInBottomEdge() As Boolean
        Get
            Return m_MouseIsInBottomEdge
        End Get
        Set(value As Boolean)
            m_MouseIsInBottomEdge = value
        End Set
    End Property
    Private Shared m_MouseIsInBottomEdge As Boolean

    Friend Enum MoveOrResize
        Move
        Resize
        MoveAndResize
    End Enum

    Friend Shared Property WorkType() As MoveOrResize
        Get
            Return m_WorkType
        End Get
        Set(value As MoveOrResize)
            m_WorkType = value
        End Set
    End Property
    Private Shared m_WorkType As MoveOrResize

    Friend Shared Sub Init(control As Control)
        Init(control, control)
    End Sub

    Friend Shared Sub Init(control As Control, container As Control)
        _moving = False
        _resizing = False
        _moveIsInterNal = False
        _cursorStartPoint = Point.Empty
        MouseIsInLeftEdge = False
        MouseIsInLeftEdge = False
        MouseIsInRightEdge = False
        MouseIsInTopEdge = False
        MouseIsInBottomEdge = False
        WorkType = MoveOrResize.MoveAndResize
		AddHandler control.MouseDown, AddressOf  StartMovingOrResizing
		AddHandler control.MouseUp, AddressOf  StopDragOrResizing
		AddHandler control.MouseMove, AddressOf  MoveControl
    End Sub

    Private Shared Sub UpdateMouseEdgeProperties(control As Control, mouseLocationInControl As Point)
        If WorkType = MoveOrResize.Move Then
            Return
        End If
        MouseIsInLeftEdge = Math.Abs(mouseLocationInControl.X) <= 2
        MouseIsInRightEdge = Math.Abs(mouseLocationInControl.X - control.Width) <= 2
        MouseIsInTopEdge = Math.Abs(mouseLocationInControl.Y) <= 2
        MouseIsInBottomEdge = Math.Abs(mouseLocationInControl.Y - control.Height) <= 2
    End Sub

    Private Shared Sub UpdateMouseCursor(control As Control)
        If WorkType = MoveOrResize.Move Then
            Return
        End If
        If MouseIsInLeftEdge Then
            If MouseIsInTopEdge Then
                control.Cursor = Cursors.SizeNWSE
            ElseIf MouseIsInBottomEdge Then
                control.Cursor = Cursors.SizeNESW
            Else
                control.Cursor = Cursors.SizeWE
            End If
        ElseIf MouseIsInRightEdge Then
            If MouseIsInTopEdge Then
                control.Cursor = Cursors.SizeNESW
            ElseIf MouseIsInBottomEdge Then
                control.Cursor = Cursors.SizeNWSE
            Else
                control.Cursor = Cursors.SizeWE
            End If
        ElseIf MouseIsInTopEdge OrElse MouseIsInBottomEdge Then
            control.Cursor = Cursors.SizeNS
        Else
            control.Cursor = Cursors.[Default]
        End If
    End Sub

    Private Shared Sub StartMovingOrResizing(control As Control, e As MouseEventArgs)
        If _moving OrElse _resizing Then
            Return
        End If
        If WorkType <> MoveOrResize.Move AndAlso (MouseIsInRightEdge OrElse MouseIsInLeftEdge OrElse MouseIsInTopEdge OrElse MouseIsInBottomEdge) Then
            _resizing = True
            _currentControlStartSize = control.Size
        ElseIf WorkType <> MoveOrResize.Resize Then
            _moving = True
            control.Cursor = Cursors.Hand
        End If
        _cursorStartPoint = New Point(e.X, e.Y)
        control.Capture = True
    End Sub

    Private Shared Sub MoveControl(control As Control, e As MouseEventArgs)
        If Not _resizing AndAlso Not _moving Then
            UpdateMouseEdgeProperties(control, New Point(e.X, e.Y))
            UpdateMouseCursor(control)
        End If
        If _resizing Then
            If MouseIsInLeftEdge Then
                If MouseIsInTopEdge Then
                    control.Width -= (e.X - _cursorStartPoint.X)
                    control.Left += (e.X - _cursorStartPoint.X)
                    control.Height -= (e.Y - _cursorStartPoint.Y)
                    control.Top += (e.Y - _cursorStartPoint.Y)
                ElseIf MouseIsInBottomEdge Then
                    control.Width -= (e.X - _cursorStartPoint.X)
                    control.Left += (e.X - _cursorStartPoint.X)
                    control.Height = (e.Y - _cursorStartPoint.Y) + _currentControlStartSize.Height
                Else
                    control.Width -= (e.X - _cursorStartPoint.X)
                    control.Left += (e.X - _cursorStartPoint.X)
                End If
            ElseIf MouseIsInRightEdge Then
                If MouseIsInTopEdge Then
                    control.Width = (e.X - _cursorStartPoint.X) + _currentControlStartSize.Width
                    control.Height -= (e.Y - _cursorStartPoint.Y)

                    control.Top += (e.Y - _cursorStartPoint.Y)
                ElseIf MouseIsInBottomEdge Then
                    control.Width = (e.X - _cursorStartPoint.X) + _currentControlStartSize.Width
                    control.Height = (e.Y - _cursorStartPoint.Y) + _currentControlStartSize.Height
                Else
                    control.Width = (e.X - _cursorStartPoint.X) + _currentControlStartSize.Width
                End If
            ElseIf MouseIsInTopEdge Then
                control.Height -= (e.Y - _cursorStartPoint.Y)
                control.Top += (e.Y - _cursorStartPoint.Y)
            ElseIf MouseIsInBottomEdge Then
                control.Height = (e.Y - _cursorStartPoint.Y) + _currentControlStartSize.Height
            Else
                StopDragOrResizing(control)
            End If
        ElseIf _moving Then
            _moveIsInterNal = Not _moveIsInterNal
            If Not _moveIsInterNal Then
                Dim x As Integer = (e.X - _cursorStartPoint.X) + control.Left
                Dim y As Integer = (e.Y - _cursorStartPoint.Y) + control.Top
                control.Location = New Point(x, y)
            End If
        End If
    End Sub

    Private Shared Sub StopDragOrResizing(control As Control, e As System.Windows.Forms.MouseEventArgs)
        _resizing = False
        _moving = False
        control.Capture = False
        UpdateMouseCursor(control)
    End Sub

#Region "Save And Load"
     Private Shared Function GetAllChildControls(control As Control, list As List(Of Control)) As List(Of Control)
        Dim controls As List(Of Control) = control.Controls.Cast(Of Control)().ToList()
        list.AddRange(controls)
        Return controls.SelectMany(Function(ctrl) GetAllChildControls(ctrl, list)).ToList()
    End Function

    Friend Shared Function GetSizeAndPositionOfControlsToString(container As Control) As String
        Dim controls As New List(Of Control)()
        GetAllChildControls(container, controls)
        Dim cultureInfo As New CultureInfo("en")
        Dim info As String = String.Empty
        For Each control As Control In controls
            info += control.Name & ":" & control.Left.ToString(cultureInfo) & "," & control.Top.ToString(cultureInfo) & "," & control.Width.ToString(cultureInfo) & "," & control.Height.ToString(cultureInfo) & "*"
        Next
        Return info
    End Function
    Friend Shared Sub SetSizeAndPositionOfControlsFromString(container As Control, controlsInfoStr As String)
        Dim controls As New List(Of Control)()
        GetAllChildControls(container, controls)
			Dim controlsInfo As String() = controlsInfoStr.Split(New () {"*"}, StringSplitOptions.RemoveEmptyEntries)
        Dim controlsInfoDictionary As New Dictionary(Of String, String)()
        For Each controlInfo As String In controlsInfo
				Dim info As String() = controlInfo.Split(New () {":"}, StringSplitOptions.RemoveEmptyEntries)
            controlsInfoDictionary.Add(info(0), info(1))
        Next
        For Each control As Control In controls
            Dim propertiesStr As String
            controlsInfoDictionary.TryGetValue(control.Name, propertiesStr)
				Dim properties As String() = propertiesStr.Split(New () {","}, StringSplitOptions.RemoveEmptyEntries)
            If properties.Length = 4 Then
                control.Left = Integer.Parse(properties(0))
                control.Top = Integer.Parse(properties(1))
                control.Width = Integer.Parse(properties(2))
                control.Height = Integer.Parse(properties(3))
            End If
        Next
    End Sub

#End Region


modified 17-Sep-14 14:01pm.

AnswerRe: Can you write the code for Vb? Pin
EzequielKees2-Jun-16 21:08
EzequielKees2-Jun-16 21:08 
AnswerRe: Can you write the code for Vb? Pin
massimo ravaglia9-Aug-19 0:01
massimo ravaglia9-Aug-19 0:01 
QuestionThanks Pin
Skynet197011-Jul-14 3:46
Skynet197011-Jul-14 3:46 
AnswerRe: Thanks Pin
seyyed hamed monem11-Jul-14 12:06
professionalseyyed hamed monem11-Jul-14 12:06 
GeneralRe: Thanks Pin
Skynet197016-Jul-14 22:55
Skynet197016-Jul-14 22:55 
QuestionGreat...Thank you! Pin
revitarkitek22-May-14 14:06
revitarkitek22-May-14 14:06 
QuestionCapturing When a Move or Resize is Over Pin
Chris Hartnell24-Apr-14 5:12
Chris Hartnell24-Apr-14 5:12 
Questionhow to remove this feature from control Pin
MB Seifollahi9-Feb-14 9:47
professionalMB Seifollahi9-Feb-14 9:47 
AnswerRe: how to remove this feature from control Pin
seyyed hamed monem9-Feb-14 22:43
professionalseyyed hamed monem9-Feb-14 22:43 
GeneralRe: how to remove this feature from control Pin
MB Seifollahi10-Feb-14 0:04
professionalMB Seifollahi10-Feb-14 0:04 
AnswerRe: how to remove this feature from control Pin
Code_Novice2-Mar-16 11:46
Code_Novice2-Mar-16 11:46 
QuestionAdd Sizing handles to selected controls Pin
Saintniyi19-Jan-14 1:30
Saintniyi19-Jan-14 1:30 
AnswerRe: Add Sizing handles to selected controls Pin
seyyed hamed monem20-Jan-14 1:00
professionalseyyed hamed monem20-Jan-14 1:00 
AnswerRe: Add Sizing handles to selected controls Pin
Member 276569115-Jan-15 2:19
Member 276569115-Jan-15 2:19 
BugMove and resize controls on a form at runtime (with drag and drop) Pin
Saintniyi17-Jan-14 2:33
Saintniyi17-Jan-14 2:33 
GeneralRe: Move and resize controls on a form at runtime (with drag and drop) Pin
Saintniyi17-Jan-14 4:43
Saintniyi17-Jan-14 4:43 
AnswerRe: Move and resize controls on a form at runtime (with drag and drop) Pin
seyyed hamed monem18-Jan-14 5:50
professionalseyyed hamed monem18-Jan-14 5:50 

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.