WPF中带GridView的ListView删除项时触发索引越界异常求助
问题解决:WPF ListView删除项后Refresh抛出索引越界异常
问题场景
实现基于WPF ListView+GridView的书签管理功能,自定义类型BMark及集合定义如下:
private class BMark { public string folder { get; set; } public string shortName { get; set; } } private List<BMark> mBookmarks;
将mBookmarks绑定为ListView的ItemSource,XAML结构:
<ListView Margin="5" Name="lvBookmarks" SelectionChanged="lvBookmarks_SelectionChanged" > <ListView.View > <GridView x:Name="gridView" > <GridViewColumn Header="Folder Path" Width="350" DisplayMemberBinding="{Binding folder}" /> <GridViewColumn Header="Short Name" Width="170" DisplayMemberBinding="{Binding shortName}" /> </GridView> </ListView.View> </ListView>
修改、上下移动项功能正常,但删除项时,调用lvBookmarks.Items.Refresh()抛出System.ArgumentOutOfRangeException,提示索引超出范围;不调用Refresh则UI不更新,后续操作还会触发相同异常。
问题原因
删除项后,ListView的SelectedIndex仍保留原索引值,但此时mBookmarks集合长度已减少1,该索引已超出集合有效范围。调用Items.Refresh()时,控件会尝试访问这个无效的选中索引,从而抛出异常。
解决方案
方案1:临时清空选中状态再刷新(快速修复)
在删除项后,先将SelectedIndex设为-1,刷新后再根据集合剩余情况设置合理的选中索引:
private void btnDelete_Click(object sender, RoutedEventArgs e) { int selectedIndex = lvBookmarks.SelectedIndex; if (selectedIndex < 0 || selectedIndex >= mBookmarks.Count) return; mBookmarks.RemoveAt(selectedIndex); // 先清空选中状态,避免刷新时访问无效索引 lvBookmarks.SelectedIndex = -1; lvBookmarks.Items.Refresh(); // 集合还有项时,设置新的选中索引 if (mBookmarks.Count > 0) { lvBookmarks.SelectedIndex = Math.Min(selectedIndex, mBookmarks.Count - 1); } }
方案2:改用ObservableCollection(推荐的WPF规范做法)
WPF中推荐使用ObservableCollection<T>代替List<T>,它会自动通知UI集合的增删改变化,无需手动调用Items.Refresh();同时让实体类实现INotifyPropertyChanged,可自动同步属性变化到UI。
- 修改集合类型:
// 替换原List<BMark>定义 private ObservableCollection<BMark> mBookmarks; // 初始化时改为 mBookmarks = new ObservableCollection<BMark>();
- 让
BMark实现INotifyPropertyChanged接口:
private class BMark : INotifyPropertyChanged { private string _folder; public string folder { get => _folder; set { _folder = value; OnPropertyChanged(nameof(folder)); } } private string _shortName; public string shortName { get => _shortName; set { _shortName = value; OnPropertyChanged(nameof(shortName)); } } public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
- 简化删除按钮逻辑,无需手动Refresh:
private void btnDelete_Click(object sender, RoutedEventArgs e) { int selectedIndex = lvBookmarks.SelectedIndex; if (selectedIndex < 0 || selectedIndex >= mBookmarks.Count) return; mBookmarks.RemoveAt(selectedIndex); // 更新选中索引,避免无效选中 if (mBookmarks.Count > 0) { lvBookmarks.SelectedIndex = Math.Min(selectedIndex, mBookmarks.Count - 1); } else { lvBookmarks.SelectedIndex = -1; } }
- 优化其他功能,移除手动Refresh:
- 修改项功能:
private void btnApply_Click(object sender, RoutedEventArgs e) { int selectedIndex = lvBookmarks.SelectedIndex; if (selectedIndex < 0 || selectedIndex >= mBookmarks.Count) return; mBookmarks[selectedIndex].shortName = txtShortName.Text; // 属性变化会自动通知UI,无需Refresh }
- 上下移动项功能:
private void btnMoveDown_Click(object sender, RoutedEventArgs e) { int selectedIndex = lvBookmarks.SelectedIndex; if (selectedIndex >= mBookmarks.Count - 1) return; // 交换集合中元素位置,UI自动同步 var temp = mBookmarks[selectedIndex]; mBookmarks[selectedIndex] = mBookmarks[selectedIndex + 1]; mBookmarks[selectedIndex + 1] = temp; lvBookmarks.SelectedIndex = selectedIndex + 1; } private void btnMoveUp_Click(object sender, RoutedEventArgs e) { int selectedIndex = lvBookmarks.SelectedIndex; if (selectedIndex == 0) return; var temp = mBookmarks[selectedIndex]; mBookmarks[selectedIndex] = mBookmarks[selectedIndex - 1]; mBookmarks[selectedIndex - 1] = temp; lvBookmarks.SelectedIndex = selectedIndex - 1; }
该方案既解决了删除时的异常问题,也符合WPF数据绑定的设计规范,减少手动操作UI的代码。
内容的提问来源于stack exchange,提问作者Bob_M
相关产品推荐
相关产品推荐

