I have a large collection of items bound to a ListBox
, with a VirtualizingStackPanel
set as its ItemsPanel
. As the user scrolls and item containers are created, I do some work to populate the item with data (using a database query). If the user scrolls very rapidly, it builds up a large number of requests that tend to bog things down. What I would like to do is detect when the item is scrolled outside of the viewport, so I can cancel its corresponding request.
Here are the approaches I've tried thus far, and why they have not worked:
Override
VirtualizingStackPanel.OnCleanUpVirtualizedItem
. The problem is that this method seems to be called sometime much later than when the item actually goes off-screen. Cancelling my request within this method doesn't do much good because it occurs so late.Turn on container recycling with
VirtualizationMode.Recycling
. This event causes the item container'sDataContext
to change but the item container itself is reused. TheDataContextChanged
event occurs immediately as one item goes outside of view, so it is good in that regard. The problem is that container recycling creates a lot of side-effects and, in my testing, is a little buggy overall. I would prefer not to use it.
Is there are a good lower-level approach, such as hooking into layout events, that can give me a deterministic answer on when an item goes outside of view? Perhaps at the ScrollViewer
level?
Here's a rough solution that I think accomplishes what you're looking for. I'm getting the virtualizing stack panel by listening to the loaded event in the XAML. If I were doing this in production code, I might factor this into a reusable attached behavior rather than throwing a bunch of code in the code-behind.
Notice that there isn't really a property we can use here to find the on-screen children, so we look at the layout bounds of the parent compared to the children to determine which children are on screen. The code uses an extension method for getting the visual children of an item in the visual tree, included below:
This code is using a very basic view model hierarchy I created for the purposes of testing this out. I'll include it just in case it's helpful in understanding the other code:
XAML: