Introduction
The source code of this article allows the user to provide a kind of user interface in which controls can be dragged and dropped on a form at the desired place. Clicking on Design Mode will enable the user to move all form controls by just clicking the mouse left button and dropping the control by leaving the mouse control. The good thing is that to enable the runtime dragging capability, there is no need to change the main form class or classes of controls. Everything is controlled using a separate class that has a small interface with the main form class.
Background
This article is totally based on Windows controls, mouse events, paint events and update regions.
Using the Code
A separate user control (draggableControls
) is implemented. Just drag and drop that control on the Main Form from the toolbox. It would be invisible at runtime. This class has a DesignMode
function that enables and disables runtime movement of all form controls by registering and unregistering event handlers of Mouse events. Look at the function given below, it is fully commented.
public void DesignMode(bool Enable)
{
mainForm = TopLevelControl;
Control ctrl = mainForm.GetNextControl(null, true);
do
{
if(ctrl.Text!="Design Mode" && ctrl.Text!="Control Mode")
SetEvent(ctrl, Enable);
ctrl = mainForm.GetNextControl(ctrl, true);
}while( ctrl != null);
if (Enable)
{
mainForm.Paint += new PaintEventHandler(mainForm_Paint);
mainForm.MouseMove += new MouseEventHandler(mainForm_MouseMove);
mainForm.MouseUp += new MouseEventHandler(mainForm_MouseUp);
}else
{
mainForm.Paint -= new PaintEventHandler(mainForm_Paint);
mainForm.MouseMove -= new MouseEventHandler(mainForm_MouseMove);
mainForm.MouseUp -= new MouseEventHandler(mainForm_MouseUp);
}
}
Other important functions are mouse Events (up, down, move) and main form Paint Event. On the mouse down event, we store the control and control size. On the mouse move event, it shows the dummy image of the control and on mouse down, it places the control at that place.
In the OnPaint
function, it shows the dummy image of the control. To stop flickering and to optimize graphics, it creates the control path and updates only the dummy image path.
The code of these functions are given below:
void ctrl_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
controlUnderDrag = mainForm.GetChildAtPoint(((Control)sender).Location +
new Size(e.Location.X, e.Location.Y));
if (controlUnderDrag != null)
{
mainForm.Capture = true;
mainForm.Cursor = Cursors.Hand;
controlImage = new Rectangle(controlUnderDrag.Location, controlUnderDrag.Size);
mouseOffset = new Size(e.Location.X, e.Location.Y);
Rectangle tempRectangle = new Rectangle(controlImage.X - 2,
controlImage.Y - 2, controlImage.Width + 4, controlImage.Height + 4);
GraphicsPath myGraphicsPath = new GraphicsPath();
myGraphicsPath.AddRectangle(tempRectangle);
System.Drawing.Region rgn = new Region(myGraphicsPath);
mainForm.Invalidate(rgn);
controlDragStarted = true;
}
}
}
void mainForm_MouseMove(object sender, MouseEventArgs e)
{
if (controlDragStarted)
{
GraphicsPath myGraphicsPath = new GraphicsPath();
Rectangle tempRectangle = new Rectangle(controlImage.X - 2, controlImage.Y - 2,
controlImage.Width + 4, controlImage.Height + 4);
myGraphicsPath.AddRectangle(tempRectangle);
System.Drawing.Region rgn = new Region(myGraphicsPath);
controlImage = new Rectangle(e.Location.X - mouseOffset.Width,
e.Location.Y - mouseOffset.Height, controlImage.Width,
controlImage.Height);
mainForm.Invalidate(rgn);
myGraphicsPath.Dispose();
myGraphicsPath = new GraphicsPath();
rgn.Dispose();
tempRectangle = new Rectangle(controlImage.X - 2, controlImage.Y - 2,
controlImage.Width + 4, controlImage.Height + 4);
rgn = new Region(tempRectangle);
mainForm.Invalidate(rgn);
}
}
void mainForm_MouseUp(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left && controlDragStarted)
{
controlDragStarted = false;
mainForm.Capture = false;
mainForm.Cursor = Cursors.Default;
GraphicsPath myGraphicsPath = new GraphicsPath();
Rectangle tempRectangle = new Rectangle(controlImage.X - 2, controlImage.Y - 2,
controlImage.Width + 4, controlImage.Height + 4);
myGraphicsPath.AddRectangle(tempRectangle);
System.Drawing.Region rgn = new Region(myGraphicsPath);
controlUnderDrag.Location = controlImage.Location;
controlImage.Height = 0; controlImage.Width = 0;
mainForm.Invalidate(rgn);
controlUnderDrag.Invalidate();
}
}
void mainForm_Paint(object sender, PaintEventArgs e)
{
if (controlImage != null && controlDragStarted)
{
e.Graphics.DrawRectangle(Pens.Green, controlImage);
}
}
Further Enhancements
Further enhancements like allowing to move only the selected controls, showing a different color dummy image while the control is moving can be done easily just by adding some properties in the DraggableControls
class.
History
- 14th September, 2007: Initial post