ListBox – Automatically scroll CurrentItem into View

If you are binding a Collection to the ListBox you probably would like the ListBox to automatically scroll the CurrentItem into the view.
Well you can achieve this by doing the following.

Just create a AttachedProperty which listens for the CurrentChanged – Event of the ListBoxItems.
Then it just scrolls the current item into the view.

You can easily add the attached property like this to your ListBox

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

The implementation of the attached property should look like this

  /// <summary>
  /// This class contains a few useful extenders for the ListBox
  /// </summary>
  public class ListBoxExtenders : DependencyObject
  {
    #region Properties

    public static readonly DependencyProperty AutoScrollToCurrentItemProperty = DependencyProperty.RegisterAttached("AutoScrollToCurrentItem", typeof(bool), typeof(ListBoxExtenders), new UIPropertyMetadata(default(bool), OnAutoScrollToCurrentItemChanged));

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

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

    #endregion

    #region Events

    /// <summary>
    /// This method will be called when the AutoScrollToCurrentItem
    /// property was changed
    /// </summary>
    /// <param name="s">The sender (the ListBox)</param>
    /// <param name="e">Some additional information</param>
    public static void OnAutoScrollToCurrentItemChanged(DependencyObject s, DependencyPropertyChangedEventArgs e)
    {
      var listBox = s as ListBox;
      if (listBox != null)
      {
        var listBoxItems = listBox.Items;
        if (listBoxItems != null)
        {
          var newValue = (bool)e.NewValue;

          var autoScrollToCurrentItemWorker = new EventHandler((s1, e2) => OnAutoScrollToCurrentItem(listBox, listBox.Items.CurrentPosition));

          if (newValue)
            listBoxItems.CurrentChanged += autoScrollToCurrentItemWorker;
          else
            listBoxItems.CurrentChanged -= autoScrollToCurrentItemWorker;
        }
      }
    }

    /// <summary>
    /// This method will be called when the ListBox should
    /// be scrolled to the given index
    /// </summary>
    /// <param name="listBox">The ListBox which should be scrolled</param>
    /// <param name="index">The index of the item to which it should be scrolled</param>
    public static void OnAutoScrollToCurrentItem(ListBox listBox, int index)
    {
      if (listBox != null && listBox.Items != null && listBox.Items.Count > index && index >= 0)
        listBox.ScrollIntoView(listBox.Items[index]);
    }

    #endregion
  }

Have fun! :)

About these ads
  1. Wow,

    the idea that, the listbox scrolls automatic to the selecteditem is awsome!

    thanks to this great post!

      • michlG
      • January 17th, 2010

      Thank you very much :)

    • Harriet
    • March 7th, 2010

    Hi,
    This is exactly what I am looking for, but I cannot get it to work…

    My xaml code complains about the {…} as is assigned to the ItemSource, also, it complains about Extenders:ListBoxExtenders.AutoScrollToCurrentItem, the error message says that AutoScrollToCurrentItem was not found in ListBoxExtenders, although it is there.

    Could you please give me some advice?

      • michlG
      • March 7th, 2010

      Hello,

      the xamle code where I assigned {..} to the itemssource is just an example.
      You have to use the binding to your own DataSource or just remove it and add the items manually.

      The second error occurs because you have not specified the namespace Extenders:.
      You have to specifiy this “Extenders”-Namespace at the beginning (top) of the xaml file

      Regards Michael

    • justin
    • August 31st, 2010

    beautiful!
    thank you!

    • Bryan Shen
    • December 11th, 2010

    Very nice!!! It solves my problem beautifully.

  2. Great!
    In a MVVM solution this is the way to handle this isue.

    • Ian
    • January 10th, 2011

    Thanks a lot for this – it was really helpful. Great to be able to do this kind of thing in the view model.

    I’ve used it to implement a similar example; instead using a VerticalOffset property to restore the position of a listbox after WP7 tombstoning (using the VerticalOffset from the listbox’s scrollviewer).

    Cheers,
    Ian

    • David E
    • February 11th, 2011

    Thanks for writing this up. Works for me.

    • Cory Mathewson
    • May 5th, 2011

    I’ve got this implemented but when the line “listBoxItems.CurrentChanged += autoScrollToCurrentItemWorker;” is executed method “public static void OnAutoScrollToCurrentItem(ListBox listBox, int index)” never is called.

      • michlG
      • May 7th, 2011

      The line listBoxItems.CurrentChanged += autoScrollToCurrentItemWorker; does not execute the OnAutoScrollToCurrentItem line.
      It just registers to the CurrentChanged event of your collection.

      If you now change the CurrentItem of your collection then it will automatically fire the event and scroll the listbox to the new CurrentItem

  3. It seems like if the collection that is bound to the ItemsSource is filled after the listview is created (and CurrentChanged event is hooked up in the extender), the extender never gets notified of new items added to the listview. In my case, I have a TreeView and the SelectedItem is bound to an object on my viewmodel that is also bound to the SelectedItem of the ListView. So when I click on an item on the TreeView, the Extender is triggered, but it says the ListView’s items is empty. My workaround for this right now is to bind ZListViewExtenders.AutoScrollToCurrentItem to a boolean on my view model that changes to true when I add items to my bound collection which forces OnAutoScrollToCurrentItemChanged to run again. Any other suggestions?

      • michlG
      • June 30th, 2011

      Hi, I never faced this problem. But I can somehow understand what you mean.
      I’m sorry but right now I don’t know a better way to solve this issue.

      When I find the time I will have a look at it

      • shilda
      • September 17th, 2013

      I tried to put a boolen value and bind to that AutoScrollToCurrentItem and it doesnt work for me for new added items. Any thought ?

  4. Could you help me out and specify what the namespace should look like and where the class file should be in my project? I’m new to WPF and XAML. Thanks!

      • michlG
      • June 30th, 2011

      Hi,
      The namespace definition in XAML depends on the namespace in which you have the extender.
      It should look like this

      xmlns:Extenders="clr-namespace:YourNamespace"

      Where YourNamespace is the namespace of the extener.

    • Ramsey
    • September 21st, 2011

    I re-wrote the code in VB.NET; it did not work.

    • grrr
    • October 26th, 2011

    wow, the code!

    here’s ez

    public class bListBox : Behavior
    {
    protected override void OnAttached()
    {
    this.AssociatedObject.SelectionChanged += (s, e) =>
    {
    this.AssociatedObject.ScrollIntoView(this.AssociatedObject.SelectedItem);
    };

    base.OnAttached();
    }
    }

    • Kal_Torak
    • January 19th, 2012

    Thanks for the implementation!

  5. Heya! Nice code, helped me alot. However I’ve got the problem that the CurrentItem position is being defined before the listbox is completely loaded. The autoscroller won’t scroll into it, so I’ve extended it a bit and wait until IsLoaded == true. Just wanted to share it here:

    public static void OnAutoScrollToCurrentItem(ListBox listBox, int index)
    {
    if (listBox != null)
    {
    if (listBox.IsLoaded)
    ScrollToItem(listBox, index);
    else
    listBox.Loaded += OnListBoxLoaded;
    }
    }

    private static void OnListBoxLoaded(object sender, RoutedEventArgs e)
    {
    var listBox = sender as ListBox;
    if (listBox != null)
    {
    ScrollToItem(listBox, listBox.Items.CurrentPosition);
    }
    }

    private static void ScrollToItem(ListBox listBox, int itemIndex)
    {
    if (listBox != null && listBox.Items != null && listBox.Items.Count > itemIndex && itemIndex >= 0)
    listBox.ScrollIntoView(listBox.Items[itemIndex]);
    }

    Maybe this helps others, too.
    Regards,
    alex

    • Nurglitch
    • April 11th, 2012

    I’m trying to implement this in WP7, but getting these errors:

    Error 2 ‘System.Windows.Controls.ItemCollection’ does not contain a definition for ‘CurrentChanged’ and no extension method ‘CurrentChanged’ accepting a first argument of type ‘System.Windows.Controls.ItemCollection’ could be found
    Error 3 ‘System.Windows.Controls.ItemCollection’ does not contain a definition for ‘CurrentChanged’ and no extension method ‘CurrentChanged’ accepting a first argument of type ‘System.Windows.Controls.ItemCollection’ could be found
    Error 1 ‘System.Windows.Controls.ItemCollection’ does not contain a definition for ‘CurrentPosition’ and no extension method ‘CurrentPosition’ accepting a first argument of type ‘System.Windows.Controls.ItemCollection’ could be found

    Does this solution work for full WPF only?

      • michlG
      • April 11th, 2012

      Hi,
      I have no experience in Silverlight / WP7 development.
      Therefore I can not say if there is a similar way to do it there.

    • dean
    • September 17th, 2013

    Excellent!

  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

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: