Xaml 如何从GridView中获取选定项
我正在制作一个图书馆管理软件。这是XAML: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
<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
属性的集合。实现这一点最便宜的方法是在第一行使用LINQToArray
扩展
var books=AllBooks\u GridView.SelectedItems.ToArray();
这将创建一个新数组,其中包含所选的所有书籍,然后从
ViewModel.books
中删除,并更新SelectedItems
,这将不再重要,由于我们的books
变量是一个不同的实例。当您从books
集合中删除一本书时,observedcollection
将通知数据绑定有关更改的信息,该更改反过来将修改SelectedItems
集合
通常,这会引发一个异常,但在本例中,由于删除第一个项只会将第二个项移动到位(降低集合的大小),并且向前枚举只会检查下一个索引是否存在,而由于它不存在,因此循环不会继续,因此不会引发异常。您可以通过选择三个项目来确认这一点,第一个项目将被删除,第二个项目将被跳过,但第三个项目将再次被删除
最简单的解决方案是确保迭代不同于SelectedItems
属性的集合。实现这一点最便宜的方法是在第一行使用LINQToArray
扩展
var books=AllBooks\u GridView.SelectedItems.ToArray();
这将创建一个新数组,其中包含所选的所有书籍,然后从
ViewModel.books
中删除。books并更新SelectedItems
,这将不再重要,因为我们的books
变量是一个不同的实例。foreach循环是否运行两次?SelectedItems
集合是否包含两个条目?你能在方法内部设置一个断点并逐步完成代码吗?我怀疑,当您从Books
集合中删除书籍时,数据绑定会导致重新加载和SelectedItems
更改。虽然我希望这会导致foreach
循环中出现异常。另外,激活Windows;-)@MartinZikmund我刚刚调试了代码,有趣的是,它没有引发典型的“通过迭代修改集合”异常,它只是继续处理已经更改的集合。因此,如果您有8本书(全部选中),单击将删除4本