0
votes

I'm using a Windows Forms ListView control to display a list of items, up to 1000 items in total, though only about 20 are visible within the listview at one time (listview is using Details view).

I'm frequently adding new items to the bottom of the listview, and scroll to the newly added item automatically (using item.EnsureVisible()) so the user can see the most recent data. When the list size is above 1000 items, I remove the oldest list item (i.e. index 0, the one at the top of the listview) from the list to keep it at 1000 items.

Now to my problem:

Whenever the selection in the listview changes, additional details associated with the item are shown elsewhere in the form. When this selection change occurs, I stop the auto-scroll-to-new-items so the user's selected item stays where it is (i.e. the list doesn't scroll to newest items when an item in it is selected), and only re-enable the auto-scroll-to-newest when the user dismisses the additional details part of the form.

This all works fine, except when I remove the oldest item from the listview (to ensure the list doesn't grow beyond 1000 items): When I remove that oldest item, the listview scrolls everything up by 1 automatically (i.e. nothing I've done programatically does this scrolling). I realise that if the selected item is one of the earliest 20 events (which makes the earliest 20 events the visible ones), it will have no choice but to scroll the visible items when it removes the earliest, but if the selection is, say, midway through the list, it should have no need to scroll the listview items.

Is there any way I can prevent the listview automatically scrolling up by one when I remove the oldest item? Or will I have to work around it by making sure the visible items remain in the position they were before I removed the oldest item, after removing it (which seems a real hassle)?

1
Force the SelectedIndex to be what you want it to be based on your requirements, after you remove item(s).Steve
Not sure what you mean - that won't mean the selected item is still visible?Nick Shaw

1 Answers

0
votes

Ok, this is my not-ideal (but at least mostly working) solution, in C# (converted from VB.NET so StackOverflow's syntax highlighter can display it properly, so apologies for any typos!).

Any better solutions, please do suggest them!

// loop through every item in the list from the top, until we find one that is
// within the listview's visible area
private int GetListViewTopVisibleItem()
{
    foreach (ListViewItem item In ListView1.Items)
    {
        if (ListView1.ClientRectangle.IntersectsWith(item.GetBounds(ItemBoundsPortion.Entire)))
        {
            // +2 as the above intersection doesn't take into account the height
            // of the listview's header (in Detail mode, which my listview is)
            return (item.Index + 2);
        }
    }
    // failsafe: return a valid index (which won't be used)
    return 0;
}

private void RemoveOldestItem()
{
    // pause redrawing of the listview while we fiddle...
    ListView1.SuspendLayout();

    // if we are not auto-scrolling to the most recent item, and there are
    // still items in the list:
    int TopItemIndex = 0;
    if (!AllowAutoScroll && (ListView1.SelectedItems.Count > 0))
        TopItemIndex = GetListViewTopVisibleItem();

    // remove the first item in the list
    ListView1.Items.RemoveAt(0);

    // if we are not auto-scrolling, and the previous top visible item was not
    // the one we just removed, make that previously top-visible item visible
    // again.  (We decrement TopItemIndex as we just removed the first item from
    // the list, which means all indexes have shifted down by 1)
    if (!AllowAutoScroll && (--TopItemIndex > 0))
        ListView1.Items(TopItemIndex).EnsureVisible();

    // allow redraw of the listview now
    ListView1.ResumeLayout()
}

(This assumes, of course, that the selected item is currently visible otherwise it doesn't make a whole lot of sense to do; it always is visible in my scenario though, unless the selected event is the one being removed from the top of the list (in which case nothing is selected anymore so the issue goes away))