Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"Infinite Scrolling" in a ListView / Avoiding Re-entrant Scroll Events

My goal is to simulate "infinite scrolling" in a WPF ListView. I have achieved this task with some less-than-ideal methods, and I'm sure there is a better way to do it.

by "infinite scrolling" I mean the following:
Let's say a ListView has 20 items (ordered 1, 2, 3, 4, ... 17, 18, 19, 20). When the user scrolls down one item, the item at the top of the ListView is removed and placed at the end of the ListView so the order of the items is 2, 3, 4, 5, ... 18, 19, 20, 1. Now if the user scrolls down two items, the top two items are removed and placed at the end so the order of the items is 4, 5, 6, 7, ... 20, 1, 2, 3. Now, similarly, if the user scrolls up one item, the item at the bottom of the ListView is removed and placed at the beginning so the order of the items is 3, 4, 5, 6, ... 19, 20, 1, 2.

I have achieved this task with the following function assigned to the ScrollChanged event of the ScrollViewer I wish to be "infinite":

// sv - the ScrollViewer to which this event handler is listening
// lv - the ListView associated with "sv"
bool handle_scroll = true;
private void inf_scroll(object sender, ScrollChangedEventArgs e) {
    if (handle_scroll) {
        for (int i = 0; i < e.VerticalChange; i++) {
            object tmp = lv.Items[0];
            lv.Items.RemoveAt(0);
            lv.Items.Add(tmp);
            handle_scroll = false;
        }
        for (int i = 0; i > e.VerticalChange; i--) {
            object tmp = lv.Items[lv.Items.Count - 1];
            lv.Items.RemoveAt(lv.Items.Count - 1);
            lv.Items.Insert(0, tmp);
            handle_scroll = false;
        }
        if(!handle_scroll){
            sv.ScrollToVerticalOffset(sv.VerticalOffset - e.VerticalChange);
        }
    }
    else {
        handle_scroll = true;
    }
}

Notice the variable handle_scroll. I put this in place because the call to sv.ScrollToVerticalOffset will cause the entire inf_scroll function to be called recursively if it was not there.

I know it is bad practice to scroll the ScrollViewer in a ScrollChanged event handler, so that's why I'm asking: is there a better way to do this? How can I prevent the recursive calls to inf_scroll? Is there a better way to simulate "infinite scrolling"?

like image 810
Den Dinh Avatar asked Jul 05 '18 14:07

Den Dinh


1 Answers

Though it does not explicitly avoid scrolling in the ScrollChanged handler, you could try the following:

// sv - the ScrollViewer to which this event handler is listening
// lv - the ListView associated with "sv"
private void inf_scroll(object sender, ScrollChangedEventArgs e) {
    for (int i = 0; i < e.VerticalChange; i++) {
        object tmp = lv.Items[0];
        lv.Items.RemoveAt(0);
        lv.Items.Add(tmp);
    }
    for (int i = 0; i > e.VerticalChange; i--) {
        object tmp = lv.Items[lv.Items.Count - 1];
        lv.Items.RemoveAt(lv.Items.Count - 1);
        lv.Items.Insert(0, tmp);
    }
    lv.ScrollChanged -= inf_scroll;        // remove the handler temporarily
    sv.ScrollToVerticalOffset(sv.VerticalOffset - e.VerticalChange);
    Dispatcher.BeginInvoke(DispatcherPriority.Send, new Action(() =>{
        sv.ScrollChanged += inf_scroll;    // add the handler back after the scrolling has occurred to avoid recursive scrolling
    }));
}
like image 135
archer Avatar answered Oct 20 '22 12:10

archer