After going through the web ,I haven't got anything that i can use directly to enable drag drop between two listview controls or within same for multiple items.
The best thing available in my opinion is this one by Josh smith:
http://www.codeproject.com/KB/WPF/ListViewDragDropManager.aspx
But it is for single SelectionMode only
Another one for multiselct is this:
http://www.codeproject.com/KB/WPF/WPF_MultiSelect_DragDrop.aspx
But the second one has many limitations..
So i have done some work to extend it from there..
I don't have much time in near weeks to work on this ,So it is not that much well written.
But it will serve as the good to get u one what u want during drag drop in more graceful way....
A small project which shows demo,U can download it from here:..the code might differ a little bit but the theme remains same,,..:)
http://www.rapidshare.com/files/306026760/Listview.zip.html
The code is as follows:
using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows;
using System.Windows.Media;
using System.Windows.Controls.Primitives;
using System.Collections.ObjectModel;
using System.Reflection;
using System.Diagnostics;
namespace DragDropManager
{
public class DragDropManager<T> where T:class,ICloneable
{
ListView listView;
// delegate for GetPosition function of DragEventArgs and
// MouseButtonEventArgs event argument objects. This delegate is used to reuse the code
// for processing both types of events.
delegate Point GetPositionDelegate(IInputElement element);
public DragDropManager(ListView listView)
{
this.listView = listView;
listView.PreviewMouseLeftButtonDown += new System.Windows.Input.MouseButtonEventHandler(listView_PreviewMouseLeftButtonDown);
listView.Drop += new System.Windows.DragEventHandler(listView_Drop);
}
void listView_Drop(object sender, DragEventArgs e)
{
int index = this.GetCurrentIndex(e.GetPosition);
if (index < 0)
return;
Dictionary<int,T> draggedItems = e.Data.GetData(typeof(Dictionary<int,T>)) as Dictionary<int,T>;
if(sender==this.listView)
this.listView.SelectedItems.Clear();
int i = 0;
ObservableCollection<T> itemsSource= this.listView.ItemsSource as ObservableCollection<T>;
// finally insert the items into the source collection
T dropTarget = itemsSource[index];
if (draggedItems.ContainsValue(dropTarget)) return;
foreach(int key in draggedItems.Keys)
{
T draggedItemClone = draggedItems[key].Clone() as T;
itemsSource.Insert(index + i, draggedItemClone);
i++;
}
}
void listView_PreviewMouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
int index = this.GetCurrentIndex(e.GetPosition);
if (index <0) return;
// this will create the drag "rectangle"
DragDropEffects allowedEffects = DragDropEffects.Move;
if (this.listView.SelectedItems.Count == 0)
return;
Dictionary<int,T> draggedItems = new Dictionary<int, T>();
foreach(T draggedItem in this.listView.SelectedItems)
{
draggedItems[this.listView.Items.IndexOf(draggedItem)] = (draggedItem as T);
}
T item = this.listView.Items[index] as T;
if (!draggedItems.ContainsValue(item))
return;
DragDrop.DoDragDrop(this.listView, draggedItems, allowedEffects);
}
ListViewItem GetListViewItem(int index)
{
if (this.listView.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated)
return null;
return this.listView.ItemContainerGenerator.ContainerFromIndex(index) as ListViewItem;
}
// returns the index of the item in the ListView
int GetCurrentIndex(GetPositionDelegate getPosition)
{
int index = -1;
for (int i = 0; i < this.listView.Items.Count; ++i)
{
ListViewItem item = GetListViewItem(i);
if (this.IsMouseOverTarget(item, getPosition))
{
index = i;
break;
}
}
return index;
}
bool IsMouseOverTarget(Visual target, GetPositionDelegate getPosition)
{
if (target == null)
return false ;
Rect bounds = VisualTreeHelper.GetDescendantBounds(target);
Point mousePos= MouseUtilities.GetMousePosition(target);
return bounds.Contains(mousePos);
}
}
}
..........................................................................
The code for MouseUtilities.cs is :
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Media;
namespace DragDropManager
{
/// <summary>
/// Provides access to the mouse location by calling unmanaged code.
/// </summary>
/// <remarks>
/// This class was written by Dan Crevier (Microsoft).
/// http://blogs.msdn.com/llobo/archive/2006/09/06/Scrolling-Scrollviewer-on-Mouse-Drag-at-the-boundaries.aspx
/// </remarks>
public class MouseUtilities
{
[StructLayout(LayoutKind.Sequential)]
private struct Win32Point
{
public Int32 X;
public Int32 Y;
};
[DllImport("user32.dll")]
private static extern bool GetCursorPos(ref Win32Point pt);
[DllImport("user32.dll")]
private static extern bool ScreenToClient(IntPtr hwnd, ref Win32Point pt);
/// <summary>
/// Returns the mouse cursor location. This method is necessary during
/// a drag-drop operation because the WPF mechanisms for retrieving the
/// cursor coordinates are unreliable.
/// </summary>
/// <param name="relativeTo">The Visual to which the mouse coordinates will be relative.</param>
public static Point GetMousePosition(Visual relativeTo)
{
Win32Point mouse = new Win32Point();
GetCursorPos(ref mouse);
// Using PointFromScreen instead of Dan Crevier's code (commented out below)
// is a bug fix created by William J. Roberts. Read his comments about the fix
// here: http://www.codeproject.com/useritems/ListViewDragDropManager.asp?msg=1911611#xx1911611xx
return relativeTo.PointFromScreen(new Point((double)mouse.X, (double)mouse.Y));
#region Commented Out
//System.Windows.Interop.HwndSource presentationSource =
// (System.Windows.Interop.HwndSource)PresentationSource.FromVisual( relativeTo );
//ScreenToClient( presentationSource.Handle, ref mouse );
//GeneralTransform transform = relativeTo.TransformToAncestor( presentationSource.RootVisual );
//Point offset = transform.Transform( new Point( 0, 0 ) );
//return new Point( mouse.X - offset.X, mouse.Y - offset.Y );
#endregion // Commented Out
}
}
}
-------------------------------------------------------------------------------------------------------------------------
I have tested the drag drop of nearly 10,000 items from one listview to another and it was excellent(milliseconds).
There are some things that i want to say:
1)If u want to this to work for all SelectionModes,U'll will to do some changes or better to checks before using selectedItems property.
2)The dragDrop between listviews will function when the two controls have same itemType(T) as the observableCollection type.
For any other type,U'll have do some little more coding .:P
3)The reason for ICloneable Interface is because if u drag and drop one item two or more times to another listview,And then select that item in the other listview,U'll get some annoying behaviour of selection.
To Overcome that,I have used IConeable Interface for the meanwhile..
If u have any questions regarding this code or feedback,u r most welcome.
And do let me know if u think to make it more graceful and well written code in near future,becoz I'll have time to work on this after 1-2 months only... hopefully....:)