UWP 在 Xbox 上重新排序 Gridviewitems

UWP Reorder Gridviewitems on Xbox

我正在制作一个 UWP 应用程序,该应用程序目前应该在 xbox 上运行,并且将来可能会在 PC 和其他平台上发布。我知道在 PC 和移动设备上,我们可以通过 GridViewListView.

上的以下 2 个属性启用此功能
CanReorderItems=True
CanDrop=True

但是根据 Microsoft Docs,拖放功能是 not available or supported on xbox

那么在 xbox 上实现此重新排序功能还有哪些其他选项 GridView

更新 1

So here is my backend code for the gridview. selection mode is single but I am not using selectionchanged event because that just creates lot of confusion and for now just assume that we always need to swap the items I will set the boolean later once the swapping in working perfectly.

private void SamplePickerGridView_ChoosingItemContainer(Windows.UI.Xaml.Controls.ListViewBase sender, ChoosingItemContainerEventArgs args)
    {
        if (args.ItemContainer != null)
        {
            return;
        }
        GridViewItem container = (GridViewItem)args.ItemContainer ?? new GridViewItem();
        //should be xbox actually after pc testing
        if (DeviceTypeHelper.GetDeviceFormFactorType() == DeviceFormFactorType.Desktop)
        {
            container.GotFocus += Container_GotFocus;
            container.LostFocus += Container_LostFocus;
            //container.KeyDown += Container_KeyDown;
        }
        args.ItemContainer = container;
    }
    private TVShow GotItem, LostItem;
    private void Container_LostFocus(object sender, RoutedEventArgs e)
    {

        LostItem = OnNowAllGridView.ItemFromContainer(e.OriginalSource as GridViewItem) as TVShow;
        GotItem = null;

    }

    private void Container_GotFocus(object sender, RoutedEventArgs e)
    {

        GotItem = OnNowAllGridView.ItemFromContainer(e.OriginalSource as GridViewItem) as TVShow;
        if (GotItem != null && LostItem != null)
        {
            var focusedItem = GotItem;
            var lostitem = LostItem;
            var index1 = ViewModel.Source.IndexOf(focusedItem);
            var index2 = ViewModel.Source.IndexOf(lostitem);
            ViewModel.Source.Move(index1, index2);
        }
        LostItem = null;

    }

u can try the code with adaptivegridview or just normal gridview of uwp if it works with that it should work with adaptivegridview as well.

Current Bheaviour 项已交换,但焦点保持在同一索引处。

预期 焦点也应随项目移动。

您的发现是正确的,开箱即用的 Xbox 不支持拖放(尽管将来 Xbox 支持鼠标时,我想它会起作用)。

因此,如果您需要此功能,则必须从一开始就手动实施。一种选择是添加一个按钮,该按钮将仅在 Xbox 上显示并且显示为 Reorder Grid.

启用此 "reorder" 模式后,您可以使用多种解决方案。

对您来说最简单的解决方案 是将 SelectionMode 设置为 Single,当一个项目被 select 编辑时,您会把它带到基础 collection.

的起点
collection.Remove( selectedItem );
collection.Insert( 0, selectedItem );

置于前面 解决方案已在 Xbox One 仪表板上实现,用于重新排序图块。

第二个选项 是将 SelectionMode 设置为 Multiple,其中用户将首先 select 一项,然后是第二项.之后,您可以将第一个 selected 项目移动到第二个 selected:

之前
collection.Remove( firstSelectedItem );
var targetIndex = collection.IndexOf( secondSelectedItem );
collection.Insert( targetIndex, firstSelectedItem );

最后一个解是最复杂的。使用 SelectionMode = Single,您将 select 单个项目,然后观察用户焦点移动的方向并移动图块 "in real time"。这是最用户友好的,但最难可靠地实施。

正如第三个解决方案的概述 - 如果您实现 GridView:

的自定义模板,您可以捕获 GotFocus 事件
<GridView.ItemsPanel>
    <ItemsPanelTemplate>
        <ItemsWrapGrid Orientation="Horizontal" 
                       GotFocus="GridViewItem_GotFocus"/>
    </ItemsPanelTemplate>
</GridView.ItemsPanel>

现在,在此 GotFocus 处理程序中,您可以检索当前从 EventArgs.OriginalSource 获得焦点的项目。这样您就可以知道哪个项目获得了焦点,并且可以将其与用户 select编辑的项目交换。

更新 - 骇人听闻的解决方案

我想出了一个解决 GotFocus/LostFocus 混乱的 hacky 方法。

GotFocus 的问题在于,当我们移动 collection 中的项目时,焦点会变得混乱。但是如果我们根本不移动物品会怎样

假设您的项目类型是 TVShow。让我们围绕这种类型创建一个包装器:

public class TVShowContainer : INotifyPropertyChanged
{
    private TVShow _tvShow;

    public TVShow TvShow
    {
        get => _tvShow;
        set
        {
            _tvShow = value; 
            OnPropertyChanged();
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

现在将 collection 项目类型更改为这种新的 "wrapper" 类型。当然,您还必须更新 GridView DataTemplate 以获得正确的引用。您现在需要使用 "{Binding TvShow.Property}" 而不是 "{Binding Property}",或者您可以将 DataContext="{Binding TvShow}" 属性设置为 DataTemplate.

中的根元素

但是你现在可能明白我要处理这个问题了。目前您正在使用 Move 方法移动 collection 中的项目。让我们用交换替换它:

var item1 = focusedItem.TvShow;
focusedItem.TvShow = LostItem.TvShow;
LostItem.TvShow = item1;

这是一个很大的不同,因为我们不再更改 collection 本身,而只是将引用移动到包装在 "static" 容器中的项目。由于绑定,项目将正确显示在它们应该显示的位置。

这仍然是一个 hacky 解决方案,因为它要求您包装您的物品只是为了重新排序,但它至少有效。然而,我仍然有兴趣找到一种更好的方法来做到这一点。