Windows 8 及更高版本的短列表中包含工件的虚拟 ListView

Virtual ListView with artifacts in short list for Windows 8 and higher

我在 Winform 用户控件中有一个 ListView。 VirtualMode 为 true,VirtualListSize 为 200。当 ListView 中的项目少于可见行时,我会在(实际)项目下方看到奇怪的字符。这些 "artifacts" 当软件在 Windows 8、10 或 Windows Server 2012 上 运行 时出现,但在 Windows 7.

上不出现

有谁知道这些 "artifacts" 可能是什么原因造成的?我在所有创建ListViewItems的地方的Title都加了一个字符"A""B"等。所以我知道这个用户控件中的 none 代码正在创建它们。我添加了一个示例解决方案来显示以下问题。 有时它们显示为汉字,有时只是随机的字母和字符组合。通常不超过4个字符。

[更新] 它不会出现在 Windows 10.

的最新版本上

[Update2] 我能够在一个小样本解决方案上重现该问题。找到 zip 文件 here.

每当 ListView 中的项目数发生变化时,我最终将 ListView 的可见行数分配给 VirtualListSize 属性。

我使用以下 class 来确定 ListView 中的可见行数:

public static class ListViewSizer
{
    const UInt32 LVM_FIRST = 0x1000;
    const UInt32 LVM_GETHEADER = (LVM_FIRST + 31);

    private static int GetHeaderHeight(ListView listView)
    {
        Rect rect = new Rect();
        IntPtr hwnd = SendMessage((IntPtr)listView.Handle, LVM_GETHEADER, IntPtr.Zero, IntPtr.Zero);
        if (hwnd != null)
        {
            if (GetWindowRect(new System.Runtime.InteropServices.HandleRef(null, hwnd), out rect))
            {
                return rect.Bottom - rect.Top;
            }
        }

        return -1;
    }

    public static int GetLastVisibleLine(ListView listView)
    {
        int firstVisibleIndex = listView.TopItem.Index;

        int heightOfFirstItem = listView.GetItemRect(firstVisibleIndex, ItemBoundsPortion.Entire).Height;

        // assuming each item has the same height (I think this is true for list view and details view)
        const int DefaultVisibleLines = 11;
        int visibleLines = heightOfFirstItem != 0 ? (int)Math.Ceiling((decimal)((listView.Height - GetHeaderHeight(listView)) / heightOfFirstItem)) : DefaultVisibleLines;

        int lastVisibleIndexInDetailsMode = firstVisibleIndex + visibleLines;
        return lastVisibleIndexInDetailsMode;
    }

    [System.Runtime.InteropServices.DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)]
    private static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);

    [System.Runtime.InteropServices.DllImport("user32.dll")]
    private static extern bool GetWindowRect(System.Runtime.InteropServices.HandleRef hwnd, out Rect lpRect);

    [Serializable, System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
    public struct Rect
    {
        public int Left;
        public int Top;
        public int Right;
        public int Bottom;
    }
}

我知道这有点坑爹。工件仍然存在,但您再也看不到它们了,因为 ListView 总是有尽可能多的行可以显示。这是我在不更改控件的情况下所能想到的最好结果。