UWP Reorder Gridviewitems на Xbox

Я делаю приложение UWP, которое сейчас должно быть на xbox, и, возможно, в будущем я выпущу его на ПК и других платформах. Я знаю, что на ПК и на мобильных устройствах мы можем включить эту функцию с помощью следующих двух свойств: GridView или ListView.

CanReorderItems=True
CanDrop=True

Но согласно Microsoft Docs, функция перетаскивания - это недоступно или не поддерживается на Xbox.

Итак, каковы еще варианты для достижения этой функции переупорядочивания на xbox GridView?

ОБНОВЛЕНИЕ 1

Итак, вот мой бэкэнд-код для gridview. режим выбора является одиночным, но я не использую событие selectionchanged, потому что это просто создает много путаницы, и пока просто предположим, что нам всегда нужно менять элементы местами. Я установлю логическое значение позже, когда обмен будет работать идеально.

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;

    }

Вы можете попробовать код с Adaptivegridview или просто с обычным gridview uwp, если он работает с этим, он также должен работать с Adaptivegridview.

Элементы Current Bheaviour меняются местами, но фокус остается в том же индексе.

Ожидается, фокус также должен перемещаться вместе с элементом.


person Muhammad Touseef    schedule 05.02.2018    source источник


Ответы (1)


Ваш вывод верен, перетаскивание не поддерживается на Xbox из коробки (хотя, когда в будущем поддержка мыши появится на Xbox, я думаю, это будет работать).

Так что, если вам нужна эта функция, вам придется с самого начала реализовать ее вручную. Один из вариантов - добавить кнопку, которая будет отображаться только на Xbox и будет выглядеть как Reorder Grid.

Когда этот режим «переупорядочивания» был включен, вам доступно несколько решений.

Самым простым решением для вас было бы установить SelectionMode на Single, и когда элемент выбран, вы переместите его в начало базовой коллекции.

collection.Remove( selectedItem );
collection.Insert( 0, selectedItem );

Это решение на передний план было реализовано на панели управления Xbox One для изменения порядка плиток.

Второй вариант - установить SelectionMode на Multiple, при этом пользователь сначала выбирает один элемент, а затем второй. После этого вы можете переместить первый выбранный элемент перед вторым выбранным:

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

Последнее решение является наиболее сложным. С SelectionMode = Single вы должны выбрать один элемент, а затем наблюдать направление, в котором перемещается фокус пользователя, и перемещать плитку «в реальном времени». Это самый удобный для пользователя, но самый трудный для надежной реализации.

Точно так же в качестве схемы третьего решения - вы можете зафиксировать событие GotFocus, если вы реализуете собственный шаблон GridView:

<GridView.ItemsPanel>
    <ItemsPanelTemplate>
        <ItemsWrapGrid Orientation="Horizontal" 
                       GotFocus="GridViewItem_GotFocus"/>
    </ItemsPanelTemplate>
</GridView.ItemsPanel>

Теперь в этом GotFocus обработчике вы можете получить элемент, который в данный момент находится в фокусе, из EventArgs.OriginalSource. Таким образом, вы могли узнать, какой элемент получил фокус, и поменять его местами на элемент, выбранный пользователем.

Обновление - хакерское решение

Я придумал хитрый подход, который решает _14 _ / _ 15_ беспорядок.

Проблема с GotFocus в том, что когда мы перемещаем элемент в коллекцию, фокус путается. Но что, если мы вообще физически не перемещали предметы?

Предположим, ваш тип элемента 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));
    }
}

Теперь измените тип элемента коллекции на этот новый тип «оболочки». Конечно, вы также должны обновить свой GridView DataTemplate, чтобы иметь правильные ссылки. Вместо "{Binding Property}" вам теперь нужно будет использовать "{Binding TvShow.Property}", или вы можете установить атрибут DataContext="{Binding TvShow}" на корневой элемент внутри DataTemplate.

Но теперь вы можете увидеть, к чему я клоню. В настоящее время вы используете метод Move для перемещения элементов в коллекции. Давайте заменим это свопом:

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

Это большая разница, потому что мы больше не меняем саму коллекцию, а просто перемещаем ссылки на элементы, заключенные в «статический» контейнер. А благодаря привязкам предметы будут правильно отображаться там, где должны.

Это все еще хакерское решение, потому что оно требует, чтобы вы обернули свои предметы только ради изменения порядка, но оно, по крайней мере, работает. Однако я все еще заинтересован в поиске лучшего способа сделать это.

person Martin Zikmund    schedule 06.02.2018
comment
Спасибо за ответ, я тоже думал сделать что-то вроде третьего варианта, я знаю, что это может быть сложно, но как мы можем это сделать? Любые идеи? - person Muhammad Touseef; 06.02.2018
comment
Я обновил свой ответ идеей, как реализовать это - person Martin Zikmund; 06.02.2018
comment
почему я должен использовать GotFocus? Я имею в виду, не будет ли полученный фокус и выбранный элемент одинаковыми? Bcz, когда выбор изменяется, элемент выбран, а также получает фокус правильно? - person Muhammad Touseef; 06.02.2018
comment
что, если я использую только событие selectionchanged в grdivew, а затем заменяю удаленный элемент выбранным элементом из аргументов? это сработает? - person Muhammad Touseef; 06.02.2018
comment
или, может быть, я могу использовать потерянный фокус вместе с полученным фокусом, а затем поменять местами эти 2 элемента? что ты посоветуешь? @Martin Zikmund - person Muhammad Touseef; 06.02.2018
comment
Проблема в том, что вам нужно различать случай, когда пользователь переходит к элементу, который он хочет переместить, и случай, когда он перемещается. Таким образом, выбор объекта, который нужно переместить, обрабатывается моим Selection, а затем само перемещение GotFocus. Конечно, это немного сложно, потому что вы должны убедиться, что не обрабатываете фокус для выбранного элемента и так далее ... - person Martin Zikmund; 06.02.2018
comment
Я заметил, что вы удалили статус решенного, возникают ли у вас проблемы? - person Martin Zikmund; 10.02.2018
comment
Я пробовал несколько способов добиться этого, но каждый способ вызывает определенные типы проблем. Я не могу использовать третий способ, предоставленный bcz. Я на самом деле использую AdaptiveGridView с помощью инструментария сообщества uwp, у которого нет установщика для ItemsPanel, но я использую событие фокуса для каждого gridviewItem через событие ChoosingItemContainer - person Muhammad Touseef; 10.02.2018
comment
Мне удалось добиться этого в некоторой степени с помощью метода Collection.Move (index, index), но фокус остается на своем исходном месте, но на самом деле я хочу, чтобы фокус перемещался вместе с элементом в его следующее место назначения, чтобы пользователь может продолжать перемещать объект до тех пор, пока он не захочет бросить его в желаемое место, а затем бросить туда, я могу легко переключать, когда его запускать, а когда заканчивать, с помощью gamepadX, используя переменную типа bool в коде позади - person Muhammad Touseef; 10.02.2018
comment
Не могли бы вы использовать вызов метода SetFocus, чтобы заставить фокус двигаться дальше? - person Martin Zikmund; 10.02.2018
comment
да, я тоже пробовал, это создает еще несколько проблем, bcz я использовал потерянный фокус и получил фокус, чтобы узнать 2 элемента, которые нужно поменять местами. поэтому он создает своего рода вложенный цикл событий получения и потери фокуса - person Muhammad Touseef; 10.02.2018
comment
bcz, по-видимому, после замены или перемещения элементов они снова автоматически вызывают события gotfocus, bcz они переделываются в xaml, я думаю? поэтому они снова загружаются и получают фокус - person Muhammad Touseef; 10.02.2018
comment
Вы можете создать логический флаг, который будет указывать на то, что потерянные события amd got focus должны игнорироваться до тех пор, пока GotFocus не будет вызван для желаемого нового местоположения фокуса. - person Martin Zikmund; 10.02.2018
comment
не могли бы вы сделать для этого небольшой образец? Поскольку я на самом деле пробовал все, что пришло мне в голову, и в моем проекте в настоящее время просто много беспорядочного кода, я попытался создать еще одно логическое значение, но тогда произошла только 1-кратная замена bcz somehwre, мне нужно снова изменить логическое значение - person Muhammad Touseef; 10.02.2018
comment
Боюсь, что в ближайшее время не доберусь до Xbox. Не могли бы вы обновить вопрос с помощью имеющегося у вас кода? Я постараюсь применить к нему свою идею - person Martin Zikmund; 10.02.2018
comment
У меня тоже нет Xbox, я тестирую приложение на своем ноутбуке с подключенным к нему контроллером Xbox, но движение фокуса также работает с обычными стрелками клавиатуры, так что вы можете проверить и этот способ, я постараюсь сделать немного более чистый код, а затем обновите им мой вопрос - person Muhammad Touseef; 10.02.2018
comment
Отличная идея! Сообщите мне, когда вопрос обновится, и я попробую - person Martin Zikmund; 10.02.2018
comment
пожалуйста, проверьте обновленный вопрос, спасибо @Martin Zikmund - person Muhammad Touseef; 10.02.2018
comment
Позвольте нам ​​продолжить это обсуждение в чате. - person Muhammad Touseef; 10.02.2018
comment
У меня просто была взломанная идея, как это решить. Теперь я дополню свой ответ этой идеей. - person Martin Zikmund; 11.02.2018
comment
Вы лично тестировали этот метод на своем ПК? - person Muhammad Touseef; 11.02.2018
comment
кстати, я уже использую наблюдаемую коллекцию, так что мне нужно снова добавить это свойство Inotify, измененное? - person Muhammad Touseef; 11.02.2018
comment
ObservableCollection уведомляет привязку об изменениях в самих элементах коллекции, но в этом случае они вообще не меняются, изменяется их содержимое. Вот почему контейнер должен также уведомлять об изменении свойств. - person Martin Zikmund; 11.02.2018
comment
Хорошо, поэтому, когда я добавляю элементы в коллекцию, я добавляю новый TVShow () и назначаю ему все свойства TVShow в моей модели просмотра, теперь в этом случае я должен добавить новый объект TVShowContainer в свою коллекцию и установить его свойство TVShow к новому объекту TVShow, верно? - person Muhammad Touseef; 11.02.2018
comment
Да, по сути, у вас будет набор контейнеров, содержащих сами телешоу. - person Martin Zikmund; 11.02.2018
comment
и что ты думаешь об аннулировании объектов? что я делаю? lostitem и gotitem Я аннулирую их обоих в конце противоположных событий для них обоих, как вы можете видеть в моем коде. Я делаю это, потому что, если фокус покидает gridview или просто приходит из чего-то другого в gridview для любого элемента, тогда он не должен пытаться сделать этот обмен, даже если логическое значение истинно, есть ли лучший способ сделать это? или это нормально? - person Muhammad Touseef; 11.02.2018
comment
Я использовал lostitem и gotitem как элементы GridViewItems и брал их как элементы TVShow, поэтому теперь я должен принять их как элементы TVShowContainer, а затем поменять местами их объекты tvshow внутри них. но мне было интересно, следует ли мне просто обнулить весь gotitem или я должен обнулить объект gotitem.TVShow? - person Muhammad Touseef; 11.02.2018
comment
Нет, обнуление не обязательно, но вы должны обнулить ссылку на контейнер, я думаю - person Martin Zikmund; 11.02.2018
comment
да, по ссылке на контейнер вы имеете в виду объекты, которые я уже обнуляю в своем коде? которые раньше были телешоу, но теперь это tvshowContainers, lostitem и gotitem - person Muhammad Touseef; 11.02.2018