8

I'm working on making an app come back nicely from being tombstoned. The app contains large listboxes, so I'd ideally like to scroll back to wherever the user was while they were scrolling around those listboxes.

It's easy to jump back to a particular SelectedItem - unfortunately for me, my app never needs the user to actually select an item, they're just scrolling through them. What I really want is some sort of MyListbox.ScrollPositionY but it doesn't seem to exist.

Any ideas?

Chris

Chris Rae
  • 5,627
  • 2
  • 36
  • 51

1 Answers1

10

You need to get hold of the ScrollViewer that is used by the ListBox internally so you can grab the value of the VerticalOffset property and subsequently call the SetVerticalOffset method.

This requires that you reach down from the ListBox through the Visual tree that makes up its internals.

I use this handy extension class which you should add to your project (I've gotta put this up on a blog because I keep repeating it):-

public static class VisualTreeEnumeration
{
    public static IEnumerable<DependencyObject> Descendents(this DependencyObject root, int depth)
    {
        int count = VisualTreeHelper.GetChildrenCount(root);
        for (int i = 0; i < count; i++)
        {
            var child = VisualTreeHelper.GetChild(root, i);
            yield return child;
            if (depth > 0)
            {
                foreach (var descendent in Descendents(child, --depth))
                    yield return descendent;
            }
        }
    }

    public static IEnumerable<DependencyObject> Descendents(this DependencyObject root)
    {
        return Descendents(root, Int32.MaxValue);
    }

    public static IEnumerable<DependencyObject> Ancestors(this DependencyObject root)
    {
        DependencyObject current = VisualTreeHelper.GetParent(root);
        while (current != null)
        {
            yield return current;
            current = VisualTreeHelper.GetParent(current);
        }
    }
}

With this available the ListBox (and all other UIElements for that matter) gets a couple of new extension methods Descendents and Ancestors. We can combine those with Linq to search for stuff. In this case you could use:-

ScrollViewer sv = SomeListBox.Descendents().OfType<ScrollViewer>().FirstOrDefault();
AnthonyWJones
  • 187,081
  • 35
  • 232
  • 306
  • Thank you kindly! I didn't even realise that controls' makeup could be navigated in this way. – Chris Rae Dec 22 '10 at 20:08
  • so how do you know when to use that info? How doe a page know it's being restored from tombstoning without jumping through a bunch of hoops? – Roger Jun 12 '11 at 23:01
  • @Roger: Have read of this MSDN topic: http://msdn.microsoft.com/en-us/library/ff967548(v=VS.92).aspx – AnthonyWJones Jun 13 '11 at 12:12
  • 2
    Shouldn't `foreach (var descendent in Descendents(child, --depth))` be `foreach (var descendent in Descendents(child, depth - 1))`? The code as written will recurse to `depth - 1` for the first child, `depth - 2` for the second, etc. Or is this intended? – Matthew May 13 '12 at 03:44