Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / WinForms

An improved drag-drop implementation with full positioning control

4.58/5 (21 votes)
19 Jul 20076 min read 1   2.8K  
If you need some more sophisticated control when using drag-drop operations on your own classes this article is a good start. The user is given much more control in positioning the objects. The best way to understand the contents of this article is to download and run the small demo.

Introduction

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.

Background

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

  1. Download the latest SmartDragDrop demo application to get a feeling for what this is!
  2. 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.
  3. Read the messages at the end of this page!

Using the code

Screenshot - SmartDragDrop.jpg

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 AllowDrop to true to make our Form accept any drop object at all. We then add the two events DragEnter and DragDrop. The 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 PictureBox. The MouseDown and MouseUp events are used to simply resize our MyUserControl object.
  • Both fake and real drag-drop while moving: By fake drag-drop, I mean simply using the MouseDown and 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 UserControl object.
  • 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 MyForm method 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.

Discussion

This is all implemented as a sample application. How can this be made useful in any application? We could inherit from our two classes MyForm and 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 MyForm and 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 UserControl class
  • How to draw a grid on a Form

History

  • 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.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here