Link to home
Start Free TrialLog in
Avatar of Bodekaer
Bodekaer

asked on

Drag and drop ListView items, with fade/transparent effect.

How do I create a listview item dragger... I want to be able to move arround the icons inside a listview.

Is it also possible to use the cool XP effect where the icons become transparent when moving files/folders arround in Windows.

Thanks in advance,

Michael
Avatar of Jazzyfoot
Jazzyfoot

Do you want to move the items around inside the same listview or do you want to be able to drag items between different listviews?
Here is how to do a simple drag for a single list view item...

1. Subscribe to the following events...

this.listView1.DragOver += new System.Windows.Forms.DragEventHandler(this.listView1_DragOver);
this.listView1.DragDrop += new System.Windows.Forms.DragEventHandler(this.listView1_DragDrop);
this.listView1.DragEnter += new System.Windows.Forms.DragEventHandler(this.listView1_DragEnter);
this.listView1.ItemDrag += new System.Windows.Forms.ItemDragEventHandler(this.listView1_ItemDrag);

2. Implement the following event handlers...

private void listView1_DragDrop(object sender, System.Windows.Forms.DragEventArgs e)
{
     // Convert screen position into listview position...
     Point curPoint = listView1.PointToClient(new Point(e.X, e.Y));              

     // Determine destination item based on mouse position...
     ListViewItem destItem = listView1.GetItemAt(curPoint.X, curPoint.Y);

     // Retrieve source item from event data...
     ListViewItem srcItem = e.Data.GetData(typeof(ListViewItem)) as ListViewItem;

     // Create copy of list view items (for some reason removing an item from
     // listview1.Items and inserting it again DOES NOT reorder the items!!)...
     ArrayList items = new ArrayList(listView1.Items);

     // Get index of destination item...
        int index = items.IndexOf(destItem);
         
     // Remove source item...
     items.Remove(srcItem);    
         
     // Insert source item in new position...
     items.Insert(index, srcItem);          
               
     // Repopulate list view...
     listView1.Items.Clear();
     listView1.Items.AddRange((ListViewItem[])items.ToArray(typeof(ListViewItem)));
               
     // Set source item to be selected...
     listView1.SelectedItems.Clear();
     srcItem.Selected = true;
}

private void listView1_DragEnter(object sender, System.Windows.Forms.DragEventArgs e)
{
     // Set to move...
     e.Effect = DragDropEffects.Move;          
}

private void listView1_DragOver(object sender, System.Windows.Forms.DragEventArgs e)
{
     // Convert screen position into listview position...
     Point curPoint = listView1.PointToClient(new Point(e.X, e.Y));              

     // Determine destination item based on mouse position...
     ListViewItem destItem = listView1.GetItemAt(curPoint.X, curPoint.Y);

     // Retrieve source item from event data...
     ListViewItem srcItem = e.Data.GetData(typeof(ListViewItem)) as ListViewItem;
               
     if(destItem != null)
     {
          // Select both source and destination items...
          listView1.SelectedItems.Clear();                    
          srcItem.Selected = true;
          destItem.Selected = true;
     }
}

private void listView1_ItemDrag(object sender, System.Windows.Forms.ItemDragEventArgs e)
{
     // Start drag operation with selected item...
     listView1.DoDragDrop(e.Item, DragDropEffects.All);
}


Performing the "cool XP effect" is slightly harder, I'll post some additional code shortly which demonstrates how that can be done.
The following method can be used to make a drag image, it's not really transparent but unless you look really closely you can hardly tell...

(C#)
private Image CreateDragImage(ListViewItem item)
{
     // Ensure the node is completely visible...
     item.EnsureVisible();

     // Calculate node rectange (including item icon)...
     Rectangle itemRect = new Rectangle(
          item.Bounds.Left - 20, item.Bounds.Top,
          item.Bounds.Width + 20, item.Bounds.Height);

     // Set selected node to null & redraw...
     listView1.SelectedItems.Clear();
     listView1.Update();

     // Get reference to tree control graphics...
     Graphics listviewGraphics = listView1.CreateGraphics();

     // Create bitmap and get reference to it's graphics...
     Bitmap   memBitmap   = new Bitmap(itemRect.Width, itemRect.Height);
     Graphics memGraphics = Graphics.FromImage(memBitmap);
               
     // Get references to device context handles...
     IntPtr treeDC = listviewGraphics.GetHdc();
     IntPtr memDC  = memGraphics.GetHdc();
               
     // Copy required region of screen to bitmap...
     BitBlt(
          memDC, 0, 0, itemRect.Width, itemRect.Height,
          treeDC,itemRect.Left, itemRect.Top, 0xCC0020);

     // Release device contexts...
     listviewGraphics.ReleaseHdc(treeDC);
     memGraphics.ReleaseHdc(memDC);

     // Make it look faded...
     memGraphics.FillRectangle(new SolidBrush(Color.FromArgb(128, listView1.BackColor)), 0, 0, itemRect.Width, itemRect.Height);

     // Make selection color transparent, leaving text only...
     memBitmap.MakeTransparent(listView1.BackColor);              
                   
     // Return image...
     return memBitmap;
}

You will also need to import the following method;

(C#)
[DllImport("gdi32.dll")]
public static extern bool BitBlt(
     IntPtr hdcDest, // handle to destination DC
     int nXDest, // x-coord of destination upper-left corner
     int nYDest, // y-coord of destination upper-left corner
     int nWidth, // width of destination rectangle
     int nHeight, // height of destination rectangle
     IntPtr hdcSrc, // handle to source DC
     int nXSrc, // x-coordinate of source upper-left corner
     int nYSrc, // y-coordinate of source upper-left corner
     System.Int32 dwRop // raster operation code
);

The following describes how to integrate it into the drag and drop example...

1. Define some member variables

(C#)
private Image m_dragImage = null;
private Point m_dragPosition;

2. Change the "listView1_ItemDrag" handler as follows.

(C#)
private void listView1_ItemDrag(object sender, System.Windows.Forms.ItemDragEventArgs e)
{
     m_dragImage = CreateDragImage(e.Item as ListViewItem);

     // Start drag operation with selected item...
     listView1.DoDragDrop(e.Item, DragDropEffects.All);              
}

3. Add the following code to the END of the "listView1_DragOver" handler.

if(m_dragImage != null && m_dragPosition != curPoint)
{
     // Redraw last image position...
     listView1.Invalidate(new Rectangle(m_dragPosition.X, m_dragPosition.Y, m_dragImage.Width, m_dragImage.Height));
     listView1.Update();
         
     // Determine new image position...
     Point imagePos = new Point(curPoint.X, curPoint.Y);
         
     // Draw image...
     Graphics.FromHwnd(listView1.Handle).DrawImage(m_dragImage, imagePos);

     m_dragPosition = curPoint;          
}

4. You might also need a call to "listView1.Refresh()" at the end of the "listView1_DragDrop" handler

Avatar of Bodekaer

ASKER

Ok, looks great. What if I want it to work with VB.NET ? :)

(it was within the same listview)
ASKER CERTIFIED SOLUTION
Avatar of SpideyMod
SpideyMod

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial