ListBox – Automatically scroll to bottom

In WinForms it was so simple to tell the ListBox to scroll to the buttom.
But in WPF it seems to be rather difficult but it is not.
You can simply create an attached property and once done it will do it for you.

Simply add the attached property to your ListBox

<ListBox ItemsSource="{....}" Extenders:ListBoxExtenders.AutoScrollToEnd="True" />

And here is the source code of the attached property

/// <summary>
  /// This class contains a few useful extenders for the ListBox
  /// </summary>
  public class ListBoxExtenders : DependencyObject
  {
    public static readonly DependencyProperty AutoScrollToEndProperty = DependencyProperty.RegisterAttached("AutoScrollToEnd", typeof(bool), typeof(ListBoxExtenders), new UIPropertyMetadata(default(bool), OnAutoScrollToEndChanged));

    /// <summary>
    /// Returns the value of the AutoScrollToEndProperty
    /// </summary>
    /// <param name="obj">The dependency-object whichs value should be returned</param>
    /// <returns>The value of the given property</returns>
    public static bool GetAutoScrollToEnd(DependencyObject obj)
    {
      return (bool)obj.GetValue(AutoScrollToEndProperty);
    }

    /// <summary>
    /// Sets the value of the AutoScrollToEndProperty
    /// </summary>
    /// <param name="obj">The dependency-object whichs value should be set</param>
    /// <param name="value">The value which should be assigned to the AutoScrollToEndProperty</param>
    public static void SetAutoScrollToEnd(DependencyObject obj, bool value)
    {
      obj.SetValue(AutoScrollToEndProperty, value);
    }

    /// <summary>
    /// This method will be called when the AutoScrollToEnd
    /// property was changed
    /// </summary>
    /// <param name="s">The sender (the ListBox)</param>
    /// <param name="e">Some additional information</param>
    public static void OnAutoScrollToEndChanged(DependencyObject s, DependencyPropertyChangedEventArgs e)
    {
      var listBox = s as ListBox;
      var listBoxItems = listBox.Items;
      var data = listBoxItems.SourceCollection as INotifyCollectionChanged;

      var scrollToEndHandler = new System.Collections.Specialized.NotifyCollectionChangedEventHandler(
          (s1, e1) =>
          {
            if (listBox.Items.Count > 0)
          {
            object lastItem = listBox.Items[listBox.Items.Count - 1];
            listBoxItems.MoveCurrentTo(lastItem);
            listBox.ScrollIntoView(lastItem);
          }
          });

      if ((bool)e.NewValue)
        data.CollectionChanged += scrollToEndHandler;
      else
        data.CollectionChanged -= scrollToEndHandler;
    }
  }

Note that it is similar to the scroll current element into view
So check this out: scroll current item into view

Advertisements
    • Mortifus
    • May 6th, 2011

    Used with a listbox, with itemssource databinding, it doesn’t work.
    Replace OnAutoScrollToEndChanged by OnIsAutoscrollChanged method to fix it @ http://khason.net/blog/auto-scroll-listbox-in-wpf/

      • michlG
      • May 7th, 2011

      Hey. Thank you for telling me that.
      Now I changed my code a little bit and it works 🙂
      I guess that I have not been testing the old code before posting it 😉

    • Al
    • September 30th, 2011

    The line
    data.CollectionChanged -= scrollToEndHandler
    potentially leaks memory.

    Unregistering events with lambda expressions hs problems, see
    http://stackoverflow.com/questions/2465040/using-lambda-expressions-
    for-event-handlers/2465054#2465054

      • michlG
      • October 1st, 2011

      Hi, thanks for the information. I will fix that when I find a bit of time 🙂

      • openshac
      • March 21st, 2013

      I tried this and yes the lamba expression does not unregister correctly because you are not using the exact same lamba. You are simply declaring another one that looks the same. And you can’t store the original one because you are working inside a static function.

      However I did find a workaround. Since the same ListBox is injected into the OnAutoScrollToEndChanged() method we can store the exact EventHandler there by using the ListBox’s Tag property:

      if ((bool)e.NewValue)
      {
      if (listBox.Tag == null)
      data.CollectionChanged += scrollToEndHandler;

      listBox.Tag = scrollToEndHandler;
      }
      else
      {
      var eventHandler = listBox.Tag as NotifyCollectionChangedEventHandler;

      if (listBox.Tag != null && eventHandler != null)
      data.CollectionChanged -= (NotifyCollectionChangedEventHandler)listBox.Tag;

      listBox.Tag = scrollToEndHandler;
      }

    • Sunny
    • November 6th, 2011

    Thanks for sharing this. It works like charm.

    • Michael Luna
    • October 30th, 2014

    Databind the AutoScrollToEnd to property on view model.
    I set the property if any item is selected it will not scroll

    And changed from ListBox to ListView for my own purposes.

    Added:

    private static Dictionary eventPointer = new Dictionary();

    then changed OnAutoScrollToEndChanged to:

    public static void OnAutoScrollToEndChanged(DependencyObject s, DependencyPropertyChangedEventArgs e)
    {
    var listView = s as ListView;
    var listViewItems = listView.Items;
    var data = listViewItems.SourceCollection as INotifyCollectionChanged;

    if ((bool)e.NewValue)
    {
    var scrollToEndHandler = new System.Collections.Specialized.NotifyCollectionChangedEventHandler(
    (s1, e1) =>
    {
    if (listView.Items.Count > 0)
    {
    object lastItem = listView.Items[listView.Items.Count – 1];
    listViewItems.MoveCurrentTo(lastItem);
    listView.ScrollIntoView(lastItem);
    }
    });

    eventPointer[listView] = scrollToEndHandler;
    data.CollectionChanged += scrollToEndHandler;
    }
    else if (eventPointer.ContainsKey(listView))
    {
    data.CollectionChanged -= eventPointer[listView];
    eventPointer.Remove(listView);
    }
    }

    • GrailSeeker
    • June 16th, 2017

    Works like a charm – you made ma day !!! Thank you so much.

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: