0

I'm trying to implement a multi-selection listview in UWP while staying as close as possible with the MVVM model. My problem is that I'm unable to get the selected items in the viewmodel via binding.

Looking at other answers on SO, I found out that the only possible way to achieve this is by binding via the Command and CommandParameter fields of my ListView. The answers, however, usually either focused on a simple code-behind approach or were made with WPF, which led to me being stuck at implementing the command.

A short note before my MWE: The program takes a .pdf file as input and displays it by converting every page into a BitmapImage. What I want is to select these single pages (BitmapImages) and perform an action on all selected items (in this case different actions; my MWE, however, only includes a single button). I'm trying to implement a multi-selection listview in UWP while staying as close as possible with the MVVM model. My problem is that I'm unable to get the selected items in the viewmodel via binding.

This is my MWE:

Model

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

View

<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;
    }
}
2
  • I think it should be something like: RelayCommand<SelectedListViewItemCollection>(async arg => { // do something with arg }); because the SelectedItems is of type: SelectedListViewItemCollection Commented Sep 30, 2022 at 23:05
  • @JeroenvanLangen sadly, I get CS0246 error: The type or namespace name 'SelectedListViewItemCollection' could not be found. As per the docs, it seems this type is related to Windows Forms Commented Oct 1, 2022 at 7:53

1 Answer 1

1

I finally found out how to achieve this. For anyone curious, or also stumbling on this problem, here's what I did.

I found this thread, which pretty much solved what I tried to do.

The OP implemented a converter (see below) to get all the selected items from the list as a ListView element. From the docs, I learned that the returned type of the SelectedItems property is the List Interface IList<>, which I implemented in my SelectedPagesCommand.

Then, they used this converter in the command call. (Note here that the SelectedItems call was removed, as it isn't needed anymore due to using the converter. This is a necessary and important step.) With this, I was finally able to get the selected elements in a list. The returned elements were of type System.__ComObject, instead of whatever was expected. This is easy to circumvene, though; simply cast the type to what it should be (in my case, it was a simple BitmapImage).

Converter

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

View

<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);
                }
             }
          }
     }
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.