Xaml 如何从GridView中获取选定项

Xaml 如何从GridView中获取选定项,xaml,uwp,uwp-xaml,Xaml,Uwp,Uwp Xaml,我正在制作一个图书馆管理软件。这是XAML: <CommandBar Grid.Row="0"> <AppBarButton Icon="Add" Label="New Book" Click="NewBook_Click"/> <AppBarButton Icon="Delete" Label="R

我正在制作一个图书馆管理软件。这是XAML:

<CommandBar Grid.Row="0">
        <AppBarButton Icon="Add" 
                      Label="New Book"
                      Click="NewBook_Click"/>

        <AppBarButton Icon="Delete"
                      Label="Remove a Book"
                      Click="DeleteBook_Click"/>
    </CommandBar>

    <GridView x:Name="AllBooks_GridView"
              ItemsSource="{x:Bind Path=ViewModel.Books, Mode=OneWay}"
              ScrollViewer.VerticalScrollBarVisibility="Visible"
              ScrollViewer.VerticalScrollMode="Enabled"
              ScrollViewer.HorizontalScrollMode="Disabled"
              Grid.Row="1"
              SelectionMode="Multiple">

        <GridView.ItemTemplate>
            <DataTemplate x:DataType="data:Book">

                <StackPanel Margin="10" HorizontalAlignment="Center">
                    <Image Width="200" Height="200" Source="{x:Bind Path=CoverImageLocation, Mode=OneWay}" />
                    <Grid HorizontalAlignment="Stretch">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition />
                            <ColumnDefinition Width="Auto"/>
                        </Grid.ColumnDefinitions>
                        <StackPanel>
                            <TextBlock FontSize="16" 
                                       Text="{x:Bind Path=Title, Mode=OneWay}" 
                                       TextTrimming="WordEllipsis"/>
                            <TextBlock FontSize="10" 
                                       Text="{x:Bind Path=Author, Mode=OneWay}" 
                                       Margin="0, 3, 0, 0" 
                                       TextTrimming="WordEllipsis"/>
                        </StackPanel>
                        <TextBlock FontSize="20" 
                                   Text="{x:Bind Path=Quantity, Mode=OneWay}"
                                   Grid.Column="1" 
                                   VerticalAlignment="Bottom"                                        
                                   Margin="20, 0, 0, 0"
                                   HorizontalAlignment="Left"/>
                    </Grid>
                </StackPanel>

            </DataTemplate>
        </GridView.ItemTemplate>

    </GridView>
这是我的应用程序的屏幕截图:


单击删除图标时,仅删除第一本书。尽管他们都被选中了。我做错了什么?

在迭代集合的项目时,您正在更改集合

AllBooks\u GridView.SelectedItems
是集合,当您在
foreach
循环中遍历它时,可以通过调用

ViewModel.Books.Remove(book);
此调用后,
GridView
将刷新以反映您对
ViewModel
所做的更改,并且
SelectedItems
集合中的项目将减少。在下一次迭代中,
SelectedItems
不再像您预期的那样包含两本书,它只包含一本。由于代码试图在下一次迭代中从集合中删除第二本书,因此没有发生任何事情,循环终止

这里有一个修正:

//Now the books collection is no longer bound to the SelecteItems of the GridView, 
//it is 'immutable' in the foreach loop
List<Book> books = new List<Book>(); 
foreach (var item in AllBooks_GridView.SelectedItems)
    books.Add(item as Book); 

foreach (var book in books)
{
   DataAccess.DeleteBook(book.Title);
   ViewModel.Books.Remove(book);
}
//现在图书集合不再绑定到GridView的SelecteItems,
//它在foreach循环中是“不可变的”
列表书籍=新列表();
foreach(AllBooks\u GridView.SelectedItems中的var项)
图书。添加(项目为图书);
foreach(账簿中的var账簿)
{
DataAccess.DeleteBook(book.Title);
ViewModel.Books.Remove(书本);
}

在遍历集合项时,您正在更改集合

AllBooks\u GridView.SelectedItems
是集合,当您在
foreach
循环中遍历它时,可以通过调用

ViewModel.Books.Remove(book);
此调用后,
GridView
将刷新以反映您对
ViewModel
所做的更改,并且
SelectedItems
集合中的项目将减少。在下一次迭代中,
SelectedItems
不再像您预期的那样包含两本书,它只包含一本。由于代码试图在下一次迭代中从集合中删除第二本书,因此没有发生任何事情,循环终止

这里有一个修正:

//Now the books collection is no longer bound to the SelecteItems of the GridView, 
//it is 'immutable' in the foreach loop
List<Book> books = new List<Book>(); 
foreach (var item in AllBooks_GridView.SelectedItems)
    books.Add(item as Book); 

foreach (var book in books)
{
   DataAccess.DeleteBook(book.Title);
   ViewModel.Books.Remove(book);
}
//现在图书集合不再绑定到GridView的SelecteItems,
//它在foreach循环中是“不可变的”
列表书籍=新列表();
foreach(AllBooks\u GridView.SelectedItems中的var项)
图书。添加(项目为图书);
foreach(账簿中的var账簿)
{
DataAccess.DeleteBook(book.Title);
ViewModel.Books.Remove(书本);
}
原因在客户的回答中给出

要使其正常工作,请执行以下操作:

private void DeleteBook_Click(object sender, RoutedEventArgs e)
{
   var books = AllBooks_GridView.SelectedItems.ToList(); // this will create a new list

   foreach (var b in books)
   {
      var book = b as Book;

      DataAccess.DeleteBook(book.Title);
      ViewModel.Books.Remove(book);
   }
}
他的回答中给出了原因

要使其正常工作,请执行以下操作:

private void DeleteBook_Click(object sender, RoutedEventArgs e)
{
   var books = AllBooks_GridView.SelectedItems.ToList(); // this will create a new list

   foreach (var b in books)
   {
      var book = b as Book;

      DataAccess.DeleteBook(book.Title);
      ViewModel.Books.Remove(book);
   }
}

当您从
图书
集合中删除图书时,
ObservableCollection
将通知数据绑定有关更改,该更改将依次修改
SelectedItems
集合

通常,这会引发一个异常,但在本例中,由于删除第一个项只会将第二个项移动到位(降低集合的大小),并且向前枚举只会检查下一个索引是否存在,而由于它不存在,因此循环不会继续,因此不会引发异常。您可以通过选择三个项目来确认这一点,第一个项目将被删除,第二个项目将被跳过,但第三个项目将再次被删除

最简单的解决方案是确保迭代不同于
SelectedItems
属性的集合。实现这一点最便宜的方法是在第一行使用LINQ
ToArray
扩展

var books=AllBooks\u GridView.SelectedItems.ToArray();

这将创建一个新数组,其中包含所选的所有书籍,然后从
ViewModel.books
中删除,并更新
SelectedItems
,这将不再重要,由于我们的
books
变量是一个不同的实例。

当您从
books
集合中删除一本书时,
observedcollection
将通知数据绑定有关更改的信息,该更改反过来将修改
SelectedItems
集合

通常,这会引发一个异常,但在本例中,由于删除第一个项只会将第二个项移动到位(降低集合的大小),并且向前枚举只会检查下一个索引是否存在,而由于它不存在,因此循环不会继续,因此不会引发异常。您可以通过选择三个项目来确认这一点,第一个项目将被删除,第二个项目将被跳过,但第三个项目将再次被删除

最简单的解决方案是确保迭代不同于
SelectedItems
属性的集合。实现这一点最便宜的方法是在第一行使用LINQ
ToArray
扩展

var books=AllBooks\u GridView.SelectedItems.ToArray();

这将创建一个新数组,其中包含所选的所有书籍,然后从
ViewModel.books
中删除。books并更新
SelectedItems
,这将不再重要,因为我们的
books
变量是一个不同的实例。

foreach循环是否运行两次?
SelectedItems
集合是否包含两个条目?你能在方法内部设置一个断点并逐步完成代码吗?我怀疑,当您从
Books
集合中删除书籍时,数据绑定会导致重新加载和
SelectedItems
更改。虽然我希望这会导致
foreach
循环中出现异常。另外,激活Windows;-)@MartinZikmund我刚刚调试了代码,有趣的是,它没有引发典型的“通过迭代修改集合”异常,它只是继续处理已经更改的集合。因此,如果您有8本书(全部选中),单击将删除4本