ListView Virtualization for Loading on Scroll

Matt.CookMatt.Cook USMember ✭✭

Hello all!

So I'm trying to figure out how to allow for loading individual cells as you scroll through a listview. I have a database with thousands of items, and so I don't want all of them to load at once, but only load each cell as you scroll down.

I was thinking of using the ItemAppearing for ListViews, but it seems that you get the "item" which is the cell rather than any sort of index to go and grab the right item. Also, at least for iOS. ItemAppearing seems to get called on every single item in the list first, then on those that are actually there on screen (I believe this is a bug... i.e. if you make the source an Enumerable of many integers, creating the ListView will call ItemAppearing on all of the integers, and then the 10-11 items on the actual screen).

The only method that seems to index into the ListView is to override the SetupContent method, but it seems that only gets called at creation in the beginning and not as they are created when you scroll down or up.

Does anyone have a solution for this that doesn't involve rendering the entire listview in iOS and Android individually? It seems like the idea of indexing would be difficult given that the source is held as an IEnumerable (which if someone from Xamarin could answer why that design choice is the case, that would be greatly appreciated!). And if not, could someone maybe recommend a semi-elegant solution for loading items from database into a ListView?

Thank you so much!

Best Answers


  • I am also very interested in this. How is GetCell (on iOS, for example) supposed to work to support virtual scrolling if the ListView is bound to an IEnumerable? (Seeing as how indexing is not efficient with an IEnumerable.)

  • Any word on this? Both iOS and Android allow a virtualized list. Why does Xamarin.Forms force you to hold your data in memory, even if it is off screen? (Or am I missing something here?) Thanks!!

  • Matt.CookMatt.Cook USMember ✭✭

    So based on the API for ItemsSource (<TVisual>.ItemsSource), it appears that if you implement the source as an IList? I'm wondering if that would allow you to index into the source (and also as Cristina mentions, why is ItemsSource not just an IList to begin with?)?

    While any IEnumerable implementer is accepted, any that do not implement IList or IReadOnlyList (where T is a class) will be converted to list by iterating.

  • Matt.CookMatt.Cook USMember ✭✭

    @ermau‌ Thank you for the response! So right now I am making my own custom IList class, and implementing the IList method to get the item to just go fetch the item straight from the database when called (This way it only gets the item when necessary) and returns it. On iOS this seems to work fine, as the IList.Item "get" just gets called as the cells appear on screen. On Android it appears to always iterate from the zeroth row, getting each item until the current row that has appeared. Is this a bug, or am I just implementing this incorrectly? It impacts the performance significantly, and although I suppose I could save a list of previously fetched items, this list could get very large with many items, and it wouldn't change the fact that the ListView needs to iterate through the list anyways.

  • Matt.CookMatt.Cook USMember ✭✭

    @ermau Ok thank you for your answer!

    This is currently normal for Android. It's not ideal, and there's even a comment in the code where this happens that effectively says "we should optimize this". As you say, the best solution to this at the moment is to cache items that have already been loaded.

    Just curious, is this the same for Xamarin Android as well? (just wondering whether if I made a custom renderer it would be better performance wise, or whether it would be the same)

  • EricMaupinEricMaupin USXamarin Team Xamurai

    Just curious, is this the same for Xamarin Android as well? (just wondering whether if I made a custom renderer it would be better performance wise, or whether it would be the same)

    No, this is a XF/ListView thing. For normal native use, you can only give it an array (ArrayAdapter) or implement the adapter yourself (BaseAdapter) in which the platform will ask you for a view at a position and give you a view to potentially re-use.

  • EricMaupinEricMaupin USXamarin Team Xamurai

    So I was taking another look at this code. As it turns out there's a case (likely the one you're running into) in which it is iterating when it shouldn't be (or rather, doesn't need to). I've fixed this and it should go out with the update after next. Unfortunately there is no workaround to avoid this case.

  • JoeBoothJoeBooth USMember ✭✭

    @ermau is your fix in the 1.2.2 preview 2 build? thanks!

  • EricMaupinEricMaupin USXamarin Team Xamurai
  • ThomasBurkhartThomasBurkhart DEMember ✭✭✭✭

    It would be great if you could provide an Example for this. Preferedly with XAML-Binding.

  • AdamLangleyAdamLangley NZMember ✭✭

    Hi @ermau‌,
    Has the ListView internal implementation changed since this discussion?
    I have also attempted binding a ListView to a custom IList implementation, and the ListView always grabs the enumerator and scans the entire thing in an attempt to count it.
    Of course - this sucks in the entire SQLite database - pretty bad.

  • AdamLangleyAdamLangley NZMember ✭✭

    Nevermind - I missed the mention of using IReadOnlyList - that does indeed work.

Sign In or Register to comment.