1

I'm working on a UWP Windows 10 app. It contains a listview which redirects you to another page when an item is tapped I'm trying to get the last item to be re-selected when I navigate back to the previous page.

My listview's ItemSource is binded to an ObservableCollection and loads my items as expected but when I navigate back, it keeps the item selected but it's not visible.

I've read various articles:

Make ListView.ScrollIntoView Scroll the Item into the Center of the ListView

ListViewBase.ScrollIntoView methods

Windows 10 ScrollIntoView() is not scrolling to the items in the middle of a listview

And quite a few others, and I tried to use the extension from the last article mentioned above which provided various extensions to the Listview and I thought I'd be good to go but to no avail!

While investigating and trying various things out, I thought I'd make a call from

protected async override void OnNavigatedTo(NavigationEventArgs e)

and call the following code:

if (e.NavigationMode == NavigationMode.Back)
  {
    if (GetViewModel.SelectedChannel != null)
      {
        await this.lvwChannels.ScrollToIndex
              (GetViewModel.SelectedIndex);
      }
  }

or

if (e.NavigationMode == NavigationMode.Back)
  {
    if (GetViewModel.SelectedChannel != null)
      {
        await this.lvwChannels.ScrollToItem
              (GetViewModel.SelectedItem);
      }
  }

but it still not working.

This is when I noticed that my Listview.Items.count was returning 0 and yet the item are reloaded, but I assume they are being reloaded at a later stage so my question is:

but I noticed the following:

  1. When stepping through the extension, the

    public async static Task ScrollToIndex(this ListViewBase listViewBase, int index)

my listViewBase.Items.Count is 0?? Why?

  1. When trying to find the ScrollViewer within the ListView, it doesn't find it?

  2. When calling

    var selectorItem = listViewBase.ContainerFromIndex(index) as SelectorItem;

selectorItem is null as it doesn't find anything based on the index. The index is the only thing that appears to be set correct i.e. 10, 23, 37

Can someone point me the right direction? I assume it's a timing issue and that my ObservableCollection hasn't been re-binded at this stage. I could re-bind at just before calling the ScrollIntoView, but that's still not going to resolve the fact that my listViewBase is null.

Any ideas?

Thanks.

Community
  • 1
  • 1
Thierry
  • 6,142
  • 13
  • 66
  • 117
  • I've tried attaching a property as suggested in this article: http://stackoverflow.com/questions/18019425/scrollintoview-for-wpf-datagrid-mvvm and while my listview contains data, and it calls the ScrollIntoView, it still doesn't show the relevant item!!! – Thierry Dec 20 '15 at 23:48

2 Answers2

0

Where did you add your data to ObservableCollection? I'm not sure how you do this. By me, the following method works fine:

public sealed partial class MainPage : Page
{
    ObservableCollection<listText> list = new ObservableCollection<listText>();
    static int selectedcount = -1;


public MainPage()
{
    this.InitializeComponent();
    mylist.ItemsSource = list;
}

class listText
{
    public string listtxt { get; set; }
}

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    list.Clear();
    list.Add(new listText { listtxt = "Item1" });
    list.Add(new listText { listtxt = "Item2" });

    mylist.SelectedIndex = selectedcount;
}

private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
    switch (mylist.SelectedIndex)
    {
        case 0:
            selectedcount = 0;
            this.Frame.Navigate(typeof(Item1));                    
            break;

        case 1:
            selectedcount = 1;
            this.Frame.Navigate(typeof(Item2));
            break;

        default:
            selectedcount = 0;
            this.Frame.Navigate(typeof(Item1));
            break;
    }
}
}

And the NavigateBack code I wrote in the App.xaml.cs like this:

        private void App_BackRequested(object sender, BackRequestedEventArgs e)
        {
            Frame rootFrame = Window.Current.Content as Frame;
            if (rootFrame == null)
                return;

            // Navigate back if possible, and if the event has not 
            // already been handled .
            if (rootFrame.CanGoBack && e.Handled == false)
            {
                e.Handled = true;
                rootFrame.GoBack();
            }
        }

        /// <summary>
        /// Invoked when the application is launched normally by the end user.  Other entry points
        /// will be used such as when the application is launched to open a specific file.
        /// </summary>
        /// <param name="e">Details about the launch request and process.</param>
        protected override void OnLaunched(LaunchActivatedEventArgs e)
        {

//#if DEBUG
//            if (System.Diagnostics.Debugger.IsAttached)
//            {
//                this.DebugSettings.EnableFrameRateCounter = true;
//            }
//#endif

            Frame rootFrame = Window.Current.Content as Frame;
            Windows.UI.Core.SystemNavigationManager.GetForCurrentView().BackRequested += App_BackRequested;
            // Do not repeat app initialization when the Window already has content,
            // just ensure that the window is active
            if (rootFrame == null)
            {
                // Create a Frame to act as the navigation context and navigate to the first page
                rootFrame = new Frame();

                rootFrame.NavigationFailed += OnNavigationFailed;

                if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
                {
                    //TODO: Load state from previously suspended application
                }

                // Place the frame in the current Window
                Window.Current.Content = rootFrame;
            }

            if (rootFrame.Content == null)
            {
                // When the navigation stack isn't restored navigate to the first page,
                // configuring the new page by passing required information as a navigation
                // parameter
                rootFrame.Navigate(typeof(MainPage), e.Arguments);
            }
            // Ensure the current window is active
            Window.Current.Activate();
        }

I add my data to the ObservableCollection in the OnNavigatedTo, this OnNavigatedTo will be called when it navi-back, so the count of my listview will not be 0. Wish this helps you.

Grace Feng
  • 16,564
  • 2
  • 22
  • 45
  • Hi, my ObservableCollection is set in the ViewModel for that specific page and I can clearly see it being called but as mentioned it's a timing issue. I've managed to resolve it a few minutes ago by call the build-in ScrollIntoView from within the Loaded event. When called at this stage, it seems that my collection has been fully reloaded. I'll look at one of the article mentioned above as while it re-selects it, it puts the item at the very top of the page and I'd prefer if it was position in the middle of the list. – Thierry Dec 21 '15 at 19:42
0

It does seem to be a timing issue and while I'm not 100% sure it is the correct way to address the problem, it seem to have resolved it by calling the ScrollIntoView within the Loaded event:

    private void Page_Loaded(object sender, RoutedEventArgs e)
    {
        if (GetViewModel.SelectedChannel != null)
            lvwChannels.ScrollIntoView(GetViewModel.SelectedChannel);
    }

I guess I'm not breaking any MVVM rules either as it is dealing with the UI only at this stage.

When called from the Loaded event, both my ObservableCollection has been reloaded automatically since it is binded and the SelectedChannel is also set correctly and everything appears to be working as expected.

As mentioned in my reply below, by default, the selected items is now highlighted and displayed at the top of the list and I'd prefer if it was centered.

If there is a better way to do this, let me know but for now it will have to do.

Thanks

Thierry
  • 6,142
  • 13
  • 66
  • 117