WindowsCommunityToolkit icon indicating copy to clipboard operation
WindowsCommunityToolkit copied to clipboard

Add ListView extension method to get the index for the dropped item

Open mikegoatly opened this issue 1 year ago • 1 comments

Describe the problem

When dragging and dropping from one ListView to another you sometimes need to know the index of the dropped item so you can update a model accordingly. DragEventArgs doesn't expose this information, so it has to be manually calculated by the position of the drop and the current scroll container state.

Describe the solution

Provide an extension method (a convenience wrapper over this Stack Overflow solution) that allows you to calculate the drop index by simply calling myListView.GetDropIndex(e):

public static class ListViewExtensions
{ 
    public static int GetDropIndex(this ListView listView, DragEventArgs e)
    {
        var scrollViewer = VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(listView, 0), 0) as ScrollViewer;
        var position = e.GetPosition(listView);
        var positionY = scrollViewer.VerticalOffset + position.Y;
        return GetItemIndex(positionY, listView);
    }

    private static int GetItemIndex(double positionY, ListView targetListView)
    {
        var index = 0;
        double height = 0;

        foreach (var item in targetListView.Items)
        {
            height += GetRowHeight(item, targetListView);
            if (height > positionY)
            {
                return index;
            }

            index++;
        }

        return index;
    }

    private static double GetRowHeight(object listItem, ListView targetListView)
    {
        var listItemContainer = targetListView.ContainerFromItem(listItem) as ListViewItem;
        var height = listItemContainer.ActualHeight;
        var marginTop = listItemContainer.Margin.Top;
        return marginTop + height;
    }
}

Example usage:

private void MyListOnDrop(object sender, DragEventArgs e)
{
  if (!(sender is ListView listView))
  {
      return;
  }

  var insertIndex = listView.GetDropIndex(e);
  // ...
}

Alternatives

I've previously derived a new DragDropListView that wrapped all this logic up and exposed a DroppedAtIndex property as well, but the extension method is much cleaner because it allows you to work with an out-of-the-box ListView.

Additional info

No response

Help us help you

Yes, but only if others can assist.

mikegoatly avatar Oct 27 '22 07:10 mikegoatly