I’ve decided to start work on a piece of software which will be useful within the industry I work in. I don’t want to go into too much detail about what the application will be for but thought it would be useful to others if I blog about various parts of the experience as I work on it.
One of the things my application will require is the ability for users to create their own interfaces by adding text boxes and buttons to a design surface, in a similar way to Visual Studio 2005/2008 and storing these “screen layouts” in a database, providing a basic development environment to customize the application to the users requirements. The actual controls that they will move around to create their layouts do not have to be functional controls, but “positioners” for where controls will appear during application use and as such will probably end up being custom user controls rather than real buttons and text boxes for instance.
This evening I thought I would look into how to drag and drop controls around a form using real buttons for now, the example should work for any type of control. I will try to post my learnings in tutorial format so that anyone should be able to follow them.
1) Start by opening Visual Studio 2005/2008 (the free express versions will also suffice) and create a new windows application by selecting “File > New > Project” then selecting “Windows Forms Application” before clicking “OK”.
2) Drag the resize handles of the form to give us about twice the starting work surface to work on.
3) Drag a button onto the form and position roughly central.
4) Next we need event handlers. Select the button so that it is active, then depress the “lightning bolt” events button in the properties pane. Double-click each of the required events in turn to auto generate the event handler in the Form1.cs file. The events we require are: “MouseDown”, “MouseMove” and “MouseUp”.
The code should look like this:
using System;
using System.Drawing;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_MouseDown(object sender, MouseEventArgs e)
{
}
private void button1_MouseMove(object sender, MouseEventArgs e)
{
}
private void button1_MouseUp(object sender, MouseEventArgs e)
{
}
}
}
5) Next we need to declare some variables:
bool Dragging; int mouseX, mouseY; int clipLeft, clipTop, clipWidth, clipHeight;
Place these below the public Form1(){ InitializeComponent();} constructor.
6) Next we will work on the MouseDown event handler. First we will set our “Dragging” variable to true. This will be checked in the MouseMove event handler to ensure that we are still dragging before updating the position of the button, which will be “Offset” using the mouse pointer position which will be stored in “mouseX” and “mouseY”.
To prevent dragging out of the form, we need to set up a “Cursor.Clip” area the size of the form.
Finally we call the Invalidate() method to force a redraw of the button control.
So the completed MouseDown event handler looks like this:
private void button1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
Dragging = true;
mouseX = -e.X;
mouseY = -e.Y;
clipLeft = this.PointToClient(MousePosition).X - button1.Location.X;
clipTop = this.PointToClient(MousePosition).Y - button1.Location.Y;
clipWidth = this.ClientSize.Width - (button1.Width - clipLeft);
clipHeight = this.ClientSize.Height - (button1.Height - clipTop);
Cursor.Clip = this.RectangleToScreen(new Rectangle(clipLeft, clipTop, clipWidth, clipHeight));
button1.Invalidate();
}
}
7) The “MouseMove” event handler is next. The next position of the button will be stored using a Point object, which is why we have the line “using System.Drawing”. A “Point” object is used to store coordinates on a 2 dimensional plane. This can then be assigned to the buttons “Location” property to update its position based on it’s top left corner.
The complete MouseMove event handler:
private void button1_MouseMove(object sender, MouseEventArgs e)
{
if (Dragging)
{
Point nextPosition = new Point();
nextPosition = this.PointToClient(MousePosition);
nextPosition.Offset(mouseX, mouseY);
button1.Location = nextPosition;
}
}
9) Are you wondering why this is step 9 and not step 8? 8 and a close bracket shows
and I can’t be bothered to work out how to escape it!. Finally the “MouseUp” event handler. First we set “Dragging” to false to ensure that we do not move the button when it is not depressed, next we reset the “Cursor.Clip” so that mouse movement is no longer restricted to the form area. A call to Invalidate() is used once more to update the button drawing.
private void button1_MouseUp(object sender, MouseEventArgs e)
{
if (Dragging)
{
Dragging = false;
Cursor.Clip = System.Drawing.Rectangle.Empty;
button1.Invalidate();
}
}
10) That’s it! It isn’t perfect, you can move part of the button off the form to the right and off the form to the bottom. I will work out how to sort this out later. You will notice in the final code shown below that I have added “this.Cursor = Cursors.Hand;” to the MouseDown event and “this.Cursor = Cursors.Default;” to the MouseUp event. It’s obvious what this does!
The complete Form1.cs file:
using System;
using System.Drawing;
using System.Windows.Forms;
namespace WindowsFormsApplication8
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
bool Dragging;
int mouseX, mouseY;
int clipLeft, clipTop, clipWidth, clipHeight;
private void button1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
Dragging = true;
mouseX = -e.X;
mouseY = -e.Y;
clipLeft = this.PointToClient(MousePosition).X - button1.Location.X;
clipTop = this.PointToClient(MousePosition).Y - button1.Location.Y;
clipWidth = this.ClientSize.Width - (button1.Width - clipLeft);
clipHeight = this.ClientSize.Height - (button1.Height - clipTop);
Cursor.Clip = this.RectangleToScreen(new Rectangle(clipLeft, clipTop, clipWidth, clipHeight));
button1.Invalidate();
this.Cursor = Cursors.Hand;
}
}
private void button1_MouseMove(object sender, MouseEventArgs e)
{
if (Dragging)
{
Point nextPosition = new Point();
nextPosition = this.PointToClient(MousePosition);
nextPosition.Offset(mouseX, mouseY);
button1.Location = nextPosition;
}
}
private void button1_MouseUp(object sender, MouseEventArgs e)
{
if (Dragging)
{
Dragging = false;
Cursor.Clip = System.Drawing.Rectangle.Empty;
button1.Invalidate();
this.Cursor = Cursors.Default;
}
}
}
}
If this quick tutorial interested you then please feel free to comment, you could always Digg it too!

February 1st, 2008 at 4:19 am
Excellent example! Do you have any examples of drawing a border around a control and allowing the user to resize it like in the C# IDE?
February 8th, 2008 at 3:50 pm
To fix the problem with the button going off the screen, remove clipLeft and clipTop from lines 10 and 11 within MouseDown.
February 15th, 2008 at 10:38 am
Thanks! I really struggeled to make my own far simpler control dragging without success until I found this code.
Thanks again
February 16th, 2008 at 7:47 am
With regards to drawing borders and resizing etc, shortly after putting this code together I discovered that you can actually use a design-time like interface (visual studio form editor) at runtime using some .net classes.
As soon as I get time I will put some examples together
February 20th, 2008 at 12:49 pm
Thank you. it works for a normal windows control,
But i didnt work for a custom control, inherited from Control class.
In fact it does something like dragging but too messy .
Do you know what the problem is?
class myControl : Control
{
//……codes …
}
Thank you..
February 20th, 2008 at 1:30 pm
Solved..!
Works for every custom control..
Thank you again for your nice code.
February 22nd, 2008 at 8:02 am
Thanks for the sample! Im kind off new to this language (using VisualStudio 2008). But the second i add a background (on the main canvas, another panel or picture box) to the project the dragging is all messed up (artifacts, image not showing up when drawing etc..).
Is there an simple explenation for this?
Regards
March 21st, 2008 at 9:19 am
I have same problem as Kayvan - How did you solve for custom control?