Try this. It creates some demo TOC data (8000 entries in 10 ms: seems too fast to be true), and then initializes the CLV with the first and last 20 entries, and every 30th entry in-between.
Then, as you scroll through the CLV, each time the VisibleRangeChanged event is called, it adds any missing entries to the CLV. Eg, for VisibleRangeChanged(100, 110) where CLV index 100 is TOC entry 1111, then it would ensure that TOC entries 1111..1121 were loaded into the CLV at indexes 100 to 110.
It works mostly ok, perhaps even good enough. During scrolling, sometimes higher-numbered items are visible, but when scrolling stops and it realigns itself with the first item, those higher-number items disappear off the bottom of the CLV. You might be able to get rid of this artifact by doing the alignment with the middle item rather than the top/first item, but... I'll leave that fun for you
If this solves the problem of quickly loading large CLVs, we now have the issue of it taking a long time to scroll through this large CLV of 8000 items. I added a translucent green scroll overlay thing that directly positions the CLV directly to anywhere from start to finish. A long-click on the CLV will hide/show the scroll overlay.
This screenshot shows the CLV as initialized, at the top. The numbers down the right-hand side are: 0 = CLV first-visible/top item index, 6 = CLV last-visible/bottom index, 305 is the number of items loaded into the CLV (approx. 20 + 8000/30 + 20), and 8000 is the number of entries in the TOC.
This screenshot shows the CLV positioned half-way though, at TOC entries 4227..4233 of 8000. Key thing to note is that we have only loaded 867 items into the CLV, ie not all 4233 entries.
This screenshot shows the CLV positioned at the end/bottom. Only 1289 items loaded into CLV, not all 8000.