Привязка множественного выбора UWP ListView с моделью MVVM

Я пытаюсь реализовать представление списка с множественным выбором в UWP, оставаясь как можно ближе к модели MVVM. Моя проблема в том, что я не могу получить выбранные элементы в модели представления через привязку.

Глядя на другие ответы на SO, я обнаружил, что единственный возможный способ добиться этого — привязать через поля Command и CommandParameter моего ListView. Ответы, однако, обычно либо были сосредоточены на простом подходе с выделенным кодом, либо были сделаны с помощью WPF, что приводило к тому, что я застревал в реализации команды.

Короткое примечание перед моим MWE: программа принимает файл .pdf в качестве входных данных и отображает его, конвертируя каждую страницу в BitmapImage. Я хочу выбрать эти отдельные страницы (BitmapImages) и выполнить действие над всеми выбранными элементами (в данном случае разные действия; мой MWE, однако, включает только одну кнопку). Я пытаюсь реализовать представление списка с множественным выбором в UWP, оставаясь как можно ближе к модели MVVM. Моя проблема в том, что я не могу получить выбранные элементы в модели представления через привязку.

Это мой MWE:

Модель

 public class PdfPageModel
    {
        public string Title { get; set; }
        public PdfDocument PdfDoc { get; set; }

        public PdfPageModel(string Title, PdfDocument pdfdoc)
        {
            this.Title = Title;
            this.PdfDoc = pdfdoc;
        }

Вид

<ListView
                x:Name = "PdfPageViewer"
                CanReorderItems = "True" AllowDrop = "True" CanDragItems = "True"
                ItemsSource = "{x:Bind ViewModel.PdfPages}"
                IsItemClickEnabled = "True"
                SelectionMode = "Multiple"
                
                IsMultiSelectCheckBoxEnabled = "False"
                ScrollViewer.HorizontalScrollBarVisibility = "Auto"
                ScrollViewer.HorizontalScrollMode = "Enabled"
                ScrollViewer.IsHorizontalRailEnabled = "True"
                ScrollViewer.ZoomMode = "Enabled"
                IsZoomedInView = "False"
                >
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <Image Source = "{Binding }"/>
                    </DataTemplate>
                </ListView.ItemTemplate>

                <ListView.ItemsPanel>
                    <ItemsPanelTemplate>
                        <StackPanel
                            Orientation = "Horizontal">
                        </StackPanel>
                    </ItemsPanelTemplate>
                </ListView.ItemsPanel>
            </ListView>
<Button
                CommandParameter = "{Binding SelectedItems, Mode=OneWay, ElementName=PdfPageViewer}"
                Command = "{x:Bind ViewModel.SelectedPagesCommand}"
                />

ViewModel

public ObservableCollection<BitmapImage> PdfPages { get; set; }

private ICommand _selectedPagesCommand;
public ICommand SelectedPagesCommand
{
    get
    {
        if (_selectedPagesCommand == null)
        {
                _selectedPagesCommand = new RelayCommand<?>(async () =>
                {
                    // ?? 
                });
        }
        return _selectedPagesCommand;
    }
}

Я думаю, что это должно быть что-то вроде: RelayCommand<SelectedListViewItemCollection>(async arg => { // do something with arg });, потому что SelectedItems имеет тип: SelectedListViewItemCollection

Jeroen van Langen 01.10.2022 01:05

@JeroenvanLangen, к сожалению, я получаю ошибку CS0246: The type or namespace name 'SelectedListViewItemCollection' could not be found. Согласно документам кажется, что этот тип связан с Windows Forms

Emkay 01.10.2022 09:53
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
2
93
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Наконец-то я узнал, как этого добиться. Для тех, кто любопытен или также наткнулся на эту проблему, вот что я сделал.

Я нашел эту ветку, которая в значительной степени решила то, что я пытался сделать.

ОП реализовал преобразователь (см. ниже), чтобы получить все выбранные элементы из списка в виде элемента ListView. Из документов я узнал, что возвращаемый тип свойства SelectedItems — это List интерфейс IList<>, который я реализовал в своем SelectedPagesCommand.

Затем они использовали этот преобразователь в командном вызове. (Обратите внимание, что вызов SelectedItems был удален, так как он больше не нужен из-за использования конвертера. Это необходимый и важный шаг.) Благодаря этому я наконец смог получить выбранные элементы в списке. Возвращаемые элементы были типа System.__ComObject, а не того, что ожидалось. Однако это легко обойти; просто приведите тип к тому, каким он должен быть (в моем случае это был простой BitmapImage).

Конвертер

public class ListViewSelectedItemsConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, string language)
    {
        var listView = value as ListView;
        return listView.SelectedItems;
    }

    public object ConvertBack(object value, Type targetType, object parameter, string language)
    {
        throw new NotImplementedException();
    }
}

Вид

<Page.Resources>
        <helper:ListViewSelectedItemsConverter x:Key = "ListViewSelectedItemsConverter"/>
    </Page.Resources>
<!-- ... -->

<ListView
                x:Name = "PdfPageViewer"
                CanReorderItems = "True" AllowDrop = "True" CanDragItems = "True"
                ItemsSource = "{x:Bind ViewModel.PdfPages}"
                IsItemClickEnabled = "True"
                SelectionMode = "Multiple"
                
                IsMultiSelectCheckBoxEnabled = "False"
                ScrollViewer.HorizontalScrollBarVisibility = "Auto"
                ScrollViewer.HorizontalScrollMode = "Enabled"
                ScrollViewer.IsHorizontalRailEnabled = "True"
                ScrollViewer.ZoomMode = "Enabled"
                IsZoomedInView = "False"
                >
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <Image Source = "{Binding }"/>
                    </DataTemplate>
                </ListView.ItemTemplate>

                <ListView.ItemsPanel>
                    <ItemsPanelTemplate>
                        <StackPanel
                            Orientation = "Horizontal">
                        </StackPanel>
                    </ItemsPanelTemplate>
                </ListView.ItemsPanel>
            </ListView>

<Button
                CommandParameter = "{Binding ElementName=PdfPageViewer, Converter = {StaticResource ListViewSelectedItemsConverter}}"
                Command = "{x:Bind ViewModel.SelectedPagesCommand}"
                />

ViewModel

public ObservableCollection<BitmapImage> PdfPages { get; set; }

private ObservableCollection<BitmapImage> _selectedPdfPages;
public ObservableCollection<BitmapImage> SelectedPdfPages { get; set; }

private ICommand _selectedPagesCommand;
public ICommand SelectedPagesCommand
{
    get
    {
        if (_selectedPagesCommand == null)
        {
           _selectedPagesCommand = new RelayCommand<IList<object>>(async param =>
           {
              foreach (var i in param)
               {
                   var img = i as BitmapImage;
                   SelectedPdfPages.Add(img);
                }
             }
          }
     }
}

Другие вопросы по теме