winRT拖放,交换两个项目而不是插入

winRT drag and drop, swap two items instead of inserting

我是 WPF 的老用户,但对 WinRT 还是个新手。我想知道是否有一种内置的方式或简单的方式来将交换功能集成到容器中,以便交换交换容器中的两个项目。所需的行为是拖动一个项目并将其放在另一个项目上,并让被拖动的项目和被拖动到的项目交换它们在容器中的位置。

示例我有一个包含 1 2 3 4 5 6 7 8 的列表,如果我拖动 7 "on" 4 我想交换两个项目,以便结果列表变为 1 2 3 7 5 6 4 8

我目前正在使用 GridViewItemsWrapGrid 作为显示大量图片缩略图的容器。我需要能够对它们进行重新排序,最常需要的操作是交换两个图像的位置。

或者如果没有内置方式,您能否提示我在 WinRT 中从头开始做的 "proper" 方向是什么?我正在考虑不在容器处而是在项目级别处理拖放,并手动交换 ObservableCollection?

中的项目

最简单的方法是利用 ObservableCollection,让 ListBox 或 w/e 控件处理其余部分。

基本上,您所要做的就是创建拖放处理程序,找出客户端要移动的项目(跟踪旧索引/新索引),并实施交换:

var dragSourceItem = yourObservable[oldIndex];
var dragTargetItem = yourObservable[newIndex];
yourObservable[newIndex]=dragSourceItem;
yourObservable[oldIndex]=dragTargetItem;

ObservableCollection 将引发“替换”操作,WPF 知道如何处理。

这里有一些东西可以让你继续:http://www.hardcodet.net/2009/03/moving-data-grid-rows-using-drag-and-drop

您基本上想将其包装到附加行为中,并在 ViewModel 中实现交换。

这是我的做法(感谢这个blog):

XAML代码:

 <ListView x:Name="MyListView" CanDragItems="True" AllowDrop="True" HorizontalAlignment="Center" VerticalAlignment="Center" DragItemsStarting="MyListView_DragItemsStarting" Drop="MyListView_Drop">
        <ListView.ItemTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding}" AllowDrop="True" Drop="TextBlock_Drop" DragOver="TextBlock_DragOver"/>
            </DataTemplate>                
        </ListView.ItemTemplate>
    </ListView>

C#代码:

ObservableCollection<string> MyList = new ObservableCollection<string>();
string DraggedString;
TextBlock DraggedOverTextBlock;

protected override void OnNavigatedTo(NavigationEventArgs e)
    {
        MyList.Add("1");
        MyList.Add("2");
        MyList.Add("3");
        MyList.Add("4");
        MyList.Add("5");
        MyList.Add("6");
        MyList.Add("7");
        MyList.Add("8");
        MyList.Add("9");
        MyList.Add("10");
        MyListView.ItemsSource = MyList;
    }

    private void MyListView_DragItemsStarting(object sender, DragItemsStartingEventArgs e)
    {
        DraggedString = e.Items[0] as String;
    }

    private void MyListView_Drop(object sender, DragEventArgs e)
    {
        if (DraggedString == null || DraggedOverTextBlock == null) return;
        var indexes = new List<int> { MyList.IndexOf(DraggedString), MyList.IndexOf(DraggedOverTextBlock.Text) };
        if (indexes[0] == indexes[1]) return;
        indexes.Sort();
        var values = new List<string> { MyList[indexes[0]], MyList[indexes[1]] };
        MyList.RemoveAt(indexes[1]);
        MyList.RemoveAt(indexes[0]);
        MyList.Insert(indexes[0], values[1]);
        MyList.Insert(indexes[1], values[0]);
        DraggedString = null;
        DraggedOverTextBlock = null; 
    }

    private void TextBlock_DragOver(object sender, DragEventArgs e)
    {
        DraggedOverTextBlock = sender as TextBlock;
    }

两个现有答案都将在数据级别为您进行交换。以下是使 UI 更多 user-friendly.

的方法

恕我直言,处理交换的最佳用户体验是,当您拖动一个项目并将其移动到另一个项目上时,后者应该出现到被拖动项目最初所在的位置。这清楚地告诉用户物品的确切去向。就像下面的gif图所示。

为此,您需要创建一个 Image 并使用 RenderTargetBitmapdrop item 的外观复制到其来源,当拖动项目 移动到放置项目 上。当然,当拖动动作开始时,您需要获取 拖动项 的位置,以便您知道该图像的确切放置位置。

然后,一旦物品掉落,您应该清除并隐藏图像并进行数据交换。

private void GridView_DragItemsStarting(object sender, DragItemsStartingEventArgs e)
{
    // get item container, i.e. GridViewItem
    var itemContainer = (GridViewItem)this.MyGridView.ContainerFromItem(e.Items[0]);

    // get drag item index from its item container
    _dragItemIndex = this.MyGridView.IndexFromContainer(itemContainer);

    // get drag item position
    var position = itemContainer.GetRelativePosition(this.LayoutRoot);

    // set the width and height of the image
    this.DropItemImage.Width = itemContainer.ActualWidth;
    this.DropItemImage.Height = itemContainer.ActualHeight;

    // move the image to this location
    this.DropItemImage.RenderTransformOrigin = new Point(0, 0);
    this.DropItemImage.RenderTransform.Animate(null, position.X, "TranslateX", 0, 0);
    this.DropItemImage.RenderTransform.Animate(null, position.Y, "TranslateY", 0, 0);
}

private void GridView_Drop(object sender, DragEventArgs e)
{
    // first we need to reset the image
    this.DropItemImage.Source = null;

    // get the drop & drop items
    var dragItem = _groups[_dragItemIndex];
    var dropItem = _groups[_dropItemIndex];

    // then we swap their positions
    _groups.RemoveAt(_dragItemIndex);
    _groups.Insert(_dragItemIndex, dropItem);
    _groups.RemoveAt(_dropItemIndex);
    _groups.Insert(_dropItemIndex, dragItem);
}

private object _previous;
private async void ItemRoot_DragOver(object sender, DragEventArgs e)
{
    // first we get the DataContext from the drop item in order to retrieve its container
    var vm = ((Grid)sender).DataContext;

    // get the item container
    var itemContainer = (GridViewItem)this.MyGridView.ContainerFromItem(vm);

    // this is just to stop the following code to be called multiple times druing a DragOver
    if (_previous != null && _previous == itemContainer)
    {
        return;
    }
    _previous = itemContainer;

    // get drop item index from its item container
    _dropItemIndex = this.MyGridView.IndexFromContainer(itemContainer);

    // copy the look of the drop item to an image
    var bitmap = new RenderTargetBitmap();
    await bitmap.RenderAsync(itemContainer);
    this.DropItemImage.Source = bitmap;

    // animate the image to make its appearing more interesting
    this.DropItemImage.Animate(0, 0.4, "Opacity", 200, 0);
    this.DropItemImage.RenderTransformOrigin = new Point(0.5, 0.5);
    this.DropItemImage.RenderTransform.Animate(0.8, 1, "ScaleX", 200, 0, new ExponentialEase { EasingMode = EasingMode.EaseIn });
    this.DropItemImage.RenderTransform.Animate(0.8, 1, "ScaleY", 200, 0, new ExponentialEase { EasingMode = EasingMode.EaseIn });
}

我提供了一个小示例项目 here 以便您可以查看动画是如何完成的。请注意,数据交换部分不包括在内,正如我所说,其他答案已经很好地解释了。 :)