Windows Store App drag and drop between ListViews

我是研究僧i 提交于 2019-12-06 07:50:52
Riadh Ben Hassine

I found a solution. I developed an Info Class to recuperate the index of where I dropped the new item.

public  class Info
{
    public int index { get; set; }
    public string color { get; set; }
}

I then defined my observable:

ObservableCollection<Info> c = new ObservableCollection<Info>();
c.Add(new Info { color = "#d9202b", index = 0 }); c.Add(new Info { color = "#ffffff", index = 1 }); 
c.Add(new Info { color = "#15c23c", index = 2 }); c.Add(new Info { color = "#c29b8f", index = 3 });
c.Add(new Info { color = "#0000ff", index = 4 }); c.Add(new Info { color = "#deba83", index = 5 });

I also defined another collection(c2) the same way. For this senario I will drag an item from second collection(c2) and i will drop it in the first one(c) So for the dragstarted I used this:

private void x2_DragItemsStarting(object sender, DragItemsStartingEventArgs e)
{
    strin = e.Items.FirstOrDefault() as Info;

    e.Data.Properties.Add("item", strin);
    e.Data.Properties.Add("gridSource", sender);
}

To recuperate information about the place where I dropped my item, it should be dropped over an item in the first list, so I used this:

private void x_Drop(object sender, DragEventArgs e)
{
    object gridSource;
    e.Data.Properties.TryGetValue("gridSource", out gridSource);
    if (gridSource == sender)
        return;
    object sourceItem;
    e.Data.Properties.TryGetValue("item", out sourceItem);
    //recuperate Info about place of dropped item
    Info p = ((FrameworkElement)e.OriginalSource).DataContext as Info;

    if(p==null)
    {
        //its not dropped over an item, lets add it in the end of the collection
        c2.Remove(sourceItem as Info);
        c.Add(sourceItem as Info);
    }
    else
    {
        //here we have information that we need
        c2.Remove(sourceItem as Info);
        c.Insert(p.index, sourceItem as Info);
        //c.Add(strin);
    }
    Reorder();
}

Then we should set index for new items in Reorder method:

private void Reorder()
{
    for (int i = 0; i < c.Count; i++)
        c[i].index = i;
    for (int i = 0; i < c2.Count; i++)
        c2[i].index = i;
 }

I found a solution that is good enough for my purposes. Essentially what I ended up doing was handling the drop event on both the ListView and the list item because it's easy to figure out the index if the drop happens on a list item. I still had to handle the drop on the ListView too though for when an item is dropped in between.

Here is the code I ended up with:

MyView.xaml

<UserControl.Resources>    
    <DataTemplate x:Key="MyItemTemplate">
        <local:MyControl AllowDrop="True" Drop="ListItem_OnDrop" />
    </DataTemplate>
</UserControl.Resources>

<Grid>
    <ListView ItemTemplate="{StaticResource MyItemTemplate}"
              CanDragItems="True" AllowDrop="True"
              DragItemsStarting="ListView_OnDragItemsStarting"
              DragOver="ListView_OnDragOver"
              DragLeave="ListView_OnDragLeave"
              Drop="ListView_OnDrop" />
</Grid>

MyView.xaml.cs

public sealed partial class MyView
{
    private readonly SolidColorBrush listViewDragOverBackgroundBrush = new SolidColorBrush(Color.FromArgb(255, 247, 247, 247));

    public MyView()
    {
        InitializeComponent();
    }

    private IMyViewModel ViewModel
    {
        get { return DataContext as IMyViewModel; }
    }

    private void ListView_OnDragItemsStarting(object sender, DragItemsStartingEventArgs e)
    {
        e.Data.Properties.Add("dataItem", e.Items[0] as IMyItemViewModel);
    }

    private void ListView_OnDragOver(object sender, DragEventArgs e)
    {
        var dropTarget = sender as ListView;
        if (dropTarget == null)
        {
            return;
        }

        dropTarget.Background = listViewDragOverBackgroundBrush;
    }

    private void ListView_OnDragLeave(object sender, DragEventArgs e)
    {
        var dropTarget = sender as ListView;
        if (dropTarget == null)
        {
            return;
        }

        dropTarget.Background = null;
    }

    private void ListView_OnDrop(object sender, DragEventArgs e)
    {
        var draggedItem = e.Data.Properties["dataItem"] as IMyItemViewModel;
        var targetListView = sender as ListView;

        if (targetListView == null || draggedItem == null)
        {
            return;
        }

        targetListView.Background = null;

        var droppedPosition = e.GetPosition(targetListView);
        var itemsSource = targetListView.ItemsSource as IList;
        const double extraHeightThatImNotSureWhereItCameFrom = 8d;
        var highWaterMark = 3d;  // This list starts with 3px of padding
        var dropIndex = 0;
        var foundDropLocation = false;

        for (int i = 0; i < itemsSource.Count && !foundDropLocation; i++)
        {
            var itemContainer = (ListViewItem)targetListView.ContainerFromIndex(i);

            highWaterMark = highWaterMark + itemContainer.ActualHeight - extraHeightThatImNotSureWhereItCameFrom;

            if (droppedPosition.Y <= highWaterMark)
            {
                dropIndex = i;
                foundDropLocation = true;
            }
        }

        if (foundDropLocation)
        {
            // Handle the drag/drop at a specific location
            // DropPosition is an enumeration I made that has Before & After
            ViewModel.CompleteDragDrop(draggedItem, DropPosition.Before, dropIndex);
        }
        else
        {
            // Add to the end of the list. Also works for an empty list.
            ViewModel.CompleteEvidenceDragDrop(draggedItem, DropPosition.After, itemsSource.Count - 1);
        }
    }

    private void ListItem_OnDrop(object sender, DragEventArgs e)
    {
        e.Handled = true;

        var draggedItem = e.Data.Properties["dataItem"] as IMyItemViewModel;
        var dropTarget = sender as MyControl;

        if (dropTarget == null || draggedItem == null)
        {
            return;
        }

        var parentList = dropTarget.Closest<ListView>();
        var dropPosition = dropTarget.GetDropPosition(e);

        parentList.Background = null;

        ViewModel.CompleteDragDrop(draggedItem, dropPosition, dropTarget.DataContext as IMyItemViewModel);
    }
}

ExtensionMethods

public static class ExtensionMethods
{
    public static T Closest<T>(this DependencyObject obj) where T : DependencyObject
    {
        if (obj == null)
        {
            return null;
        }

        while (true)
        {
            var parent = VisualTreeHelper.GetParent(obj);

            if (parent == null)
            {
                return null;
            }

            if (parent.GetType() == typeof(T))
            {
                return (T)parent;
            }

            obj = parent;
        }
    }

    public static DropPosition GetDropPosition(this FrameworkElement dropTarget, DragEventArgs e)
    {
        var positionRelativeToTarget = e.GetPosition(dropTarget);

        var dropBefore = positionRelativeToTarget.Y < (dropTarget.ActualHeight / 2);

        return dropBefore ? DropPosition.Before : DropPosition.After;
    }
}

If you use a DataContext as part of your list population process, then you can simply do the following:

private void x_Drop(object sender, DragEventArgs e)
{
     MyDataModel model = (sender as FrameworkElement).DataContext as MyDataModel;
     // ...

I'm doing this to get the target listviewItem on the drag over event. It should work for both list and grid views

private YOURITEMCLASS _dragTarget;

private void ItemListView_OnDragOver(object sender, DragEventArgs e)
{
   var pos = e.GetPosition(this);
   // Offset position by left and top borders if in split view control

   var elements = VisualTreeHelper.FindElementsInHostCoordinates(pos, this);
   foreach (var element in elements)
   {
       var cellItem = element as ContentControl;
       var item = cellItem?.Content as YOURITEMCLASS;
       if (item == null) continue;
       _dragTarget = item;
       break;
   }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!