Detect when a ListBox scrolls to its end (WP7)
I wanted to fetch and add new items to a ListBox when it scrolls to its end, to reproduce infinite scrolling.
Fellow tweep @GaryGJohnson pointed me to the ScrollViewer’s VerticalOffset, ExtentHeight and ViewportHeight, which would give me all the maths I needed.
Unfortunately, there it no scrolling events available, either on the ListBox or the inner ScrollViewer. From what I understand, I could have received change notifications directly from the ScrollViewer’s VerticalOffset property if I were to work with SL4, but not with SL3 on Windows Phone 7. My friend Jacques came to the rescue! He suggested I create myself a custom DependencyProperty with a changed notification, and bind it to the ScrollViewer’s VerticalOffset.
ItemsSource="{Binding Listings}"
ItemTemplate="{StaticResource ListingItemTemplate}"
SelectionChanged="businessesListBox_SelectionChanged"
Style="{StaticResource BusinessListBoxStyle}" />
From Blend, I right-clicked my ListBox, chose “Edit Template” –> “Edit a copy” to add that BusinessListBoxStyle, and added a handler for the ScrollViewer’s Loaded event.
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Foreground" Value="{StaticResource PhoneForegroundBrush}"/>
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled"/>
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBox">
<ScrollViewer x:Name="scrollViewer"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}"
Foreground="{TemplateBinding Foreground}"
Padding="{TemplateBinding Padding}"
Loaded="ScrollViewer_Loaded">
<ItemsPresenter/>
</ScrollViewer>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I created a custom property in my PhoneApplicationPage.
"ListVerticalOffset",
typeof( double ),
typeof( SearchBusinessResultsPage ),
new PropertyMetadata( new PropertyChangedCallback( OnListVerticalOffsetChanged ) ) );
public double ListVerticalOffset
{
get { return ( double )this.GetValue( ListVerticalOffsetProperty ); }
set { this.SetValue( ListVerticalOffsetProperty, value ); }
}
From the ScrollViewer’s Loaded handler, I just need to bind my custom property to to the ScrollViewer’s VerticalOffset. Notice I keep a reference on the ScrollViewer, since it’s in a ContentTemplate.
private void ScrollViewer_Loaded( object sender, RoutedEventArgs e )
{
_listScrollViewer = sender as ScrollViewer;
Binding binding = new Binding();
binding.Source = _listScrollViewer;
binding.Path = new PropertyPath( "VerticalOffset" );
binding.Mode = BindingMode.OneWay;
this.SetBinding( ListVerticalOffsetProperty, binding );
}
The final job is to verify how far the scrolling is every time my custom property changes (thus every time the ScrollViewer’s VerticalOffset changes). I also make sure not to trigger updates more than once per ScrollableHeight.
private static void OnListVerticalOffsetChanged( DependencyObject obj, DependencyPropertyChangedEventArgs e )
{
SearchBusinessResultsPage page = obj as SearchBusinessResultsPage;
ScrollViewer viewer = page._listScrollViewer;
if( viewer != null )
{
if( page._lastFetch < viewer.ScrollableHeight )
{
// Trigger within 1/4 the viewport.
if( viewer.VerticalOffset >= viewer.ScrollableHeight – viewer.ViewportHeight / 4 )
{
page._lastFetch = viewer.ScrollableHeight;
this.ViewModel.FetchNextPage();
}
}
}
}
As you can see, you can adjust how close to the end you trigger an update. There is one final gotcha you much be warned of. Make sure your “FetchNextPage” equivalent prevents reentrancy, just in case adding a new item to your bound collection causes the OnListVerticalOffset event to fire (we never know) and another fetch is triggered.

Nice work!
Nice. I was thinking about this too, although I was considering detecting when the last bound data is fetched and for that to fire event
Since it’s a static method, how can you put “this.ViewModel.FetchNextPage();” ?
Ah! And nobody noticed before? It’s obviously “page.”, not “this.”. Will fix. Thanks!
Meh. I can’t edit the code snippets without the post becoming unreadable, and Windows Live Writer doesn’t let me change the HTML. People will have to read the comments to realize the error.
It’s the single line of code anyone using this technique must replace with their own code anyway.
Awesome ! I could have looked a little bit more to get what you were doing. Thanks a lot !
Thanks for this! This worked great in my application, and is greatly appreciated
cheers
Hello
I’ve trying to get your sample all together but i can’t make it.
Every time i’m getting an error either in the building phase the execution.
Could you provide a sample code working?
Thanks
Laranzu
I liked the approach of it. What will be the differences when the listbox is in a UserControl? Because it is not working for me when the list box is in user control.
Sorry dude, Cancel my comment. I did a mistake in implementing this. Mine is a search app so I need to clear the _lastFetch variable and I forgot to do that. I was blindly implementing it earlier since I am new to Dependencies properties and Silverlight. I read on them now and figured it. Thanks for the answer.
bad code, it’s not work.
try to search working code