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

时间:2015-01-02 11:40:01

标签: c# windows-runtime winrt-xaml

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

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

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

或者,如果没有内置方式,你能不能告诉我从头开始做的“正确”方向是WinRT?我正在考虑处理拖放而不是容器但是在项目级别,并手动交换ItemsWrapGrid中的项目?

3 个答案:

答案 0 :(得分:3)

现有的答案都会在数据级别为您进行交换。以下是可以使UI更加用户友好的方法。

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

drag, drop & swap

要执行此操作,您需要创建Image并使用RenderTargetBitmap放置项的外观复制到其来源,当拖动项时移动放置项。当然,当拖动动作开始时,您需要获取拖动项的位置,以便知道将图像放在何处。

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

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,因此您可以查看动画是如何完成的。请注意,数据交换部分不包括在内,正如我所说,其他答案已经很好地解释了。 :)

答案 1 :(得分:1)

最简单的方法是利用ObservableCollection,让ListBox或w / e控件来处理剩下的事情。

基本上,你所要做的就是制造阻力& drop handler,找出客户想要移动到哪个项目(跟踪oldIndex / newIndex),并实现交换:

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中实现交换。

答案 2 :(得分:1)

以下是我的表现(感谢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;
    }