Introduction

An upcoming application at work required me to have drag and drop functionality between many different ListViews or DataGrids. When I got word, I decided to start searching the Internet and find a pre-existing free control that I could use. My personal requirements were that the ListView or DataGrid needed to have the ability to move rows around within its own control, but also allow me to move rows to other controls. My initial sorting pointed me towards Drag and Drop ListView row reordering by David Boland. As his control allowed users to reorder items in a ListView, it lacked support for moving to other ListViews as well as letting the user know where the new rows are to be placed. This control is somewhat similar to David's, however, it was rewritten only using David's code as a reference.

Using the Code

The DragAndDropListView control inherits from ListView, and provides native support for dragging and dropping ListItems to reorder them or move them to other DragAndDropListView controls. Because it needs to utilize Drag and Drop functionality, I had to override the OnDragDrop, OnDragOver, OnDragEnter, OnItemDrag, OnLostFocus, and OnDragLeave to provide the functionality.

When an item is selected and started to be dragged, OnItemDrag gets called which starts the Drag and Drop functionality by retrieving the control it originates from as well as the selected items. Both of these are stored in a private class that is passed around in the data of the drag and drop events.

As the user moves the selected list items over the ListView, OnDragOver is called. This method basically determines if the selected items to be dragged & dropped can actually be dragged & dropped. By checking to see if the item it's currently hovering over is not one of the list items being moved, it either displays the move cursor or denied cursor. If the user is trying to drop the items into an area that doesn't have a list item, it will still allow you, thus allowing you to place and reorder items in any order.

One of the really cool things that I wanted to do, was to add a line either above or below the hovered list item, however, this really didn't pose an easy feat at first. To figure this out, I had 2 actual implementations of creating the line, 1 for when you were hovering over a list item, and 1 for when you were in an empty area. When you were hovering over a list item, the line needed to be added between that item and the item above it. If the user is currently over the empty area, and items existed, then the line would be added to the bottom of the very last item. Below is the code that shows how to add the line and arrows on both side:

Previous versions of this article stated that lines would only be drawn if it was in Details or Line mode, however this updated version includes both Small Icon List and Large Icon List replacing horizontal lines with vertical lines. The demo has also been updated, so you can test this method out as well.

As the user is dragging the selected items around the form, OnLostFocus and OnDragLeave are utilized to invalidate the form so the drawn lines are erased.

Lastly, OnDragDrop is called whenever the user drops the selected items into a DragAndDropListView. This method determines if the items to be dropped are from the current DragAndDropListView or from another DragAndDropListView. At which point, the items are appended to the end, or above the hovered item. Finally, the selected items are removed from the respective DragAndDropListView.

References

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.

Now my problem is that I'd still like to allow external files drag & dropping to one of my listviews, while still allowing items reordering. When I drag a file over my listview, the DragEnter even is not even called. If I disable AllowReorder, everything works fine.

For others who have this problem (I had it too). I just changed some of the logic. All drag and drop handlers should call the corresponding base events no matter what at some point in the code. Also, a drag drop should turn off column sorting just in case it's on so that reordering by dragging takes precedence over sorting.

1. you cant drag items from a standard listview, only from dragndrop control
2. it appears that the listview items when dragged down the listview, are added one extra line down, but when dragging up everything works fine.

// determine if the items to be dropped are from// this list view. If they are, perform a hack// to (increment) decrement the hover index so that the items// get moved properly.if(this == data.ListView){ if(hoverIndex > base.SelectedItems[0].Index) hoverIndex-= base.SelectedItems.Count;}

this piece needs to be a decrement. That fixes the problem of the selected items being placed too far down.

Another thing I added to it is down below this piece I've changed the order a bit. Instead of first adding the listItems and then removing them from thier source list, I've removed them first, and then added them setting thier Selected Property to true, so that they stay selected when they are dropped.

I've only briefly looked at it, so if someone sees a reason why this would break it, please do tell. This control is great by the way.

In the OnDragOver event handler, the drawing of the horizontal line and triangles takes place.

When the scrollbar is displayed, the right hand triangle is drawn underneath the scrollbar. To correct this, you need to use ClientSize.Width, rather than the width of the hoverItem when drawing the line and triangle.

You can also use hoverItem.Bounds.Bottom rather than adding the Y+Height together - it makes the code a little simpler!

In addition, it's usually a good idea to Dispose of Brushes and Pens as soon as you've used them. This is easily done by wrapping them in a using() statement.

It works brilliantly. Thanks!
One thing to remember - in case you are in the habit of subclassing ListItem and adding the subclass to the ListView (giving you a nice bit of polymorphism), you will need to override the Clone() method, cloning any data you store in the subclass. Also remember to provide a public, parameterless constructor to your subclass.