Bookmark and Share Share...    Subscribe to this feed Feed   About Christian Moser  


ItemsControl

How to automatically scroll to the last item

 
<ListBox l:ItemsControlHelper.ScrollToLastItem="true" />
 
 


 
public static class ItemsControlHelper
{
    public static readonly DependencyProperty ScrollToLastItemProperty =
        DependencyProperty.RegisterAttached("ScrollToLastItem",
            typeof(bool), typeof(ItemsControlHelper),
            new FrameworkPropertyMetadata(false, OnScrollToLastItemChanged));
 
    public static void SetScrollToLastItem(UIElement sender, bool value)
    {
        sender.SetValue(ScrollToLastItemProperty, value);
    }
 
    public static bool GetScrollToLastItem(UIElement sender)
    {
        return (bool)sender.GetValue(ScrollToLastItemProperty);
    }
 
    private static void OnScrollToLastItemChanged(DependencyObject sender, 
                            DependencyPropertyChangedEventArgs e)
    {
        var itemsControl = sender as ItemsControl;
 
        if (itemsControl != null)
        {
            itemsControl.ItemContainerGenerator.StatusChanged += 
                     (s,a) => OnItemsChanged(itemsControl,s,a);
        }
    }
 
    static void OnItemsChanged(ItemsControl itemsControl, object sender, EventArgs e)
    {
        var generator = sender as ItemContainerGenerator;
        if( generator.Status == GeneratorStatus.ContainersGenerated )
        {
            if (itemsControl.Items.Count > 0)
            {
                ScrollIntoView(itemsControl, 
                 itemsControl.Items[itemsControl.Items.Count - 1]);
            }
        }
    }
 
    private static void ScrollIntoView(ItemsControl itemsControl, object item)
    {
        if (itemsControl.ItemContainerGenerator.Status == 
            GeneratorStatus.ContainersGenerated)
        {
            OnBringItemIntoView(itemsControl, item);
        }
        else
        {
            Func<object, object> onBringIntoView = 
                    (o) => OnBringItemIntoView(itemsControl, item);
            itemsControl.Dispatcher.BeginInvoke(DispatcherPriority.Loaded, 
                  new DispatcherOperationCallback(onBringIntoView));
        } 
    }
 
    private static object OnBringItemIntoView(ItemsControl itemsControl, object item)
    {
        var element = itemsControl.ItemContainerGenerator.
                 ContainerFromItem(item) as FrameworkElement;
        if (element != null)
        {
            element.BringIntoView();
        }
        return null;
    }
}
 
 




Last modified: 2010-02-03 14:58:42
Copyright (c) by Christian Moser, 2011.

 Comments on this article

Show all comments
Kyaw Wunna
Commented on 12.March 2010
Hello, I just found the coding error when I use this Dependency property tutorial in the .xaml page. I used separate class for "ItemsControlHelper".

These are as follow:

1. The property 'ItemsControlHelper.ScrollToLastItem' does not exist in XML namespace
2. Name 'l.ItemsControlHelper.ScrollToLastItem' is not a valid XAML name.
3. The attachable property 'ItemsControlHelper' was not found in type 'l'.

If possible, please let me know how to fix it. Thanks
Yuri
Commented on 29.June 2010
it will not work with logical scrolling (VirtualizingStackPanel.IsVirtualizing="True")
chet
Commented on 1.July 2010
@Kyaw Wunna
you must reference the namespace
xmlns:l="clr-namespace:[namespace of ItemsControlHelper]"
chet
Commented on 1.July 2010
It can't scroll to the first item ever..cause of scroll the bar will fire itemsControl.ItemContainerGenerator.StatusChanged several times,one generator.Status == GeneratorStatus.ContainersGenerated...why?
chinni
Commented on 2.July 2010
Hello,im not able to set ItemsControlHelper.ScrollToLastItem="true"
suji
Commented on 22.November 2010
hi, how to use AddText method in ItemControl
Don
Commented on 27.December 2010
I think, to make things work, the is a line like
itemsControl.ItemContainerGenerator.StatusChanged = null
missing. Not the finest method to clear the events, but it works.

Nice Text to show the possibillities !
m.n
Commented on 12.May 2011
Doesn't work if last item equals to the first, because ContainerFromItem() finds the first item, not the last.
Using item index instead of item itself, solves the problem:

private static object OnBringItemIntoView(ItemsControl itemsControl, int index)
{
var element = itemsControl.ItemContainerGenerator.ContainerFromIndex(index) as FrameworkElement;
if (element != null)
{
element.BringIntoView();
}
return null;
}

Plus change &quot;object item&quot; to &quot;int index&quot; appropriately.

Name
E-Mail (optional)
Comment