Restore Scroll Position in LongListSelector after tombstone
Asked Answered
F

2

5

I'm trying to work with the LongListSelector control from the WP7 Silverlight Toolkit. It's taken a bit of work, but I finally have it working with my app. Unfortunately, I am having some trouble properly handling the tombstoning process.

When the application tombstones (or the user navigates to another page by selecting an item in the list), I save a copy of the topmost visible item in the list. I save it to both a class variable and to the app state collection.

ICollection<object> visibleItems = myLongList.GetItemsInView();
_lastItem = null;
if (visibleItems.Count > 0)
    _lastItem = visibleItems.First();
IDictionary<string, object> state = 
              Microsoft.Phone.Shell.PhoneApplicationService.Current.State;
state["IndexByName_LastTopItem"] = _lastItem;

Then, when the user returns to the page I check for one of the two values (state or variable) and use it to restore the last scroll position.

if (_lastItem == null) 
{ 
    if (state.ContainsKey("IndexByName_LastTopItem")) 
    { 
        _lastItem = state["IndexByName_LastTopItem"] as Chemical; 
    } 
} 

if (_lastItem != null) 
    Dispatcher.BeginInvoke(() => { myLongList.ScrollTo(_lastItem); }); 

This works great unless the application tombstones. In that case I don't get any errors, but the list is completely blank until I touch it and drag. Once I do that, it redisplays at the top of the list. I took a look at the source for the control and found that when you call .ScrollTo(object) it doesn't get a match. Further investigation identified that when searching for an item to scroll to, it compares using == instead of Equals. I only overrode Equals, and apparently the default == compares (by design) references. When you restore a State item after tombstoning the references don't match. I can override ==, but that feels wrong. I can change and rebuild the control source to call equals instead (I tried and it worked), but it was written by people much smarter than I and I'm wondering if I just don't get it. Is there a better way?

Forensic answered 13/6, 2011 at 6:33 Comment(0)
F
2

This is the fix I ended up coming up with...

Since the source code is freely available for the Toolkit, I ended up editing the LongListSelector source code to call .Equals instead of ==. It seems to work properly for my use case and I thought I'd share in case anyone else finds it useful...

in LongListSelector.cs find the GetFlattenedIndex(object item) function and replace

if (item == _flattenedItems[index].Item)

with

if (item.Equals(_flattenedItems[index].Item))

and then in the same file find the GetResolvedIndex(object item, out ContentPresenter contentPresenter) function and replace

if (node.Value.Content == item)  // Nov 2010 Release
// OR
if (_flattenedItems[index].Item == item)  // Feb 2011 Release

with

if (item.Equals(node.Value.Content))  // Nov 2010 Release
// OR
if (item.Equals(_flattenedItems[index].Item))  // Feb 2011 Release

NOTE that the replace depends on which toolkit download you are using!

Once you have made these changes to the control, it will properly match up objects specified in ScrollTo(object), even if the references are not equal as long as you properly override Equals for all object types displayed in your LongListSelector. Don't forget this applies to your Grouping class as well as your item class if you have a grouped list!

Forensic answered 31/7, 2011 at 21:11 Comment(0)
N
0

Can you try to get the item in the new list ?

var _goodReference = myList.FirstOrDefault(x => x.id == _lastItem.Id);

if (_goodReference != null)     
Dispatcher.BeginInvoke(() => { myLongList.ScrollTo(_goodReference); }); 
Namesake answered 15/6, 2011 at 1:1 Comment(3)
On first glance that looked good. Unfortunately the items in the LongListSelector are a bit more complicated. The list source is essentially a List<List<item>> Where the outer List is the Groupings (displayed as group headings), and the inner list is the items for each grouping. So the _lastItem variable may hold an item, or it may hold a List<item> depending on the current scroll position.Forensic
Can you wrap the List<List<Item>> in an class with some kind of custom id ?Namesake
Yes. You are probably on track with a solution that could work. I was just hoping to get feedback that I was just missing something simple. Recovering from a tombstone seems unnecessarily complex. Now I'll need to handle recovery separately depending on if the topmost visible item is a group header or an item. It isn't rocket science, but it seems like it could be improved with something as simple as ScrollTo(intPositionOffsetForTopmostVisibleItem).Forensic

© 2022 - 2024 — McMap. All rights reserved.