There doesn't seem to be anything out of the box yet in Xamarin.Forms so I did a search and found the following :-
http://xamurais.com/drag-and-drop-entre-listview-en-xamarin-android/
This sample was written in classic Xamarin.Android, however I was looking for a Xamarin.Forms implementation. This sample however provided me with the ground work for the sample I propose below.
My sample is only targeting Android at the moment.
My implementation consists of a ViewCellRenderer, this allows you to define a ListView with an ItemTemplate, so that you can bind your ListView to more than a List
MyViewCellRenderer.cs :-
using System.Collections;
using Android.Content;
using Android.Views;
using ListViewDragDropSample.Droid;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
using View = Android.Views.View;
[assembly: ExportRenderer(typeof(ViewCell), typeof(MyViewCellRenderer))]
namespace ListViewDragDropSample.Droid
{
public class MyViewCellRenderer : ViewCellRenderer
{
public ListView ParentListView { get; set; }
public IList Items { get; set; }
protected override View GetCellCore(Cell item, View convertView, ViewGroup parent, Context context)
{
ParentListView = item.ParentView as ListView;
if (ParentListView != null)
{
Items = ParentListView.ItemsSource as IList;
}
var cellcore = base.GetCellCore(item, convertView, parent, context);
cellcore.Drag -= CellcoreOnDrag;
cellcore.Drag += CellcoreOnDrag;
return cellcore;
}
private void CellcoreOnDrag(object sender, View.DragEventArgs args)
{
ViewGroup = sender as ViewGroup;
if (ViewGroup != null)
{
ListView = ViewGroup.Parent.Parent as Android.Widget.ListView;
}
switch (args.Event.Action)
{
case DragAction.Started:
args.Handled = true;
break;
case DragAction.Entered:
args.Handled = true;
if (ListView != null)
{
if (FirstIndex == -1)
{
FirstIndex = ListView.IndexOfChild(ViewGroup.Parent as View);
}
}
break;
case DragAction.Exited:
args.Handled = true;
break;
case DragAction.Drop:
args.Handled = true;
if (SecondIndex == -1)
{
SecondIndex = ListView.IndexOfChild(ViewGroup.Parent as View);
}
if (FirstIndex != -1)
{
var firstItem = Items[FirstIndex];
if (firstItem != null)
{
Items.RemoveAt(FirstIndex);
Items.Insert(SecondIndex, firstItem);
ParentListView.ItemsSource = null;
ParentListView.ItemsSource = Items;
}
}
FirstIndex = -1;
SecondIndex = -1;
break;
case DragAction.Ended:
args.Handled = true;
break;
}
}
public Android.Widget.ListView ListView { get; set; }
public ViewGroup ViewGroup { get; set; }
private static int _firstIndex = -1;
private static int _secondIndex = -1;
public static int FirstIndex
{
get { return _firstIndex; }
set { _firstIndex = value; }
}
public static int SecondIndex
{
get { return _secondIndex; }
set { _secondIndex = value; }
}
}
}
MyListViewRenderer.cs :-
using Android.Content;
using ListViewDragDropSample.Droid;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(ListView), typeof(MyListViewRenderer))]
namespace ListViewDragDropSample.Droid
{
public class MyListViewRenderer : ListViewRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs e)
{
base.OnElementChanged(e);
Control.ItemLongClick += (s, args) =>
{
ClipData data = ClipData.NewPlainText("List", args.Position.ToString());
MyDragShadowBuilder myShadownScreen = new MyDragShadowBuilder(args.View);
args.View.StartDrag(data, myShadownScreen, null, 0);
};
}
}
}
MainPage.xaml :-
MainPage.xaml.cs :-
using System.Collections.Generic;
using Xamarin.Forms;
namespace ListViewDragDropSample
{
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
Items = new List- ();
for (int i = 1; i < 11; i++)
{
Items.Add(new Item()
{
Title = "Title : " + i,
Description = "Description : " + i,
});
}
BindingContext = this;
}
public List
- Items { get; set; }
}
}
MyDragShadowBuilder.cs :-
using Android.Graphics;
using Android.Graphics.Drawables;
using Android.Views;
namespace ListViewDragDropSample.Droid
{
public class MyDragShadowBuilder : View.DragShadowBuilder
{
private Drawable shadow;
public MyDragShadowBuilder(View v)
: base(v)
{
v.DrawingCacheEnabled = true;
Bitmap bm = v.DrawingCache;
shadow = new BitmapDrawable(bm);
shadow.SetColorFilter(Color.ParseColor("#4EB1FB"), PorterDuff.Mode.Multiply);
}
public override void OnProvideShadowMetrics(Point size, Point touch)
{
int width = View.Width;
int height = View.Height;
shadow.SetBounds(0, 0, width, height);
size.Set(width, height);
touch.Set(width / 2, height / 2);
}
public override void OnDrawShadow(Canvas canvas)
{
base.OnDrawShadow(canvas);
shadow.Draw(canvas);
}
}
}
And finally the Item.cs :-
namespace ListViewDragDropSample
{
public class Item
{
public string Title { get; set; }
public string Description { get; set; }
}
}
The complete sample is here :-http://www.smartmobiledevice.co.uk/Samples/Xamarin/ListViewDragDropSample.zip