Skip to main content
Email Password   helpLost your password?

Sample Image - DragAndDropListView.gif

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.

protected override void OnItemDrag(ItemDragEventArgs e)
{
 if(!m_allowReorder)
 {
  base.OnItemDrag(e);
  return;
 }

 base.DoDragDrop(GetDataForDragDrop(), DragDropEffects.Move);

 base.OnItemDrag(e);
}

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.

protected override void OnDragOver(DragEventArgs drgevent)
{
 if(!m_allowReorder)
 {
  base.OnDragOver(drgevent);
  return;
 }
 if(!drgevent.Data.GetDataPresent(typeof(DragItemData).ToString()))
 {
  drgevent.Effect = DragDropEffects.None;
  return;
 }

 if(base.Items.Count > 0)
 {
  Point clientPoint = base.PointToClient(new Point(drgevent.X, drgevent.Y));
  ListViewItem hoverItem = base.GetItemAt(clientPoint.X, clientPoint.Y);

  Graphics g = this.CreateGraphics();

  if(hoverItem == null)
  {
   drgevent.Effect = DragDropEffects.Move;

   if(m_previousItem != null)
   {
    m_previousItem = null;
    Invalidate();
   }

   hoverItem = base.Items[base.Items.Count - 1];

   if(this.View == View.Details || this.View == View.List)
   {
    g.DrawLine(new Pen(m_lineColor, 2), new Point(hoverItem.Bounds.X, 
     hoverItem.Bounds.Y + hoverItem.Bounds.Height), 
     new Point(hoverItem.Bounds.X + 
     this.Bounds.Width, hoverItem.Bounds.Y + 
     hoverItem.Bounds.Height));
    g.FillPolygon(new SolidBrush(m_lineColor),
     new Point[] {new Point(hoverItem.Bounds.X, 
     hoverItem.Bounds.Y + hoverItem.Bounds.Height - 5), 
     new Point(hoverItem.Bounds.X + 5, hoverItem.Bounds.Y + 
     hoverItem.Bounds.Height), 
     new Point(hoverItem.Bounds.X, hoverItem.Bounds.Y + 
     hoverItem.Bounds.Height + 5)});
    g.FillPolygon(new SolidBrush(m_lineColor), 
     new Point[] {new Point(this.Bounds.Width - 4, 
     hoverItem.Bounds.Y + hoverItem.Bounds.Height - 5), 
     new Point(this.Bounds.Width - 9, 
     hoverItem.Bounds.Y + hoverItem.Bounds.Height), 
     new Point(this.Bounds.Width - 4, 
     hoverItem.Bounds.Y + hoverItem.Bounds.Height + 5)});
   }
   else
   {
    g.DrawLine(new Pen(m_lineColor, 2), 
     new Point(hoverItem.Bounds.X + 
     hoverItem.Bounds.Width, hoverItem.Bounds.Y), 
     new Point(hoverItem.Bounds.X + 
     hoverItem.Bounds.Width, hoverItem.Bounds.Y + 
     hoverItem.Bounds.Height));
    g.FillPolygon(new SolidBrush(m_lineColor), 
     new Point[] {new Point(hoverItem.Bounds.X + 
     hoverItem.Bounds.Width - 5, hoverItem.Bounds.Y), 
     new Point(hoverItem.Bounds.X + 
     hoverItem.Bounds.Width + 5, hoverItem.Bounds.Y), 
     new Point(hoverItem.Bounds.X + 
     hoverItem.Bounds.Width, hoverItem.Bounds.Y + 5)});
    g.FillPolygon(new SolidBrush(m_lineColor), 
     new Point[] {new Point(hoverItem.Bounds.X + 
     hoverItem.Bounds.Width - 5, hoverItem.Bounds.Y + 
     hoverItem.Bounds.Height), 
     new Point(hoverItem.Bounds.X + 
     hoverItem.Bounds.Width + 5, hoverItem.Bounds.Y + 
     hoverItem.Bounds.Height), 
     new Point(hoverItem.Bounds.X + hoverItem.Bounds.Width, 
     hoverItem.Bounds.Y + hoverItem.Bounds.Height - 5)});
   }

   base.OnDragOver(drgevent);

   return;
  }

  if((m_previousItem != null && 
    m_previousItem != hoverItem) || m_previousItem == null)
  {
   this.Invalidate();
  }

  m_previousItem = hoverItem;

  if(this.View == View.Details || this.View == View.List)
  {
   g.DrawLine(new Pen(m_lineColor, 2), new Point(hoverItem.Bounds.X, 
    hoverItem.Bounds.Y), new Point(hoverItem.Bounds.X + this.Bounds.Width, 
    hoverItem.Bounds.Y));
   g.FillPolygon(new SolidBrush(m_lineColor), 
    new Point[] {new Point(hoverItem.Bounds.X, 
    hoverItem.Bounds.Y - 5), new Point(hoverItem.Bounds.X + 5, 
    hoverItem.Bounds.Y), new Point(hoverItem.Bounds.X, 
    hoverItem.Bounds.Y + 5)});
   g.FillPolygon(new SolidBrush(m_lineColor), 
    new Point[] {new Point(this.Bounds.Width - 4, 
    hoverItem.Bounds.Y - 5), new Point(this.Bounds.Width - 9, 
    hoverItem.Bounds.Y), new Point(this.Bounds.Width - 4, 
    hoverItem.Bounds.Y + 5)});
  }
  else
  {
   g.DrawLine(new Pen(m_lineColor, 2), 
    new Point(hoverItem.Bounds.X, hoverItem.Bounds.Y), 
    new Point(hoverItem.Bounds.X, 
    hoverItem.Bounds.Y + hoverItem.Bounds.Height));
   g.FillPolygon(new SolidBrush(m_lineColor), 
    new Point[] {new Point(hoverItem.Bounds.X - 5, 
    hoverItem.Bounds.Y), 
    new Point(hoverItem.Bounds.X + 5, hoverItem.Bounds.Y), 
    new Point(hoverItem.Bounds.X, hoverItem.Bounds.Y + 5)});
   g.FillPolygon(new SolidBrush(m_lineColor), 
    new Point[] {new Point(hoverItem.Bounds.X - 5, 
    hoverItem.Bounds.Y + hoverItem.Bounds.Height), 
    new Point(hoverItem.Bounds.X + 5, 
    hoverItem.Bounds.Y + hoverItem.Bounds.Height), 
    new Point(hoverItem.Bounds.X, 
    hoverItem.Bounds.Y + hoverItem.Bounds.Height - 5)});

  }

  foreach(ListViewItem itemToMove in base.SelectedItems)
  {
   if(itemToMove.Index == hoverItem.Index)
   {
    drgevent.Effect = DragDropEffects.None;
    hoverItem.EnsureVisible();
    return;
   }
  }

  hoverItem.EnsureVisible();
 }

 drgevent.Effect = DragDropEffects.Move;

 base.OnDragOver(drgevent);
}

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:

[Add the Line to the Last Item on Bottom]
g.DrawLine(new Pen(Brushes.Red, 2), new Point(hoverItem.Bounds.X, 
    hoverItem.Bounds.Y + hoverItem.Bounds.Height), 
    new Point(hoverItem.Bounds.X + this.Bounds.Width, 
    hoverItem.Bounds.Y + hoverItem.Bounds.Height));
g.FillPolygon(Brushes.Red, new Point[] {new Point(hoverItem.Bounds.X, 
    hoverItem.Bounds.Y + hoverItem.Bounds.Height - 5), 
    new Point(hoverItem.Bounds.X + 5, 
    hoverItem.Bounds.Y + hoverItem.Bounds.Height), 
    new Point(hoverItem.Bounds.X, 
    hoverItem.Bounds.Y + hoverItem.Bounds.Height + 5)});
g.FillPolygon(Brushes.Red, new Point[] {new Point(this.Bounds.Width - 4, 
    hoverItem.Bounds.Y + hoverItem.Bounds.Height - 5), 
    new Point(this.Bounds.Width - 9, 
    hoverItem.Bounds.Y + hoverItem.Bounds.Height), 
    new Point(this.Bounds.Width - 4, 
    hoverItem.Bounds.Y + hoverItem.Bounds.Height + 5)});
[Add the Line to the Top of the Current Item]
g.DrawLine(new Pen(Brushes.Red, 2), new Point(hoverItem.Bounds.X, 
    hoverItem.Bounds.Y), new Point(hoverItem.Bounds.X + 
    this.Bounds.Width, hoverItem.Bounds.Y));
g.FillPolygon(Brushes.Red, new Point[] {new Point(hoverItem.Bounds.X, 
    hoverItem.Bounds.Y - 5), new Point(hoverItem.Bounds.X + 5, 
    hoverItem.Bounds.Y), 
    new Point(hoverItem.Bounds.X, hoverItem.Bounds.Y + 5)});
g.FillPolygon(Brushes.Red, new Point[] {new Point(this.Bounds.Width - 4, 
    hoverItem.Bounds.Y - 5), new Point(this.Bounds.Width - 9, 
    hoverItem.Bounds.Y), 
    new Point(this.Bounds.Width - 4, hoverItem.Bounds.Y + 5)});

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.

protected override void OnDragDrop(DragEventArgs drgevent)
{
 if(!m_allowReorder)
 {
  base.OnDragDrop(drgevent);
  return;
 }

 Point clientPoint = base.PointToClient(new Point(drgevent.X, drgevent.Y));
 ListViewItem hoverItem = base.GetItemAt(clientPoint.X, clientPoint.Y);

 if(!drgevent.Data.GetDataPresent(typeof(DragItemData).ToString()) || 
  ((DragItemData) 
   drgevent.Data.GetData(typeof(DragItemData).ToString())).ListView == null ||
  ((DragItemData) 
   drgevent.Data.GetData(typeof(DragItemData).ToSTring())).DragItems.Count == 0)
     return;
  
 DragItemData data = 
   (DragItemData) drgevent.Data.GetData(typeof(DragItemData).ToString());

 if(hoverItem == null)
 {
  for(int i=0; i<insertItems.Count; i++)
  {
   ListViewItem newItem = (ListViewItem) insertItems[i];
   base.Items.Add(newItem);
  }
 }
 else
 {
  int hoverIndex = hoverItem.Index;

  if(this == data.ListView)
  {
   if(hoverIndex > base.SelectedItems[0].Index)
    hoverIndex++;
  }

  for(int i=data.DragItems.Count - 1; i >= 0; i--)
  {
   ListViewItem newItem = (ListViewItem) data.DragItems[i];
   base.Items.Insert(hoverIndex, newItem);
  }
 }

 if(data.ListView != null)
 {
  foreach(ListViewItem itemToRemove in data.ListView.SelectedItems)
  {
   data.ListView.Items.Remove(itemToRemove);
  }
 }


 if(m_previousItem != null)
 {
  m_previousItem = null;
 }

 this.Invalidate();

 base.OnDragDrop (drgevent);

}

Known Problems

Version History

References

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralRetaining tag or name property of item after drag-drop reorder Pin
honcho26
10:41 12 Jun '08  
GeneralFOUND THE ANSWER! Re: Retaining tag or name property of item after drag-drop reorder Pin
honcho26
11:45 12 Jun '08  
General"Jumping" at the bottom on drag and drop [FIX] - dragging down 2 nodes instead of one [FIX] Pin
paintor
2:37 11 Feb '08  
QuestionProblem with Sorting [modified] Pin
shashank_156
0:31 11 Feb '08  
GeneralBug Fix - When reordering down and horizontal scroll bar is visible Pin
Steve Marshall
7:25 29 Oct '07  
GeneralVB Code Pin
powermetal
9:19 1 Oct '07  
GeneralBug Fix for LargeIcon or SmallIcon view Pin
huanyi
4:02 25 Sep '07  
GeneralRe: Bug Fix for LargeIcon or SmallIcon view Pin
nullesc
18:24 19 Nov '08  
GeneralDrag&Drop only inside of current List Pin
MichaZ123
1:30 8 Aug '07  
GeneralQuestion Pin
loneferret
13:02 17 Jul '06  
QuestionLicensing Pin
mark_ledwich
20:44 20 Apr '06  
GeneralView LargeIcon ? Pin
miha52
23:59 7 Nov '05  
QuestionFlicker when dragging Pin
dgannon34
19:16 16 Sep '05  
Generalhow to implement drag and drop in the asp.net page ,like list data or multiple select from one to another Pin
Jade_king
23:13 15 Sep '05  
GeneralListViewItem Image problem Pin
didis
6:24 1 Dec '04  
GeneralRe: ListViewItem Image problem Pin
Diego F.
5:30 3 Apr '07  
GeneralAllow both items drag & drop and files drag & drop Pin
Bertrand Dunogier
3:35 3 Oct '04  
GeneralRe: Allow both items drag & drop and files drag & drop Pin
Craig Nagy
19:37 1 Mar '05  
GeneralRe: Allow both items drag & drop and files drag & drop Pin
Chris Cocker
5:29 5 Apr '05  
Generalgreat control, 2 questions Pin
cmac
3:41 24 Aug '04  
GeneralRe: great control, 2 questions Pin
James Pepin
6:47 8 Jun '05  
GeneralEnhancement/bug fix for List/Details Pin
Richard Birkby
0:17 23 Jul '04  
GeneralThankyou Pin
Richard Birkby
0:00 20 Jul '04  


Last Updated 12 May 2004 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2009