遍历带复选框的虚拟ListView锯齿数组时触发非越界异常求助
虚拟ListView选中状态访问异常的解决方案
问题根源
虚拟模式(VirtualMode=true)下的ListView不会为所有项创建ListViewItem实例,仅会实例化当前可见的项。直接通过索引访问非可见项的属性(如Checked、Text)时,会触发ArgumentOutOfRangeException——因为这些项并未被ListView实际创建,看似数组长度足够,但对应的ListViewItem对象根本不存在。
解决思路
放弃通过ListViewItem实例获取选中状态,改为自己维护独立的数据源,全程用数据源管理项的文本和选中状态:
- 用二维数组(或其他集合)存储每个文件夹下所有项的选中状态和文本内容
- 在ListView的
RetrieveVirtualItem事件中,将数据源的数据绑定到ListView项 - 用户操作复选框时,在对应事件中更新数据源的状态
- 遍历数据时直接读取自己维护的数据源,而非访问
ListViewItem
修改后的代码示例
1. 维护独立数据源
// 全局或类级别的数据源:存储每个文件夹下项的文本和选中状态 string[][] itemTexts; bool[][] itemCheckedStates; // 初始化数据源(需与文件夹/项数量匹配) itemTexts = new string[myListViewItems.Length][]; itemCheckedStates = new bool[myListViewItems.Length][]; for (int folder = 0; folder < myListViewItems.Length; folder++) { int rowLength = rowLengths[folder]; itemTexts[folder] = new string[rowLength]; itemCheckedStates[folder] = new bool[rowLength]; // 初始化文本(仅首次从原始数据读取,后续不再访问myListViewItems) for (int item = 0; item < rowLength; item++) { itemTexts[folder][item] = myListViewItems[folder][item].Text; itemCheckedStates[folder][item] = false; // 默认未选中 } }
2. 遍历数据源生成TagData
for (int folder = 0; folder < myListViewItems.Length; folder++) { int rowLength = rowLengths[folder]; programs[folder].Tags = new TagData[rowLength]; for (int item = 0; item < rowLength; item++) { try { // 直接从自定义数据源读取,避免访问不存在的ListViewItem var text = itemTexts[folder][item]; var isChecked = itemCheckedStates[folder][item]; programs[folder].Tags[item] = new TagData(text, isChecked); } catch (Exception ex) { AddMessage($"myListViewTags[] = {myListViewItems.Length}. myListViewTags[][] = {myListViewItems[folder].Length}"); AddMessage(ex.Message); } } }
3. 绑定ListView虚拟项事件
// RetrieveVirtualItem事件:为可见项绑定数据源数据 private void listView_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e) { int currentFolder = GetCurrentSelectedFolderIndex(); // 需实现此方法,获取TreeView当前选中的文件夹索引 if (currentFolder >= 0 && e.ItemIndex < itemTexts[currentFolder].Length) { e.Item = new ListViewItem(itemTexts[currentFolder][e.ItemIndex]); e.Item.Checked = itemCheckedStates[currentFolder][e.ItemIndex]; } } // ItemChecked事件:用户点击复选框时同步更新数据源 private void listView_ItemChecked(object sender, ItemCheckedEventArgs e) { int currentFolder = GetCurrentSelectedFolderIndex(); if (currentFolder >= 0 && e.Item.Index < itemCheckedStates[currentFolder].Length) { itemCheckedStates[currentFolder][e.Item.Index] = e.Item.Checked; } }
关键注意事项
- 必须确保
rowLengths数组与数据源的维度完全匹配,避免索引错位 - 切换TreeView文件夹时,需同步更新ListView的
VirtualListSize为当前文件夹的项数量 - 虚拟模式下绝对不要直接遍历所有
ListViewItem实例,所有状态和数据都要通过自定义数据源管理
内容的提问来源于stack exchange,提问作者Melvin
相关产品推荐
相关产品推荐

