从 ListView 中删除带有图像的 ListViewItem

Removing ListViewItem with image from ListView

我正在尝试动态更改程序中的 ListView。每个项目都有一个 ImageKey,我为它们使用 SmallImageList

问题是每当我删除一个项目时,就会出现this question中提到的问题:

Before and after deleting an item:

Code used:

// Add the images from an array of paths
foreach (string xFile in files)
{
    thumbnails_imageList.Images.Add(xFile, images[xFile]);
    files_lst.Items.Add(xFile, Path.GetFileNameWithoutExtension(xFile), xFile);
}

// Delete the selected key(s)
foreach (ListViewItem xItem in files_lst.SelectedItems)
{
    files_lst.Items.Remove(xItem);
    thumbnails_imageList.Images.RemoveByKey(xItem.Name);
}

问题中的答案(建议不要从ImageList中删除图片)不符合我的要求,因为我在删除后添加了相同ImageKey的项目,所以,超过SmallImageList.Images 中的一个 Image 得到相同的 ImageKey,因此图像变得不一致。答案也忽略了明显的内存泄漏。

我认为问题在于您尝试在 foreach 循环中修改列表。我建议首先进行循环并记住要在新列表中删除的 SelectedItems,然后在下一个循环中删除它们。这样您就不会编辑正在循环播放的列表。

不幸的是,从 ImageList 中删除 Image 确实会导致 Items 的索引向上移动。这意味着 Keys 在内部不再使用,而是在添加或设置时映射到索引,然后不再保持原样。

因此,要解决此问题,您可以...:[=​​26=]

  • 将所有 Images 保留在 ImageList 中,并与不需要的 Images 一起生活。在 256x256pixels x 4 byte 下一个 Image 只能有 256k,所以内存浪费不会那么大。 (请注意,没有 GDI+ 资源被浪费,因为 ImageList 不会为其 Images 创建任何句柄。)但是如果 adding/removing 足够大,ImageList 的大小可能会增加成为问题..

  • 或者您可以通过存储和重置 ImageKeys 来变通。

这是一个例子:

private void DeleteButton_Click(object sender, EventArgs e)
{
    foreach (ListViewItem xItem in listView1.SelectedItems)
    {
        // maybe insert the loop below here (*)
        listView1.Items.Remove(xItem);
        // only delete when no longer in use:
        string key = xItem.ImageKey;
        if (listView1.Items.Cast<ListViewItem>().Count(x => x.ImageKey == key) == 0)
            imageList1.Images.RemoveByKey(key);

    }
    // after deletions, restore the ImageKeys
    // maybe add a check for Tag == null
    foreach (ListViewItem xItem in listView1.Items)
        xItem.ImageKey = xItem.Tag.ToString();

}

为此,您需要存储正确的密钥字符串。我选择这样做是在IListViewItem.Tag属性。您可以在添加 Items 时或在删除之前执行此操作:

foreach (ListViewItem xItem in listView1.Items)
        xItem.Tag = xItem.ImageKey;               // (*)