WP7 Toolkit Update Removed GetItemsInView() from the LongListSelector
Asked Answered
T

3

6

With the latest update to the Windows Phone Toolkit they have overhauled the internals of the LongListSelector for the Mango release. One of the changes was removing support for the GetItemsInView() function (it now returns an empty list). This function previously returned a list of items that were currently visible on the screen. I was using this to get a reference to the topmost visible item when navigating away from a page so that I could support recovery after a tombstone by using ScrollTo(object item).

Does anyone know what the suggested alternative would be? I know that with Mango tombstoning is much less of an issue, but I'd still like to support it and there may be some other scenarios where I'd want to recall the scroll position. My list has thousands of items in some cases.

Teaser answered 19/8, 2011 at 15:57 Comment(4)
I guess you can't use a selected item index?Bradwell
I thought about that, but it is possible for the user to scroll to a position without making a selection.Teaser
Is modifying the source code of the Toolkit an option for you? I know I had to do it for a project, there was a major scrolling bug.Bradwell
I've modified it in the past (see my other post #6327786), but in this case I wanted to make sure I'm not missing something first.Teaser
F
4

From what I can tell from the new bits, you have to subscribe to the LLS's Link and Unlink events. Link will pass in an arg that contains the item added to the visible part of the LLS. Unlink does the same for those items removed from the LLS. So you'd do something like this:

List<string> trackedItems = new List<string>();

private void myListOfStrings_Link(object sender, LinkUnlinkEventArgs e)
{
    var x = e.ContentPresenter;
    if (x == null || x.Content == null)
        return;
    trackedItems.Add(x.Content.ToString());
}

private void myListOfString_Unlink(object sender, LinkUnlinkEventArgs e)
{
    var x = e.ContentPresenter;
    if (x == null || x.Content == null)
        return;
    trackedItems.Remove(x.Content.ToString());
}

Note that Link and Unlink will fire for EVERY rendered item in the underlying list, so if you're using the grouping features of the LLS, then you'll have to augment your test on whether or not to track the item based on what type is actually coming back. So if you have some sort of group object for which you want to track the underrlying objects, you might do something like this:

private void myGroupedListOfObjects_Link(object sender, LinkUnlinkEventArgs e)
{
    var x = e.ContentPresenter;
    if (x == null || x.Content == null)
        return;
    var myObject = x.Content as MyObject;
    if (myObject != null)
    {
        foreach (var item in myObject.Items)
        {
            trackedItems.Add(item);
        }
    }
}

I hope this helps! Let us know if it works out.

Fauces answered 23/8, 2011 at 20:59 Comment(4)
Very nice. I'm installing the Mango RC at this moment or I would try playing with your ideas. Based on the deprecation warnings this is the kind of answer I was expecting. My only followon question is, how can you determine (with link and unlink) which item is topmost? It seems you'd have to know which direction they were scrolling.Teaser
Ah - good catch. I hadn't thought of that part. Let me think about it and try to post a solution later after I get some of my meetings out of the way...Fauces
Ok - the only thing I can think of is to change the trackedItem list to also record the position of the object in the myListOfStrings collection and grab the minimum value there. Very clunky, but should work.Fauces
Thanks. Sounds like a sound idea. If I get it implemented I'll followup with an update here. Until then, I think this is the best solution given what they did to the design of the control. Thanks Chris.Teaser
J
3

The LongListSelector is using a ScrollViewer internally (apparently since the Aug 2011 release). This fact can be used to restore the position of the list after tombstoning by following the example given at http://damianblog.com/2011/01/21/wp7-scroll-pivot/ for the pivot controller.

In OnNavigatedFrom() remember the scroll offset:

    private bool _newPageInstance = true;
    private double _scollOffset = double.NaN;

    protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)
    {
        base.OnNavigatedFrom(e);

        var scrollViewer = FindScrollViewer(LongList);
        State["scrollViewer.VerticalOffset"] = scrollViewer.VerticalOffset;
        State["PreservingPageState"] = true;
        _newPageInstance = false;
    }

    private static ScrollViewer FindScrollViewer(DependencyObject parent)
    {
        var childCount = VisualTreeHelper.GetChildrenCount(parent);
        for (var i = 0; i < childCount; i++)
        {
            var elt = VisualTreeHelper.GetChild(parent, i);
            if (elt is ScrollViewer) return (ScrollViewer)elt;
            var result = FindScrollViewer(elt);
            if (result != null) return result;
        }
        return null;
    }

And restore it in OnNavigatedTo() if the app have been tombstoned:

    protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
    {
        base.OnNavigatedTo(e);

        if (!_newPageInstance || !State.ContainsKey("PreservingPageState"))
        {
            return;
        }

        _scollOffset = (double)State["scrollViewer.VerticalOffset"];
    }

    private void LongList_Loaded(object sender, RoutedEventArgs e)
    {
        if (double.IsNaN(_scollOffset)) return;

        var longListSelector = (LongListSelector)sender;
        var scrollViewer = FindScrollViewer(longListSelector);
        scrollViewer.ScrollToVerticalOffset(_scollOffset);
        _scollOffset = double.NaN;
    }
Juliannajulianne answered 19/1, 2012 at 3:52 Comment(0)
P
2

The Link/Unlink approach does not work at all for restoring the scroll position. Even if you setup the collection, you don't know if you are scrolling up or down, and the size of the collection would vary depending on the BufferSize property of the LongListSelector.

The FindScrollViewer solution in kvakulo's answer, however, works.

If someone needs the VB.Net version of this code:

Friend Function FindScrollViewer(parent As DependencyObject) As ScrollViewer

    Dim childCount = VisualTreeHelper.GetChildrenCount(parent)

    For i As Int32 = 0 To childCount - 1

        Dim elt = VisualTreeHelper.GetChild(parent, i)

        If elt.GetType Is GetType(ScrollViewer) Then Return CType(elt, ScrollViewer)

        Dim result = FindScrollViewer(elt)
        If result IsNot Nothing Then Return result

    Next

    Return Nothing

End Function
Pyrimidine answered 2/3, 2012 at 0:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.