This is a demo application that shows how to implement a drag and drop solution that gives you more positioning control. As long as you move an object within its container, there is no need to perform a true drag and drop operation. You simply change its location. This application shows how to automatically switch between the two methods.
To do some exact positioning, you might like to see the outline of the object while moving it. This application shows how to do this, as well. It's also possible to move the object with the arrow keys. To align objects, you would most likely need some grid to snap against. This, too, is included with the application. To give this demo more value, the drag-drop object is also resizable via a corner grip.
This has all been done in Visual Studio 2005 and the .NET 2.0 Framework.
I started to create an application containing a toolbox with different objects that the user would drag to a form or to other container objects. At first I tried to only use true drag-drop, but this approach gave me a very rough and simple application. Something more sophisticated was needed in order to give the user some more control.
How to get started
- Download the latest SmartDragDrop demo application to get a feeling for what this is!
- Download the latest SmartDragDrop source project to have a look at the implementation and to copy the code parts that you like into your own project.
- Read the messages at the end of this page!
Using the code
The project consists of four classes: MyForm.cs, MyUserControl.cs, Outline.cs and the standard Program.cs. The
Program class creates two
MyForm windows to be used as a playground. One
MyUserControl is then created for use as a drag-drop object. It is placed in the second window. When we move or resize the the drag-drop object, an
Outline object is used to show the mouse movements.
The MyForm class implements
- Accept drops of
MyUserControl objects. First we have to set the Form property
true to make our Form accept any drop object at all. We then add the two events
DragEnter event is used to reject objects we don't support. The
DragDrop event is used to recreate and add our
MyUserControl object at the correct location.
- A grid for exact positioning of
MyUserControl objects is needed. The method
DisplayOrHideGrid draws a grid on our
MyForm window if the grid checkbox on the Form is checked. The
Graphics class is used to draw the grid on our Form. The public method
SnapToGrid is used both locally and by the
MyUserControl class to recalculate the location to snap to the grid.
The MyUserControl class implements
- Resize by corner grip:
UserControls don't have any resize possibilities like
SizeGripStyle for Forms. To obtain this feature, a transparent corner grip picture is used with
MouseUp events are used to simply resize our
- Both fake and real drag-drop while moving: By fake drag-drop, I mean simply using the
MouseUp events to move our
MyUserControl object. The method
IsCursorInside is used in the
MouseMove event to check if the mouse cursor leaves our
MyForm. If so, a true drag-drop operation is started. The public property
MouseDownStartPoint is used in
MyForm to preserve the mouse pointer's location when the drop operation is done. That is, if I start to drag at the center, I expect to find the cursor at the center when I drop my object.
- High resolution move via the arrow keys: To give the user the ability to align an object, the arrow keys will move the
MyUserControl object one pixel. We need to override the
IsInputKey event to be able to catch arrow key events in a
- Show outline of itself during move or resize: The static methods in Outline.cs create a cursor object that is used during a move. A transparent form is used. This gives the user better control during a move or resize.
- The possibility to snap to a parent grid: Whenever a
MyUserControl object is moved (not dropped), the
SnapToGrid is called to recalculate the location to snap to grid.
The Outline class implements
- A transparent form used as cursor: All methods used to manipulate the
Outline object in this class are static. This makes the use simple and ensures that only one cursor object is used. Since we only have one mouse and can only perform one move or resize at the time, there is no need to create several cursor objects. This technique saves a lot of memory and CPU compared with the previous solution of creating a true cursor.
This is all implemented as a sample application. How can this be made useful in any application? We could inherit from our two classes
MyUserControl. The drawback is that then we would be stuck using only
Form as a container and
UserControl as a drag object. We could also create a special
SmartDragDrop class to be used in the
UserControl class. However, it's not that easy to make this a clean solution either.
Furthermore, how should mouse events be handled? There are some not-so-nice connections between our
MyUserControl classes, as well. The child should not need to know of its parent, but this is the case when
SnapToGrid is called. How can this be made better? Use interfaces, perhaps?
Please improve the code and share your ideas!
Points of interest
These questions are addressed in this article:
- How to drag-drop user-defined classes
- How to show the outline of some objects during move or resize
- How to create a resize grip for the
- How to draw a grid on a Form
- 2007-06-24: Submitted this article and code 1.0. Please don't forget to vote for this article; I really like to know and learn!
- 2007-07-13: Bug corrected: In the event method
userControl_MouseUp in MyUserControl.cs, the
MouseMove event is now unregistered. Dispose of cursor object is improved.
- 2007-07-16: New cursor solution: We now use a transparent form, Outline.cs, instead of creating a true cursor object. This saves a lot of memory and CPU, especially if large objects are moved. The cursor is now also used at resize. There is no fault on the previous release 1.1. You can get it here; it might be as good as this in some applications.