Dutton

Auto-scrolling a WPF ListView when a new item is added

AKA: "How to give a WPF ListView a sticky bottom" but I thought better of making that the blog post title =)

If you've ever tried to auto-scroll a WPF ListView when a new item is added, you will find that as you can't access the ListView's internal ScrollViewer from XAML this is a trickier task than it should be.

Here is an auto-scrolling ListView that will ensure that any new items added will be automatically scrolled into view. This behavior is overridden if the scrollbar position is not currently at the bottom.

public class AutoScrollingListView : ListView
{
  private ScrollViewer _scrollViewer;

  protected override void OnItemsSourceChanged(System.Collections.IEnumerable oldValue, System.Collections.IEnumerable newValue)
  {
    base.OnItemsSourceChanged(oldValue, newValue);

    if (oldValue as INotifyCollectionChanged != null)
      (oldValue as INotifyCollectionChanged).CollectionChanged -= ItemsCollectionChanged;

    if (newValue as INotifyCollectionChanged == null) return;

    (newValue as INotifyCollectionChanged).CollectionChanged += ItemsCollectionChanged;
  }

  public override void OnApplyTemplate()
  {
    base.OnApplyTemplate();

    // Dig out and store a reference to our internal ScrollViewer
    _scrollViewer = RecursiveVisualChildFinder<ScrollViewer>(this) as ScrollViewer;
  }

  void ItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
  {
    if (_scrollViewer == null) return;

    if (!_scrollViewer.VerticalOffset.Equals(_scrollViewer.ScrollableHeight)) return;

    UpdateLayout();
    _scrollViewer.ScrollToBottom();
  }

  private static DependencyObject RecursiveVisualChildFinder<T>(DependencyObject rootObject)
  {
    var child = VisualTreeHelper.GetChild(rootObject, 0);
    if (child == null) return null;

    return child.GetType() == typeof (T) ? child : RecursiveVisualChildFinder<T>(child);
  }
}

As you can see, we derive from ListView, and do two things:


Share this: