You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

虚拟模式下C# ListView多选时SelectedIndices.Count为0的问题求助

解决虚拟模式ListView多选无法获取全部选中索引的问题

兄弟,我太懂从C/C++转.NET GUI踩坑的滋味了!这个问题其实是虚拟模式ListView的一个“特性”——它为了极致性能,不会自动帮你维护所有项的选中状态,毕竟虚拟模式是给大数据量准备的,控件不可能缓存几万几十万条项的状态。下面给你一步步解决:

核心原因

虚拟模式下,ListView只会加载当前可见区域的项,对于不在屏幕上的项,控件完全不知道它们的选中状态。自带的SelectedIndices只能返回当前可见的选中项,非可见的选中项根本不会被记录,所以多选跨区域时就会丢数据。

解决方案:手动维护选中索引集合

我们需要自己搞一个集合来存所有选中的索引,通过控件的事件来更新这个集合,以后取选中项就从这个集合里拿,不要用控件自带的。

1. 声明全局的选中索引集合

用HashSet<int>最合适,自动去重,增删效率高:

private HashSet<int> _mySelectedIndices = new HashSet<int>();

2. 订阅ItemSelectionChanged事件更新集合

这个事件会在任何项的选中状态变化时触发,不管是点击、Ctrl+点击还是Shift+点击:

private void listView1_ItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e)
{
    if (e.IsSelected)
    {
        _mySelectedIndices.Add(e.ItemIndex);
    }
    else
    {
        _mySelectedIndices.Remove(e.ItemIndex);
    }
}

3. 在RetrieveVirtualItem中恢复项的选中状态

当滚动ListView加载新项时,要告诉控件这个项是否应该是选中的,不然之前选中的项滚出屏幕再滚回来会变成未选中:

private void listView1_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e)
{
    // 按需创建项
    var item = new ListViewItem($"虚拟项 {e.ItemIndex}");
    // 从我们的集合里判断是否选中
    item.Selected = _mySelectedIndices.Contains(e.ItemIndex);
    e.Item = item;
}

4. 获取选中索引时用自己的集合

以后别再用listView1.SelectedIndices了,直接取我们维护的集合:

// 比如点击按钮获取所有选中索引
private void btnGetSelected_Click(object sender, EventArgs e)
{
    // 可以排序一下,看起来更舒服
    var sortedSelected = _mySelectedIndices.OrderBy(idx => idx).ToList();
    MessageBox.Show($"选中的索引:{string.Join(", ", sortedSelected)}");
}

额外:支持全选操作

虚拟模式下Ctrl+A不会自动全选所有项,因为控件不知道总共有多少项。如果需要全选,手动给集合塞所有索引就行:

private void btnSelectAll_Click(object sender, EventArgs e)
{
    _mySelectedIndices.Clear();
    // 遍历所有虚拟项的索引
    for (int i = 0; i < listView1.VirtualListSize; i++)
    {
        _mySelectedIndices.Add(i);
    }
    // 刷新控件让选中状态显示出来
    listView1.Invalidate();
}

完整示例代码

把这些拼起来,一个能正常工作的虚拟模式多选ListView就搞定了:

public partial class MainForm : Form
{
    private HashSet<int> _mySelectedIndices = new HashSet<int>();
    // 模拟1000条虚拟数据
    private const int TotalVirtualItems = 1000;

    public MainForm()
    {
        InitializeComponent();
        // 配置ListView虚拟模式
        listView1.VirtualMode = true;
        listView1.VirtualListSize = TotalVirtualItems;
        listView1.MultiSelect = true;
        listView1.FullRowSelect = true;

        // 订阅必要的事件
        listView1.RetrieveVirtualItem += ListView1_RetrieveVirtualItem;
        listView1.ItemSelectionChanged += ListView1_ItemSelectionChanged;
    }

    private void ListView1_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e)
    {
        var item = new ListViewItem($"Item {e.ItemIndex}");
        item.SubItems.Add($"子项 {e.ItemIndex}");
        // 恢复选中状态
        item.Selected = _mySelectedIndices.Contains(e.ItemIndex);
        e.Item = item;
    }

    private void ListView1_ItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e)
    {
        if (e.IsSelected)
        {
            _mySelectedIndices.Add(e.ItemIndex);
        }
        else
        {
            _mySelectedIndices.Remove(e.ItemIndex);
        }
    }

    private void btnGetSelected_Click(object sender, EventArgs e)
    {
        var sortedIndices = _mySelectedIndices.OrderBy(i => i).ToList();
        MessageBox.Show($"共选中{sortedIndices.Count}项:\n{string.Join("\n", sortedIndices)}");
    }

    private void btnSelectAll_Click(object sender, EventArgs e)
    {
        _mySelectedIndices.Clear();
        for (int i = 0; i < TotalVirtualItems; i++)
        {
            _mySelectedIndices.Add(i);
        }
        listView1.Invalidate();
    }
}

这样不管你选多少项,跨多少滚动区域,都能准确拿到所有选中的索引了!

内容的提问来源于stack exchange,提问作者nohupper

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 04:01:05