简体   繁体   中英

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

I have a ListView in a Winform User Control. The VirtualMode is true and the VirtualListSize is 200. When there are less items in the ListView than visible rows, I get weird characters below the (real) items. These "artifacts" appear when the software is run on Windows 8, 10 or Windows Server 2012, but not on Windows 7.

在此处输入图片说明

Does anyone know what could be causing these "artifacts"? I added a character "A" "B", etc. to the Title of all the places where ListViewItems are created. So I know that none of the code in this user control is creating them. I added a sample solution that shows the problem below. Sometimes they appear as chinese characters, sometimes just a random letter and character combination. Usually they are not longer than 4 characters.

[Update] It does not occur on the latest Version of Windows 10.

[Update2] I was able to reproduce the problem on a small sample solution. Find the zip file here .

I ended up assigning the number of visible lines of the ListView to the VirtualListSize property, whenever the number of items in the ListView changed.

I used the following class to determine the number of visible lines in the 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;
    }
}

I know this is a bit of a hack. The artifacts would still be there, but you just can't see them anymore, because the ListView always has as many lines as can be displayed. This is the best I could come up with without changing the control.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM