如何从 GridView 中获取选定的项目
How to get selected items from a GridView
我在做图书馆管理软件。这是 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>
我想 select 多个栏并在按下第二个应用栏按钮时删除它们。这是 DeleteBook_Click 函数:
private void DeleteBook_Click(object sender, RoutedEventArgs e)
{
var books = AllBooks_GridView.SelectedItems;
foreach (var b in books)
{
var book = b as Book;
DataAccess.DeleteBook(book.Title);
ViewModel.Books.Remove(book);
}
}
这是我的应用程序的屏幕截图:
当我点击删除图标时,只有第一本书被删除了。虽然他们两个都是selected。我做错了什么?
您在循环访问其项目时正在更改集合。
AllBooks_GridView.SelectedItems
是集合,当您在 foreach
循环中迭代它时,您可以通过调用
来更改它
ViewModel.Books.Remove(book);
此调用后,GridView
会刷新以反映您对 ViewModel
所做的更改,并且 SelectedItems
集合中的项目会变少。在下一次迭代中,SelectedItems
不再像您预期的那样包含两本书,它只包含一本。由于代码试图在下一次迭代中从集合中删除第 2 本书,因此没有任何反应并且循环终止。
这是一个修复:
//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);
}
原因在Kennyzx的回答中给出。
要使其正常工作,请执行以下操作:
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);
}
}
当您从 Books
集合中删除一本书时,ObservableCollection
将通知数据绑定有关更改,进而修改 SelectedItems
集合。
通常,这会抛出异常,但在这种情况下,不会抛出异常 "masked",因为移除第一项只是将第二项移到它的位置(降低集合的大小) ) 并向前枚举只是检查下一个索引是否存在,因为它不存在,所以循环不会继续。您可以通过选择 三项 来确认这一点 - 第一项将被删除,第二项将被跳过,但第三项将再次被删除。
最简单的解决方案是确保迭代与 SelectedItems
属性 不同的集合。最便宜的实现方式是在第一行使用 LINQ ToArray
扩展。
var books = AllBooks_GridView.SelectedItems.ToArray();
这将创建一个新数组,其中包含所有已选择的书籍,然后当您从 ViewModel.Books
中删除并更新 SelectedItems
时,这将不再重要,因为我们的 books
变量是一个 不同的 实例。
我在做图书馆管理软件。这是 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>
我想 select 多个栏并在按下第二个应用栏按钮时删除它们。这是 DeleteBook_Click 函数:
private void DeleteBook_Click(object sender, RoutedEventArgs e)
{
var books = AllBooks_GridView.SelectedItems;
foreach (var b in books)
{
var book = b as Book;
DataAccess.DeleteBook(book.Title);
ViewModel.Books.Remove(book);
}
}
这是我的应用程序的屏幕截图:
当我点击删除图标时,只有第一本书被删除了。虽然他们两个都是selected。我做错了什么?
您在循环访问其项目时正在更改集合。
AllBooks_GridView.SelectedItems
是集合,当您在 foreach
循环中迭代它时,您可以通过调用
ViewModel.Books.Remove(book);
此调用后,GridView
会刷新以反映您对 ViewModel
所做的更改,并且 SelectedItems
集合中的项目会变少。在下一次迭代中,SelectedItems
不再像您预期的那样包含两本书,它只包含一本。由于代码试图在下一次迭代中从集合中删除第 2 本书,因此没有任何反应并且循环终止。
这是一个修复:
//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);
}
原因在Kennyzx的回答中给出。
要使其正常工作,请执行以下操作:
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);
}
}
当您从 Books
集合中删除一本书时,ObservableCollection
将通知数据绑定有关更改,进而修改 SelectedItems
集合。
通常,这会抛出异常,但在这种情况下,不会抛出异常 "masked",因为移除第一项只是将第二项移到它的位置(降低集合的大小) ) 并向前枚举只是检查下一个索引是否存在,因为它不存在,所以循环不会继续。您可以通过选择 三项 来确认这一点 - 第一项将被删除,第二项将被跳过,但第三项将再次被删除。
最简单的解决方案是确保迭代与 SelectedItems
属性 不同的集合。最便宜的实现方式是在第一行使用 LINQ ToArray
扩展。
var books = AllBooks_GridView.SelectedItems.ToArray();
这将创建一个新数组,其中包含所有已选择的书籍,然后当您从 ViewModel.Books
中删除并更新 SelectedItems
时,这将不再重要,因为我们的 books
变量是一个 不同的 实例。