Sinatr Sinatr - 2 months ago 36
C# Question

Flickering ListView in virtual mode

I found a not so funny bug in the default ListView (not owner drawed!). It flickers heavily when items are added constantly into it (by using

Timer
to example) and user is trying to see items slightly away from selected item (scrolled either up or down).

Am I doing something wrong here?



Here is some code to reproduce it:


  • Create WindowsFormsApplication1;

  • set form
    WindowState
    to Maximized;

  • put on form timer1, set
    Enabled
    to true;

  • put on form listView1:

    this.listView1.Dock = System.Windows.Forms.DockStyle.Fill;
    this.listView1.View = System.Windows.Forms.View.Details;
    this.listView1.VirtualMode = true;

  • add one column;

  • add event

    this.listView1.RetrieveVirtualItem += new System.Windows.Forms.RetrieveVirtualItemEventHandler(this.listView1_RetrieveVirtualItem);

  • and finally

    private void listView1_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e)
    {
    e.Item = new ListViewItem(e.ItemIndex.ToString());
    }

    private void timer1_Tick(object sender, EventArgs e)
    {
    listView1.VirtualListSize++;
    }






Now run it and wait until scrollbar on listview will appears (as timer will add enough items), then:


  • Select one of the first items in the listview (with mouse or keys), then scroll down by using scrollbar or mouse wheel, so that selected item will go outside of current view (up). The more you scroll down, the heavier flickering will become! And look at what scrollbar is doing ?!?!?

  • Similar effect appears if scrolling selected item down.






Question



How do I deal with it? Idea is to have sort of constantly updating log window with possibility to stop auto-scrolling and go up/down to investigate events in close proximity. But with that kek-effect it is just not possible!

Answer

It looks like problem is related to Selected / Focused combo (perhaps someone from Microsoft can confirm).

Here is a possible workaround (it's dirty and I liek it!):

    private void timer1_Tick(object sender, EventArgs e)
    {
        // before adding
        if (listView1.SelectedIndices.Count > 0)
        {
            if (!listView1.Items[listView1.SelectedIndices[0]].Bounds.IntersectsWith(listView1.ClientRectangle))
                listView1.TopItem.Focused = true;
            else
                listView1.Items[listView1.SelectedIndices[0]].Focused = true;
        }
        // add item
        listView1.VirtualListSize++;
    }

Trick is to check before adding new item whenever currently selected item is away (here is the topic of how to check). And if item is away, then set focus to the current TopItem temporarily (until user scroll back, so that selected item will be again "visible" and this is when it gets focus back).

Comments