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

C# winRT拖放,交换两个项目,而不是插入,c#,windows-runtime,winrt-xaml,C#,Windows Runtime,Winrt Xaml,我是WPF的长期用户,但对WinRT来说是新手。我想知道是否有一种内置的方式或简单的方式来集成容器中的交换功能,以便交换容器中的两个项目。所需的行为是将一个项目拖放到另一个项目上,并使被拖动的项目及其拖动到的项目在容器中的位置发生交换) 例如,我有一个包含12345678的列表,如果我拖动7“on”4,我希望两个项目交换,以便生成的列表成为12345648 我目前正在使用GridView和ItemsWrapGrid作为显示大量图片缩略图的容器。我需要能够重新排序它们,最常见的操作是在两个图像的位

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

例如,我有一个包含12345678的列表,如果我拖动7“on”4,我希望两个项目交换,以便生成的列表成为12345648

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


或者,如果没有内置的方式,你能告诉我在WinRT中从头开始做这件事的“正确”方向是什么吗?我想不是在容器而是在项目级别处理拖放操作,并手动交换
ObservableCollection

中的项目。最简单的方法是利用
ObservableCollection
,让
列表框
或w/e控件处理其余部分

基本上,您所要做的就是创建拖放处理程序,找出客户机希望将哪个项目移动到哪里(跟踪oldIndex/newIndex),并实现交换:

var dragSourceItem = yourObservable[oldIndex];
var dragTargetItem = yourObservable[newIndex];
yourObservable[newIndex]=dragSourceItem;
yourObservable[oldIndex]=dragTargetItem;
observedcollection
将引发“替换”操作,WPF知道如何处理

这里有一些东西可以让你行动起来:

您基本上希望将其包装到附加的行为中,并在
ViewModel

中实现交换。以下是我的操作方法(多亏了这一点):

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 MyList=新的ObservableCollection();
线牵引;
TextBlock DRAGEDOVERTEXTBLOCK;
受保护的覆盖无效OnNavigatedTo(NavigationEventArgs e)
{
MyList.添加(“1”);
MyList.添加(“2”);
MyList.添加(“3”);
MyList.添加(“4”);
MyList.添加(“5”);
MyList.添加(“6”);
MyList.添加(“7”);
MyList.添加(“8”);
MyList.添加(“9”);
MyList.添加(“10”);
MyListView.ItemsSource=MyList;
}
私有void MyListView\u DragItemsStarting(对象发送方,DragItemsStartingEventArgs e)
{
DraggedString=e.Items[0]作为字符串;
}
私有void MyListView_Drop(对象发送方,DragEventArgs e)
{
if(DraggedString==null | | DraggedOverTextBlock==null)返回;
var index=新列表{MyList.IndexOf(DraggedString),MyList.IndexOf(DraggedOverTextBlock.Text)};
如果(索引[0]==索引[1])返回;
index.Sort();
var values=新列表{MyList[indexes[0]],MyList[indexes[1]};
MyList.RemoveAt(索引[1]);
MyList.RemoveAt(索引[0]);
插入(索引[0],值[1]);
插入(索引[1],值[0]);
DraggedString=null;
DraggedOverTextBlock=null;
}
私有void TextBlock_DragOver(对象发送方,DragEventArgs e)
{
DRAGEDOVERTEXTBLOCK=发送方为文本块;
}

两个现有答案将在数据级别为您进行交换。下面是可以做些什么来让UI更加用户友好

依我看,处理交换的最佳用户体验是,当您拖动一个项目并将其移动到另一个项目上时,后者应该显示在拖动的项目最初所在的位置。这会清楚地告诉用户项目的确切位置。就像下面的gif图像所示

为此,您需要创建一个
图像
,并在拖放项移动到拖放项上时,使用
渲染目标位图
拖放项的外观复制到其源位置。当然,当拖动操作开始时,您需要获得拖动项的位置,以便知道将此图像放置在何处

然后,一旦项目被删除,您应该清除和隐藏图像并进行数据交换

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 });
}

我已经包括了一个小样本项目,这样你就可以检查动画是如何完成的。请注意,数据交换部分不包括在内,正如我所说,其他答案已经很好地解释了这一点。:)

我使用的不是WPF而是winrt,我知道如何更改observable中的数据,问题是如何正确实现拖放交换本身(而不是如何更新基础数据)。我是否应该在项目级别执行此操作,我是否应该取消此事件,以便它不会尝试交换自身等等@RonanThibaudau:WPF或no WinRT,这无关紧要-一个项目的功能更少,并且需要额外的黑客攻击来弥补缺少的功能(有时)。主要的哲学仍然是一样的。在WPF社区中,我们使用gongdrop(MVVM)——我建议您研究问题是如何解决的,并使其适应您的用例。它支持元素的重新排序。您基于的博客文章与Silverlight相关,我正在寻找最好的WinRT解决方案(不是wpf或Silverlight),因此如果有更好的WinRT解决方案,我现在不打算接受这一点。这只是在集合中手动使用常规的拖放和交换项,我知道如何操作集合我的问题是关于实现WinRT部分的正确方法(我是否最好坚持拖放并滥用它,我是否应该实现其他功能,如果我使用拖放,是否有办法使其与过渡等配合良好)这篇博文是Silverlight,但代码严格来说是WinRT。在Silverlight、WPF和WinRT中使用ObservableCollection,因此交换项目顺序的代码是相同的。没有内置的重新排序功能,这是您自己的。来源:我是Microsoft工程师。Oki听起来不错,适合您
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 });
}