We use cookies to give you the best experience on our website. If you continue to browse, then you agree to our privacy policy and cookie policy. (Last updated on: June 24, 2019).
Unfortunately, activation email could not send to your email. Please try again.
Syncfusion Feedback

Dragging item without reordering and dropping it in other item.

Thread ID:

Created:

Updated:

Platform:

Replies:

146969 Aug 27,2019 09:04 AM UTC Sep 19,2019 12:53 PM UTC Xamarin.Forms 9
loading
Tags: SfListView
Radoslaw Kubas
Asked On August 27, 2019 10:36 AM UTC

Hello,

Is it possible to get with SfListView following behaviour? 

I have SfListView:
A
B
C
D
E

Now I want long press item "D" and drag it over item "C", drop it there and with information what item was dragged and what item is the drop target I want modify items collection and have for example SfListView:
A
B
CD
E

It would be something similar like "Delete item when dropping in particular view" but I would like to drop item in other item.

Regards,
Radek







Subburaj Pandian Veluchamy [Syncfusion]
Replied On August 28, 2019 11:44 AM UTC

Hi Radoslaw, 
  
Thank you for using Syncfusion product. 
  
Based on the provided information, we have checked and you can achieve your requirement by deleting the dragged item and concatenating that with the new item. We have attached tested sample and model code snippet for your reference, please find the sample and code snippet from the following. 
  
  
public class Behavior : Behavior<SfListView> 
{ 
  
   protected override void OnAttachedTo(SfListView list) 
   { 
      ListView = list; 
      ListView.ItemDragging += ListView_ItemDragging; 
      base.OnAttachedTo(list); 
   } 
  
   private void ListView_ItemDragging(object sender, ItemDraggingEventArgs e) 
   { 
      if (e.Action == DragAction.Drop) 
      { 
         Device.BeginInvokeOnMainThread(() => 
         { 
            Contacts insertItem = null; 
            if (e.NewIndex < (ListView.BindingContext asContactsViewModel).ContactItems.Count - 1) 
            { 
              insertItem = ListView.DataSource.DisplayItems[e.NewIndex + 1] as Contacts; 
            } 
            else 
            { 
              insertItem = ListView.DataSource.DisplayItems[e.NewIndex - 1] as Contacts; 
            } 
  
            var dropItem = ListView.DataSource.DisplayItems[e.NewIndex] as Contacts; 
            insertItem.ContactName = insertItem.ContactName + " - " + dropItem.ContactName; 
  
            (ListView.BindingContext as ContactsViewModel).ContactItems.Remove(dropItem); 
  
         }); 
         e.Handled = true; 
       } 
    } 
}  
  
Please let us know whether sample meets your requirement. 
  
Regards,
Subburaj Pandian V   


Radoslaw Kubas
Replied On September 2, 2019 04:49 PM UTC

Hello,

Thank you for your example. I analysed it and mixed with some other idea I could create solution almost perfect fro my requirements. I have one problem. Could you advice me the best way to stop not dragged list items from moving to give space for dragged item? I tried 2 solutions:

1. In "SfListView_OnItemDragging(object sender, ItemDraggingEventArgs e)" in "case DragAction.Dragging:" just modify "e.Handled = true;". It stop items from moving but unfortunately it stops dragged item as well. I had to handle item dragging, and it works fine, but the problem was "case DragAction.Drop", I couldn't correctly reset item position. 

2. I created custom "DragDropController" and overrided "OnLayoutItem(View element, Rectangle rect)" to just return "true" (and not animate layout), it works almost perfect, but not always. If SfListView is scrolled during dragging items are moved to wrong positions. It seems that SfListView expects items to be in new positions even if I didn't want to move them. 

Could you advice the best way to stop items from moving? Maybe there is any switch to just stop reordering?

Thank you in advance!

Regards,
Radek

Mageshyadav M [Syncfusion]
Replied On September 4, 2019 12:39 AM UTC

Hi Radek, 
 
We would like to inform you that there is no option to avoid moving of items while dragging each item. Hence suggest you to customize drag drop by overriding the OnLayoutItem method and layout the item instead of returning true based on your requirement. 
 
Regards, 
Mageshyadav.M 


Bernard
Replied On September 18, 2019 06:51 AM UTC

Dear Radoslaw Kubas,

Were you able to resolvce this? Are you able to drag an item onto another item without the second item jumping to make space?

Our use cases are:
  • Perform action when 2 items are combined (perform calculation, transform, start process... => your case I believe?)
  • Move item in folder; in a Listview containing both items and folders, drop items into folders... (= basically how file explorer works)
@Syncfusion... Any plans to simplify this or could you provide a fully working example how to achieve this (drag drop onto another item without underlaying items jumping)?

Radoslaw Kubas
Replied On September 18, 2019 09:21 AM UTC

Hello Bernard,

It is still "work in progress" and I think it can be better optimised, but I believe I have working solution. It is not perfect (strange things happen when SfListView is scrolled during item dragging. It works fine, but empty space appears in other locations. I guess it is connected with cell reusing). Below, I will copy my current solution, if you need more info, let me know.

Regards,
Radek

Code behind:

view constructor:
SfListView.DragDropController = new CustomDragDropController(SfListView) {UpdateSource = false};

others:

private async void SfListView_OnItemDragging(object sender, ItemDraggingEventArgs e)
{
 var draggedItem = e.ItemData as PersonViewModel;
 var element = FindViewByBindingContext(draggedItem);
                
 switch (e.Action)
 {
  case DragAction.Start:
  {
   _startPositionY = e.Position.Y;

   var grid = GetGrid(element as ListViewItem);

   _ = grid.ChangeBackgroundColorTo(Color.FromRgba(0x51, 0x33, 0x47, 0x80), 160, Easing.SinOut);

   break;
  }
  case DragAction.Dragging:
  {
   var dropTarget = FindCurrentTarget(element);

   ClearTargetsBackground(dropTarget, element);

   if (dropTarget != null && dropTarget != element)
   {
    MarkTargetBackground(dropTarget);
   }
   else
   {
    _previousDropTarget = dropTarget;
   }

   break;
  }
  case DragAction.Drop:
  {
   var dropTarget = FindCurrentTarget(element);

   e.Cancel = true;

   var grid = GetGrid(element as ListViewItem);

   grid.BackgroundColor = Color.Transparent;
       
   ClearTargetsBackground(null);

   if (dropTarget is null || dropTarget == element)
   {
    return;
   }
                    
   var source = (PersonViewModel) e.ItemData;
   var destination = (PersonViewModel) dropTarget.BindingContext;
   
   var vm = (SplitPageViewModel) BindingContext;
   vm.Split.Link(source, destination);

   await Task.Delay(100);

   SfListView.RefreshView();

   break;
  }
 }
}

private Grid GetGrid(ListViewItem listViewItem)
{
 var grid = (Grid)listViewItem.Content;

 return grid;
}

private VisualElement FindViewByBindingContext(object bindingContext)
{
 var elements = ((VisualContainer)((ScrollView)SfListView.Children[0]).Children[0]).Children;
 var rows = elements.Where(element => element is ListViewItem item).ToList();

 VisualElement result = null;
 foreach (var row in rows)
 {
  if (row.BindingContext == bindingContext)
  {
   result = row;
   
   break;
  }
 }

 return result;
}

private View FindCurrentTarget(VisualElement element)
{
 var coordinates = _platformLocation.GetCoordinates(element);

 coordinates.X += (float)element.Width / 2.0f;
 coordinates.Y += (float)element.Height / 2.0f;

 var dropList = ((VisualContainer)((ScrollView)SfListView.Children[0]).Children[0]).Children;

 foreach (var view in dropList)
 {
  if(view.BindingContext is null)
  {
   continue;
  }

  var item = (ListViewItem)view;

  var targetCoordinates = _platformLocation.GetCoordinates(item);

  if (
      coordinates.Y >= targetCoordinates.Y  &&
      coordinates.Y <= targetCoordinates.Y + item.Content.Height &&
      coordinates.X >= targetCoordinates.X &&
      coordinates.X <= targetCoordinates.X + item.Content.Width)
  {
   return item;
  }
 }


 return null;
}

XAML:
customSyncfusion:CustomSfListView x:Name="SfListView" 
                                                              SelectionMode="None"
                                                              DragStartMode="OnHold"
                                                              ItemDragging="SfListView_OnItemDragging"/>
others:
public class CustomSfListView : SfListView
{
 protected override bool ShouldInvalidateOnChildRemoved(View child)
 {
  return true;
 }

 protected override bool ShouldInvalidateOnChildAdded(View child)
 {
  return true;
 }
}

class CustomDragDropController : DragDropController
{
 public CustomDragDropController(SfListView listView):base(listView)
 {
 }

 protected override async Task<bool> OnLayoutItem(View element, Rectangle rect)
 {
  return true;
 }
}



Radoslaw Kubas
Replied On September 18, 2019 09:27 AM UTC

private void ClearTargetsBackground(View dropTarget, params VisualElement[] exception)
{
 var dropList = ((VisualContainer)((ScrollView)SfListView.Children[0]).Children[0]).Children;

 if (_previousDropTarget == dropTarget)
 {
  return;
 }

 if (dropTarget is null)
 {
  _previousDropTarget = null;
 }

 _hapticFeedback.PerformHapticFeedback(SfListView, FeedbackType.VirtualKey);

 foreach (var view in dropList)
 {
  var item = (ListViewItem) view;

  if (view != dropTarget && !exception.Contains(view))
  {
   _ = item.Content.ChangeBackgroundColorTo(Color.FromRgba(0x51, 0x33, 0x47, 0x00), 160U, Easing.SinOut);
  }
 }
}

private void MarkTargetBackground(View dropTarget)
{
 if (_previousDropTarget == dropTarget)
 {
  return;
 }

 _previousDropTarget = dropTarget;

 var item = (ListViewItem)dropTarget;

 _ = item.Content.ChangeBackgroundColorTo(Color.FromRgba(0x51, 0x33, 0x47, 0xff), 60U, Easing.SinOut);
}

Bernard
Replied On September 18, 2019 09:44 AM UTC

Brilliant! Thank you so much for providing this.

@Syncfusion.. any further suggestions?

Radoslaw Kubas
Replied On September 18, 2019 10:02 AM UTC

You welcome. To make example complete, to get screen location I use:

public interface ILocation
    {
        System.Drawing.PointF GetCoordinates(global::Xamarin.Forms.VisualElement view);

        System.Drawing.PointF GetCoordinatesInPixels(global::Xamarin.Forms.VisualElement view);
    }

ANDROID:
public class LocationImplementation : ILocation
    {
        public PointF GetCoordinates(VisualElement element)
        {
            var renderer = Platform.GetRenderer(element);
            var nativeView = renderer?.View;
            var location = new int[2];
            var density = nativeView?.Context.Resources.DisplayMetrics.Density;

            nativeView?.GetLocationOnScreen(location);
            return new System.Drawing.PointF((location[0] / density)??float.NegativeInfinity, (location[1] / density)??float.NegativeInfinity);
        }

        public PointF GetCoordinatesInPixels(VisualElement element)
        {
            var renderer = Platform.GetRenderer(element);
            var nativeView = renderer?.View;
            var location = new int[2];

            nativeView?.GetLocationOnScreen(location);
            return new System.Drawing.PointF(location[0], location[1]);
        }
    }

iOS:
public class LocationImplementation : ILocation
    {
        public PointF GetCoordinates(VisualElement element)
        {
            var renderer = Platform.GetRenderer(element);
            var nativeView = renderer?.NativeView;
            var rect = nativeView?.Superview.ConvertPointToView(nativeView.Frame.Location, null);

            return new System.Drawing.PointF((float)Math.Round(rect?.X??nfloat.NegativeInfinity), (float)Math.Round(rect?.Y??nfloat.NegativeInfinity));

        }

        public PointF GetCoordinatesInPixels(VisualElement element)
        {
            var renderer = Platform.GetRenderer(element);
            var nativeView = renderer?.NativeView;

            var rect = nativeView?.Superview.ConvertPointToView(nativeView.Frame.Location, null);
            var oldResult= new System.Drawing.PointF((float)Math.Round(rect?.X ?? nfloat.NegativeInfinity), (float)Math.Round(rect?.Y ?? nfloat.NegativeInfinity));

            var scale = (float)UIScreen.MainScreen.Scale;

            var newResult = new System.Drawing.PointF(oldResult.X*scale, oldResult.Y*scale);

            return newResult;
        }
    }

Sangeetha Raju [Syncfusion]
Replied On September 19, 2019 12:53 PM UTC

Hi Bernard, 
 
The snippets shared by Radoslaw will serve your purpose. But based on your request of sample from us, we will try to provide simple sample related to your requirement on or before September 24, 2019. We will appreciate your patience until then. 
 
Regards, 
Sangeetha Raju. 


CONFIRMATION

This post will be permanently deleted. Are you sure you want to continue?

Sorry, An error occured while processing your request. Please try again later.

Please sign in to access our forum

This page will automatically be redirected to the sign-in page in 10 seconds.

Warning Icon You are using an outdated version of Internet Explorer that may not display all features of this and other websites. Upgrade to Internet Explorer 8 or newer for a better experience.Close Icon

Live Chat Icon For mobile
Live Chat Icon