Drag and Drop ListView row reordering






4.67/5 (35 votes)
Jul 18, 2003
2 min read

332785

15822
This article presents a simple ListView control that provides row reordering using Drag and Drop.
Introduction
An application I worked on required a mechanism whereby the user could rearrange the index of a row within a ListView
control at runtime. For this, I wanted to use Drag and Drop, however after trailing through the Internet for some examples, I was unable to find something specific to C#, therefore I decided to implement my own.
Using the code
The ListView
control presented here adds the property AllowRowReorder
to the existing .NET ListView
, which enables or disables the control's ability to reorder it rows.
In order to provide Drag and Drop support to the control, the methods OnItemDrag
, OnDragEnter
and OnDragDrop
of the base control must be overridden. OnItemDrag
is the method called once the control detects that a Drag and Drop operation has begun. It is here that any pre 'Drag and Drop' logic must be performed; this entails ensuring that row reordering has been enabled. Finally to inform the ListView
that the Drag and Drop operation is valid, the DoDragDrop
method is called, the first parameter being the Drag data, which identifies the object to be dragged and needs to be a String
, Bitmap
or a MetaFile
. For the ListView
provided, this is a hard-coded string, which will be used in the OnDragEnter
method for Drop validation, since the items to be dragged will implicitly be the currently selected ListView
items.
protected override void OnItemDrag(ItemDragEventArgs e)
{
base.OnItemDrag(e);
if(!this.AllowRowReorder)
{
return;
}
base.DoDragDrop(REORDER, DragDropEffects.Move);
}
The OnDragEnter
validates whether or not the item being dragged, as identified by the DragEventArgs.Data
object, can be dropped into the control. Note that when Drag and Drop is enabled within the control, it must provide logic to handle drag events from all drag enabled sources. The ListView
provided simply ensures that the data passed matches the hard-coded value supplied by the OnItemDrag
method.
protected override void OnDragEnter(DragEventArgs e)
{
base.OnDragEnter(e);
if(!this.AllowRowReorder)
{
e.Effect = DragDropEffects.None;
return;
}
if(!e.Data.GetDataPresent(DataFormats.Text))
{
e.Effect = DragDropEffects.None;
return;
}
String text = (String)e.Data.GetData(REORDER.GetType());
if(text.CompareTo(REORDER)==0)
{
e.Effect = DragDropEffects.Move;
}
else
{
e.Effect = DragDropEffects.None;
}
}
Subsequently, OnDragOver
ensures the continuing validity of the Drop operation as you move the mouse over the items in the control.
protected override void OnDragOver(DragEventArgs e)
{
if(!this.AllowRowReorder)
{
e.Effect = DragDropEffects.None;
return;
}
if(!e.Data.GetDataPresent(DataFormats.Text))
{
e.Effect = DragDropEffects.None;
return;
}
Point cp = base.PointToClient(new Point(e.X, e.Y));
ListViewItem hoverItem = base.GetItemAt(cp.X, cp.Y);
if(hoverItem==null)
{
e.Effect = DragDropEffects.None;
return;
}
foreach(ListViewItem moveItem in base.SelectedItems)
{
if(moveItem.Index==hoverItem.Index)
{
e.Effect = DragDropEffects.None;
hoverItem.EnsureVisible();
return;
}
}
base.OnDragOver(e);
String text = (String)e.Data.GetData(REORDER.GetType());
if(text.CompareTo(REORDER)==0)
{
e.Effect = DragDropEffects.Move;
hoverItem.EnsureVisible();
}
else
{
e.Effect = DragDropEffects.None;
}
}
Finally OnDragDrop
performs the actual Drop operation, this includes identifying the ListViewItems
to be moved and the row index they will be moved to. Notice that I’m assuming that the ListViewItems
to be moved are the control's currently selected items.
protected override void OnDragDrop(DragEventArgs e)
{
base.OnDragDrop(e);
if(!this.AllowRowReorder)
{
return;
}
if(base.SelectedItems.Count==0)
{
return;
}
Point cp = base.PointToClient(new Point(e.X, e.Y));
ListViewItem dragToItem = base.GetItemAt(cp.X, cp.Y);
if(dragToItem==null)
{
return;
}
int dropIndex = dragToItem.Index;
if(dropIndex>base.SelectedItems[0].Index)
{
dropIndex++;
}
ArrayList insertItems =
new ArrayList(base.SelectedItems.Count);
foreach(ListViewItem item in base.SelectedItems)
{
insertItems.Add(item.Clone());
}
for(int i=insertItems.Count-1;i>=0;i--)
{
ListViewItem insertItem =
(ListViewItem)insertItems[i];
base.Items.Insert(dropIndex, insertItem);
}
foreach(ListViewItem removeItem in base.SelectedItems)
{
base.Items.Remove(removeItem);
}
}
Downloads
Source and demo projects have been created using #Develop.
Update
- 6th October 2003 - Fix for reordering problem when dragging multiple items.
- 10 November 2003 – Updated for automatic scrolling during a Drag operation (suggestion from JayBarry and Brian Pierson)